diff --git a/app/Livewire/Notifications/Discord.php b/app/Livewire/Notifications/Discord.php
index ab3884320..59350a3e1 100644
--- a/app/Livewire/Notifications/Discord.php
+++ b/app/Livewire/Notifications/Discord.php
@@ -110,7 +110,9 @@ class Discord extends Component
refreshSession();
} else {
$this->discordEnabled = $this->settings->discord_enabled;
- $this->discordWebhookUrl = $this->settings->discord_webhook_url;
+ $this->discordWebhookUrl = auth()->user()->can('update', $this->settings)
+ ? $this->settings->discord_webhook_url
+ : null;
$this->deploymentSuccessDiscordNotifications = $this->settings->deployment_success_discord_notifications;
$this->deploymentFailureDiscordNotifications = $this->settings->deployment_failure_discord_notifications;
diff --git a/app/Livewire/Notifications/Pushover.php b/app/Livewire/Notifications/Pushover.php
index d79eea87b..f894c5005 100644
--- a/app/Livewire/Notifications/Pushover.php
+++ b/app/Livewire/Notifications/Pushover.php
@@ -113,8 +113,13 @@ class Pushover extends Component
refreshSession();
} else {
$this->pushoverEnabled = $this->settings->pushover_enabled;
- $this->pushoverUserKey = $this->settings->pushover_user_key;
- $this->pushoverApiToken = $this->settings->pushover_api_token;
+ if (auth()->user()->can('update', $this->settings)) {
+ $this->pushoverUserKey = $this->settings->pushover_user_key;
+ $this->pushoverApiToken = $this->settings->pushover_api_token;
+ } else {
+ $this->pushoverUserKey = null;
+ $this->pushoverApiToken = null;
+ }
$this->deploymentSuccessPushoverNotifications = $this->settings->deployment_success_pushover_notifications;
$this->deploymentFailurePushoverNotifications = $this->settings->deployment_failure_pushover_notifications;
diff --git a/app/Livewire/Notifications/Slack.php b/app/Livewire/Notifications/Slack.php
index f870b3986..58cab5494 100644
--- a/app/Livewire/Notifications/Slack.php
+++ b/app/Livewire/Notifications/Slack.php
@@ -110,7 +110,9 @@ class Slack extends Component
refreshSession();
} else {
$this->slackEnabled = $this->settings->slack_enabled;
- $this->slackWebhookUrl = $this->settings->slack_webhook_url;
+ $this->slackWebhookUrl = auth()->user()->can('update', $this->settings)
+ ? $this->settings->slack_webhook_url
+ : null;
$this->deploymentSuccessSlackNotifications = $this->settings->deployment_success_slack_notifications;
$this->deploymentFailureSlackNotifications = $this->settings->deployment_failure_slack_notifications;
diff --git a/app/Livewire/Notifications/Telegram.php b/app/Livewire/Notifications/Telegram.php
index fc3966cf6..78eb7ef9f 100644
--- a/app/Livewire/Notifications/Telegram.php
+++ b/app/Livewire/Notifications/Telegram.php
@@ -169,8 +169,13 @@ class Telegram extends Component
$this->settings->save();
} else {
$this->telegramEnabled = $this->settings->telegram_enabled;
- $this->telegramToken = $this->settings->telegram_token;
- $this->telegramChatId = $this->settings->telegram_chat_id;
+ if (auth()->user()->can('update', $this->settings)) {
+ $this->telegramToken = $this->settings->telegram_token;
+ $this->telegramChatId = $this->settings->telegram_chat_id;
+ } else {
+ $this->telegramToken = null;
+ $this->telegramChatId = null;
+ }
$this->deploymentSuccessTelegramNotifications = $this->settings->deployment_success_telegram_notifications;
$this->deploymentFailureTelegramNotifications = $this->settings->deployment_failure_telegram_notifications;
diff --git a/app/Livewire/Notifications/Webhook.php b/app/Livewire/Notifications/Webhook.php
index 630d422a9..4a67180ff 100644
--- a/app/Livewire/Notifications/Webhook.php
+++ b/app/Livewire/Notifications/Webhook.php
@@ -105,7 +105,9 @@ class Webhook extends Component
refreshSession();
} else {
$this->webhookEnabled = $this->settings->webhook_enabled;
- $this->webhookUrl = $this->settings->webhook_url;
+ $this->webhookUrl = auth()->user()->can('update', $this->settings)
+ ? $this->settings->webhook_url
+ : null;
$this->deploymentSuccessWebhookNotifications = $this->settings->deployment_success_webhook_notifications;
$this->deploymentFailureWebhookNotifications = $this->settings->deployment_failure_webhook_notifications;
diff --git a/app/Livewire/Project/Shared/Webhooks.php b/app/Livewire/Project/Shared/Webhooks.php
index eafc653d5..eb90262e9 100644
--- a/app/Livewire/Project/Shared/Webhooks.php
+++ b/app/Livewire/Project/Shared/Webhooks.php
@@ -34,19 +34,24 @@ class Webhooks extends Component
{
$this->deploywebhook = generateDeployWebhook($this->resource);
- $this->githubManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_github');
+ if ($this->canViewSecrets()) {
+ $this->githubManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_github');
+ $this->gitlabManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_gitlab');
+ $this->bitbucketManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_bitbucket');
+ $this->giteaManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_gitea');
+ }
+
$this->githubManualWebhook = generateGitManualWebhook($this->resource, 'github');
-
- $this->gitlabManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_gitlab');
$this->gitlabManualWebhook = generateGitManualWebhook($this->resource, 'gitlab');
-
- $this->bitbucketManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_bitbucket');
$this->bitbucketManualWebhook = generateGitManualWebhook($this->resource, 'bitbucket');
-
- $this->giteaManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_gitea');
$this->giteaManualWebhook = generateGitManualWebhook($this->resource, 'gitea');
}
+ public function canViewSecrets(): bool
+ {
+ return auth()->user()->can('update', $this->resource);
+ }
+
public function submit()
{
try {
diff --git a/resources/views/livewire/notifications/discord.blade.php b/resources/views/livewire/notifications/discord.blade.php
index 0e5406c78..64e445441 100644
--- a/resources/views/livewire/notifications/discord.blade.php
+++ b/resources/views/livewire/notifications/discord.blade.php
@@ -26,9 +26,15 @@
helper="If enabled, a ping (@here) will be sent to the notification when a critical event happens."
label="Ping Enabled" />
-
+ @can('update', $settings)
+
+ @else
+
+ @endcan
Notification Settings
diff --git a/resources/views/livewire/notifications/pushover.blade.php b/resources/views/livewire/notifications/pushover.blade.php
index 74cd9e8d2..4ea1a159c 100644
--- a/resources/views/livewire/notifications/pushover.blade.php
+++ b/resources/views/livewire/notifications/pushover.blade.php
@@ -24,12 +24,21 @@
-
-
+ @can('update', $settings)
+
+
+ @else
+
+
+ @endcan
Notification Settings
diff --git a/resources/views/livewire/notifications/slack.blade.php b/resources/views/livewire/notifications/slack.blade.php
index 14c7b3508..9ab752291 100644
--- a/resources/views/livewire/notifications/slack.blade.php
+++ b/resources/views/livewire/notifications/slack.blade.php
@@ -23,9 +23,15 @@
-
+ @can('update', $settings)
+
+ @else
+
+ @endcan
Notification Settings
diff --git a/resources/views/livewire/notifications/telegram.blade.php b/resources/views/livewire/notifications/telegram.blade.php
index f87a13c37..98ec128d5 100644
--- a/resources/views/livewire/notifications/telegram.blade.php
+++ b/resources/views/livewire/notifications/telegram.blade.php
@@ -24,12 +24,21 @@
-
-
+ @can('update', $settings)
+
+
+ @else
+
+
+ @endcan
Notification Settings
diff --git a/resources/views/livewire/notifications/webhook.blade.php b/resources/views/livewire/notifications/webhook.blade.php
index 7c32311bf..4bacb6091 100644
--- a/resources/views/livewire/notifications/webhook.blade.php
+++ b/resources/views/livewire/notifications/webhook.blade.php
@@ -28,9 +28,15 @@
-
+ @can('update', $settings)
+
+ @else
+
+ @endcan
Notification Settings
diff --git a/resources/views/livewire/project/shared/webhooks.blade.php b/resources/views/livewire/project/shared/webhooks.blade.php
index 24bba525a..bccc31383 100644
--- a/resources/views/livewire/project/shared/webhooks.blade.php
+++ b/resources/views/livewire/project/shared/webhooks.blade.php
@@ -22,9 +22,9 @@
helper="Need to set a secret to be able to use this webhook. It should match with the secret in GitHub."
label="GitHub Webhook Secret" id="githubManualWebhookSecret">
@else
-
+ label="GitHub Webhook Secret" value="Hidden (only admins can view)">
@endcan
@@ -39,9 +39,9 @@
helper="Need to set a secret to be able to use this webhook. It should match with the secret in GitLab."
label="GitLab Webhook Secret" id="gitlabManualWebhookSecret">
@else
-
+ label="GitLab Webhook Secret" value="Hidden (only admins can view)">
@endcan
@@ -51,9 +51,9 @@
helper="Need to set a secret to be able to use this webhook. It should match with the secret in Bitbucket."
label="Bitbucket Webhook Secret" id="bitbucketManualWebhookSecret">
@else
-
+ label="Bitbucket Webhook Secret" value="Hidden (only admins can view)">
@endcan
@@ -63,9 +63,9 @@
helper="Need to set a secret to be able to use this webhook. It should match with the secret in Gitea."
label="Gitea Webhook Secret" id="giteaManualWebhookSecret">
@else
-
+ label="Gitea Webhook Secret" value="Hidden (only admins can view)">
@endcan
@can('update', $resource)
diff --git a/tests/Feature/Authorization/NotificationAuthorizationTest.php b/tests/Feature/Authorization/NotificationAuthorizationTest.php
index 7a7ac94aa..aa9d1cbdc 100644
--- a/tests/Feature/Authorization/NotificationAuthorizationTest.php
+++ b/tests/Feature/Authorization/NotificationAuthorizationTest.php
@@ -15,7 +15,7 @@ use Livewire\Livewire;
uses(RefreshDatabase::class);
beforeEach(function () {
- InstanceSettings::updateOrCreate(['id' => 0]);
+ InstanceSettings::unguarded(fn () => InstanceSettings::updateOrCreate(['id' => 0], ['id' => 0]));
$this->team = Team::factory()->create();
@@ -275,3 +275,75 @@ test('member cannot send test on any notification channel', function () {
expect($this->member->can('sendTest', $this->team->pushoverNotificationSettings))->toBeFalse();
expect($this->member->can('sendTest', $this->team->webhookNotificationSettings))->toBeFalse();
});
+
+test('member cannot view notification secrets', function (string $component, string $settingsRelation, array $secrets) {
+ $settings = $this->team->{$settingsRelation};
+ $settings->update($secrets);
+
+ $this->actingAs($this->member);
+ session(['currentTeam' => $this->team]);
+
+ $componentTest = Livewire::test($component);
+
+ foreach ($secrets as $column => $value) {
+ $property = str($column)->camel()->toString();
+
+ $componentTest
+ ->assertSet($property, null)
+ ->assertDontSee($value);
+ }
+
+ $componentTest->assertSee('Hidden (only admins can view)');
+})->with([
+ 'discord webhook' => [DiscordNotification::class, 'discordNotificationSettings', [
+ 'discord_webhook_url' => 'https://discord.com/api/webhooks/secret-member',
+ ]],
+ 'slack webhook' => [SlackNotification::class, 'slackNotificationSettings', [
+ 'slack_webhook_url' => 'https://hooks.slack.com/services/secret-member',
+ ]],
+ 'telegram token and chat id' => [TelegramNotification::class, 'telegramNotificationSettings', [
+ 'telegram_token' => 'telegram-secret-token',
+ 'telegram_chat_id' => 'telegram-secret-chat',
+ ]],
+ 'pushover credentials' => [PushoverNotification::class, 'pushoverNotificationSettings', [
+ 'pushover_user_key' => 'pushover-secret-user',
+ 'pushover_api_token' => 'pushover-secret-token',
+ ]],
+ 'generic webhook' => [WebhookNotification::class, 'webhookNotificationSettings', [
+ 'webhook_url' => 'https://example.com/secret-webhook',
+ ]],
+]);
+
+test('admin can view notification secrets', function (string $component, string $settingsRelation, array $secrets) {
+ $settings = $this->team->{$settingsRelation};
+ $settings->update($secrets);
+
+ $this->actingAs($this->admin);
+ session(['currentTeam' => $this->team]);
+
+ $componentTest = Livewire::test($component);
+
+ foreach ($secrets as $column => $value) {
+ $property = str($column)->camel()->toString();
+
+ $componentTest->assertSet($property, $value);
+ }
+})->with([
+ 'discord webhook' => [DiscordNotification::class, 'discordNotificationSettings', [
+ 'discord_webhook_url' => 'https://discord.com/api/webhooks/secret-admin',
+ ]],
+ 'slack webhook' => [SlackNotification::class, 'slackNotificationSettings', [
+ 'slack_webhook_url' => 'https://hooks.slack.com/services/secret-admin',
+ ]],
+ 'telegram token and chat id' => [TelegramNotification::class, 'telegramNotificationSettings', [
+ 'telegram_token' => 'telegram-admin-token',
+ 'telegram_chat_id' => 'telegram-admin-chat',
+ ]],
+ 'pushover credentials' => [PushoverNotification::class, 'pushoverNotificationSettings', [
+ 'pushover_user_key' => 'pushover-admin-user',
+ 'pushover_api_token' => 'pushover-admin-token',
+ ]],
+ 'generic webhook' => [WebhookNotification::class, 'webhookNotificationSettings', [
+ 'webhook_url' => 'https://example.com/admin-webhook',
+ ]],
+]);
diff --git a/tests/Feature/Authorization/SharedResourceAuthorizationTest.php b/tests/Feature/Authorization/SharedResourceAuthorizationTest.php
index 23f134458..57e46c418 100644
--- a/tests/Feature/Authorization/SharedResourceAuthorizationTest.php
+++ b/tests/Feature/Authorization/SharedResourceAuthorizationTest.php
@@ -20,7 +20,7 @@ use Livewire\Livewire;
uses(RefreshDatabase::class);
beforeEach(function () {
- InstanceSettings::updateOrCreate(['id' => 0]);
+ InstanceSettings::unguarded(fn () => InstanceSettings::updateOrCreate(['id' => 0], ['id' => 0]));
$this->team = Team::factory()->create();
@@ -167,6 +167,51 @@ test('admin can update application webhooks', function () {
expect($this->admin->can('update', $this->application))->toBeTrue();
});
+test('member cannot view application webhook secrets', function () {
+ $this->application->update([
+ 'git_repository' => 'coollabsio/coolify',
+ 'git_branch' => 'main',
+ 'manual_webhook_secret_github' => 'github-secret-value',
+ 'manual_webhook_secret_gitlab' => 'gitlab-secret-value',
+ 'manual_webhook_secret_bitbucket' => 'bitbucket-secret-value',
+ 'manual_webhook_secret_gitea' => 'gitea-secret-value',
+ ]);
+
+ $this->actingAs($this->member);
+ session(['currentTeam' => $this->team]);
+
+ Livewire::test(Webhooks::class, ['resource' => $this->application->fresh()])
+ ->assertSet('githubManualWebhookSecret', null)
+ ->assertSet('gitlabManualWebhookSecret', null)
+ ->assertSet('bitbucketManualWebhookSecret', null)
+ ->assertSet('giteaManualWebhookSecret', null)
+ ->assertSee('Hidden (only admins can view)')
+ ->assertDontSee('github-secret-value')
+ ->assertDontSee('gitlab-secret-value')
+ ->assertDontSee('bitbucket-secret-value')
+ ->assertDontSee('gitea-secret-value');
+});
+
+test('admin can view application webhook secrets', function () {
+ $this->application->update([
+ 'git_repository' => 'coollabsio/coolify',
+ 'git_branch' => 'main',
+ 'manual_webhook_secret_github' => 'github-secret-value',
+ 'manual_webhook_secret_gitlab' => 'gitlab-secret-value',
+ 'manual_webhook_secret_bitbucket' => 'bitbucket-secret-value',
+ 'manual_webhook_secret_gitea' => 'gitea-secret-value',
+ ]);
+
+ $this->actingAs($this->admin);
+ session(['currentTeam' => $this->team]);
+
+ Livewire::test(Webhooks::class, ['resource' => $this->application->fresh()])
+ ->assertSet('githubManualWebhookSecret', 'github-secret-value')
+ ->assertSet('gitlabManualWebhookSecret', 'gitlab-secret-value')
+ ->assertSet('bitbucketManualWebhookSecret', 'bitbucket-secret-value')
+ ->assertSet('giteaManualWebhookSecret', 'gitea-secret-value');
+});
+
// --- Resource Limits (policy checks, mount requires full resource data) ---
test('member cannot update application resource limits', function () {