diff --git a/config/cachet.php b/config/cachet.php index e90d7633..97d57760 100644 --- a/config/cachet.php +++ b/config/cachet.php @@ -75,6 +75,8 @@ 'api_middleware' => [ 'api', + \Cachet\Http\Middleware\ApiEnabled::class, + \Cachet\Http\Middleware\ApiPublicOrProtected::class, ], /* diff --git a/database/migrations/2025_02_09_073310_add_api_settings.php b/database/migrations/2025_02_09_073310_add_api_settings.php new file mode 100644 index 00000000..65acb748 --- /dev/null +++ b/database/migrations/2025_02_09_073310_add_api_settings.php @@ -0,0 +1,25 @@ + $this->migrator->add('app.api_enabled', true)); + rescue(fn () => $this->migrator->add('app.api_protected', false)); + } + /** + * Run the migrations. + */ + public function down(): void + { + // Theme settings... + rescue(fn () => $this->migrator->deleteIfExists('app.api_enabled')); + rescue(fn () => $this->migrator->deleteIfExists('app.api_protected')); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index d594f1b0..fe596fc8 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -224,6 +224,8 @@ public function run(): void $appSettings->major_outage_threshold = 25; $appSettings->recent_incidents_only = false; $appSettings->recent_incidents_days = 7; + $appSettings->api_enabled = true; + $appSettings->api_protected = false; $appSettings->save(); $customizationSettings = app(CustomizationSettings::class); diff --git a/resources/lang/de/settings.php b/resources/lang/de/settings.php index c30eb3d9..6b3f9bd1 100644 --- a/resources/lang/de/settings.php +++ b/resources/lang/de/settings.php @@ -18,6 +18,8 @@ 'only_show_disrupted_days' => 'Nur Tage mit Vorfällen anzeigen', 'recent_incidents_only' => 'Nur aktuelle Vorfälle anzeigen', 'recent_incidents_days' => 'Anzahl der Tage, an denen aktuelle Vorfälle angezeigt werden sollen', + 'api_enabled' => 'API aktiviert', + 'api_protected' => 'API erfordert Authentifizierung', ], ], 'manage_customization' => [ diff --git a/resources/lang/de_AT/settings.php b/resources/lang/de_AT/settings.php index c30eb3d9..6b3f9bd1 100644 --- a/resources/lang/de_AT/settings.php +++ b/resources/lang/de_AT/settings.php @@ -18,6 +18,8 @@ 'only_show_disrupted_days' => 'Nur Tage mit Vorfällen anzeigen', 'recent_incidents_only' => 'Nur aktuelle Vorfälle anzeigen', 'recent_incidents_days' => 'Anzahl der Tage, an denen aktuelle Vorfälle angezeigt werden sollen', + 'api_enabled' => 'API aktiviert', + 'api_protected' => 'API erfordert Authentifizierung', ], ], 'manage_customization' => [ diff --git a/resources/lang/de_CH/settings.php b/resources/lang/de_CH/settings.php index c30eb3d9..6b3f9bd1 100644 --- a/resources/lang/de_CH/settings.php +++ b/resources/lang/de_CH/settings.php @@ -18,6 +18,8 @@ 'only_show_disrupted_days' => 'Nur Tage mit Vorfällen anzeigen', 'recent_incidents_only' => 'Nur aktuelle Vorfälle anzeigen', 'recent_incidents_days' => 'Anzahl der Tage, an denen aktuelle Vorfälle angezeigt werden sollen', + 'api_enabled' => 'API aktiviert', + 'api_protected' => 'API erfordert Authentifizierung', ], ], 'manage_customization' => [ diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php index 63b32d87..33c332b8 100644 --- a/resources/lang/en/settings.php +++ b/resources/lang/en/settings.php @@ -18,6 +18,8 @@ 'only_show_disrupted_days' => 'Only Show Disrupted Days', 'recent_incidents_only' => 'Show Recent Incidents Only', 'recent_incidents_days' => 'Number of Days to Show Recent Incidents', + 'api_enabled' => 'API Enabled', + 'api_protected' => 'API requires authentication', ], ], 'manage_customization' => [ diff --git a/resources/lang/nl/settings.php b/resources/lang/nl/settings.php index 03140c75..26af8249 100644 --- a/resources/lang/nl/settings.php +++ b/resources/lang/nl/settings.php @@ -18,6 +18,8 @@ 'only_show_disrupted_days' => 'Alleen dagen met incidenten weergeven', 'recent_incidents_only' => 'Toon alleen actuele incidenten', 'recent_incidents_days' => 'Aantal dagen waarop actuele incidenten moeten worden weergegeven', + 'api_enabled' => 'API geactiveerd', + 'api_protected' => 'API authenticatie vereist', ], ], 'manage_customization' => [ diff --git a/resources/lang/ph/settings.php b/resources/lang/ph/settings.php index 7b77da60..66cc3794 100644 --- a/resources/lang/ph/settings.php +++ b/resources/lang/ph/settings.php @@ -18,6 +18,8 @@ 'only_show_disrupted_days' => 'Ipakita lamang ang Mga Araw ng Pagka-aberya', 'recent_incidents_only' => 'Ipakita lamang ang Mga Kamakailang Insidente', 'recent_incidents_days' => 'Bilang ng Araw para Ipakita ang Mga Kamakailang Insidente', + 'api_enabled' => 'Api Pinagana', + 'api_protected' => 'Ang Api ay nangangailangan ng pagpapatunay', ], ], 'manage_customization' => [ diff --git a/resources/lang/pt_BR/settings.php b/resources/lang/pt_BR/settings.php index 52a2212f..a5030d5b 100644 --- a/resources/lang/pt_BR/settings.php +++ b/resources/lang/pt_BR/settings.php @@ -18,6 +18,8 @@ 'only_show_disrupted_days' => 'Exibir Apenas Dias com Interrupções', 'recent_incidents_only' => 'Exibir Apenas Incidentes Recentes', 'recent_incidents_days' => 'Número de Dias para Exibir Incidentes Recentes', + 'api_enabled' => 'API Ativada', + 'api_protected' => 'API requer autenticação', ], ], 'manage_customization' => [ diff --git a/resources/lang/zh_CN/settings.php b/resources/lang/zh_CN/settings.php index 2067d453..46f1d4bd 100644 --- a/resources/lang/zh_CN/settings.php +++ b/resources/lang/zh_CN/settings.php @@ -18,6 +18,11 @@ 'only_show_disrupted_days' => '仅显示受干扰天数', 'recent_incidents_only' => '仅显示最近事件', 'recent_incidents_days' => '显示最近事件的天数', + 'manage_customization' => [ + 'header_label' => '自定义 Header HTML', + 'footer_label' => '自定义 Footer HTML', + 'stylesheet_label' => '自定义 CSS', + ], ], ], 'manage_customization' => [ diff --git a/resources/lang/zh_TW/settings.php b/resources/lang/zh_TW/settings.php index a2f4a2ff..dd9fbe60 100644 --- a/resources/lang/zh_TW/settings.php +++ b/resources/lang/zh_TW/settings.php @@ -18,6 +18,8 @@ 'only_show_disrupted_days' => '僅顯示受干擾天數', 'recent_incidents_only' => '僅顯示最近事件', 'recent_incidents_days' => '顯示最近事件的天數', + 'api_enabled' => '啟用 Api', + 'api_protected' => 'Api 需要身份驗證', ], ], 'manage_customization' => [ diff --git a/src/CachetCoreServiceProvider.php b/src/CachetCoreServiceProvider.php index 2bf53e99..3772c5b5 100644 --- a/src/CachetCoreServiceProvider.php +++ b/src/CachetCoreServiceProvider.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; +use Spatie\LaravelSettings\Exceptions\MissingSettings; use Spatie\WebhookServer\Events\WebhookCallFailedEvent; use Spatie\WebhookServer\Events\WebhookCallSucceededEvent; diff --git a/src/Filament/Pages/Settings/ManageCachet.php b/src/Filament/Pages/Settings/ManageCachet.php index ad5eafd1..800d55c5 100644 --- a/src/Filament/Pages/Settings/ManageCachet.php +++ b/src/Filament/Pages/Settings/ManageCachet.php @@ -84,6 +84,8 @@ public function form(Form $form): Form ->label(__('cachet::settings.manage_cachet.toggles.only_show_disrupted_days')), Forms\Components\Toggle::make('dashboard_login_link') ->label(__('cachet::settings.manage_cachet.toggles.show_dashboard_link')), + + Forms\Components\Grid::make(2) ->schema([ Forms\Components\Toggle::make('recent_incidents_only') @@ -98,6 +100,23 @@ public function form(Form $form): Form ->suffix(__('cachet::settings.manage_cachet.recent_incidents_days_suffix_days')) ->hidden(fn (Get $get) => $get('recent_incidents_only') !== true), ]), + + + Forms\Components\Grid::make(2) + ->schema([ + Forms\Components\Toggle::make('api_enabled') + ->label(__('cachet::settings.manage_cachet.toggles.api_enabled')) + ->afterStateUpdated(function (Get $get, Forms\Set $set, ?bool $old, ?bool $state) { + if ($state === false) { + $set('api_protected', $state); + } + }) + ->reactive(), + Forms\Components\Toggle::make('api_protected') + ->label(__('cachet::settings.manage_cachet.toggles.api_protected')) + ->visible(fn (Get $get) => $get('api_enabled')) + ->reactive(), + ]), ]), ]); } diff --git a/src/Http/Middleware/ApiEnabled.php b/src/Http/Middleware/ApiEnabled.php new file mode 100644 index 00000000..fb70ca51 --- /dev/null +++ b/src/Http/Middleware/ApiEnabled.php @@ -0,0 +1,26 @@ +$name; + } catch (MissingSettings $exception) { + if(! app()->runningInConsole()) { + throw new \Exception("Please run `php artisan migrate` to load missing settings."); + } + } + return $default; + } } diff --git a/tests/Feature/Api/ApiFunctionalityTest.php b/tests/Feature/Api/ApiFunctionalityTest.php new file mode 100644 index 00000000..71273ef0 --- /dev/null +++ b/tests/Feature/Api/ApiFunctionalityTest.php @@ -0,0 +1,87 @@ +api_enabled = false; + $settings->api_protected = false; + $settings->save(); + + getJson('/status/api/ping') + ->assertNotFound(); +}); + +it('has api disabled with protected and user', function () { + $settings = app(AppSettings::class); + $settings->api_enabled = false; + $settings->api_protected = true; + $settings->save(); + + Sanctum::actingAs(User::factory()->create(), ['general.ping']); + + getJson('/status/api/ping') + ->assertNotFound(); +}); + +it('has api disabled without protected and user', function () { + $settings = app(AppSettings::class); + $settings->api_enabled = false; + $settings->api_protected = true; + $settings->save(); + + Sanctum::actingAs(User::factory()->create(), ['general.ping']); + + getJson('/status/api/ping') + ->assertNotFound(); +}); + + +it('has public api access', function () { + $settings = app(AppSettings::class); + $settings->api_enabled = true; + $settings->api_protected = false; + $settings->save(); + + Sanctum::actingAs(User::factory()->create(), ['general.ping']); + + getJson('/status/api/ping') + ->assertOk(); +}); + + +it('has public api access with a user', function () { + $settings = app(AppSettings::class); + $settings->api_enabled = true; + $settings->api_protected = false; + $settings->save(); + + getJson('/status/api/ping') + ->assertOk(); +}); + +it('has no access to api without a user', function () { + $settings = app(AppSettings::class); + $settings->api_enabled = true; + $settings->api_protected = true; + $settings->save(); + getJson('/status/api/ping') + ->assertUnauthorized(); +}); + + +it('has access to api with a user', function () { + $settings = app(AppSettings::class); + $settings->api_enabled = true; + $settings->api_protected = true; + $settings->save(); + + Sanctum::actingAs(User::factory()->create(), ['general.ping']); + + getJson('/status/api/ping') + ->assertOk(); +});