Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage api from Cachet Settings page (#235) #236

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions config/cachet.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@

'api_middleware' => [
'api',
\Cachet\Http\Middleware\ApiEnabled::class,
\Cachet\Http\Middleware\ApiPublicOrProtected::class,
],

/*
Expand Down
25 changes: 25 additions & 0 deletions database/migrations/2025_02_09_073310_add_api_settings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

use Spatie\LaravelSettings\Migrations\SettingsMigration;

return new class extends SettingsMigration
{
/**
* Run the migrations.
*/
public function up(): void
{
// Theme settings...
rescue(fn () => $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'));
}
};
2 changes: 2 additions & 0 deletions database/seeders/DatabaseSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,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);
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/de/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/de_AT/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/de_CH/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/en/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/nl/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/ph/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/pt_BR/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down
5 changes: 5 additions & 0 deletions resources/lang/zh_CN/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/zh_TW/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
'only_show_disrupted_days' => '僅顯示受干擾天數',
'recent_incidents_only' => '僅顯示最近事件',
'recent_incidents_days' => '顯示最近事件的天數',
'api_enabled' => '啟用 Api',
'api_protected' => 'Api 需要身份驗證',
],
],
'manage_customization' => [
Expand Down
1 change: 1 addition & 0 deletions src/CachetCoreServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Spatie\LaravelSettings\Exceptions\MissingSettings;
use Spatie\WebhookServer\Events\WebhookCallFailedEvent;
use Spatie\WebhookServer\Events\WebhookCallSucceededEvent;

Expand Down
14 changes: 14 additions & 0 deletions src/Filament/Pages/Settings/ManageCachet.php
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -98,6 +100,18 @@ 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'))
->reactive(),
Forms\Components\Toggle::make('api_protected')
->label(__('cachet::settings.manage_cachet.toggles.api_protected'))
->visible(fn (Get $get) => $get('api_enabled'))
->reactive(),
]),
]),
]);
}
Expand Down
26 changes: 26 additions & 0 deletions src/Http/Middleware/ApiEnabled.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Cachet\Http\Middleware;

use Cachet\Settings\AppSettings;
use Closure;
use Illuminate\Http\Request;

class ApiEnabled
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure(Request):mixed $next
* @return mixed
*
*/
public function handle($request, Closure $next)
{
if (!AppSettings::getOrDefault('api_enabled', true)) {
abort(404);
}
return $next($request);
}
}
31 changes: 31 additions & 0 deletions src/Http/Middleware/ApiPublicOrProtected.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Cachet\Http\Middleware;

use Cachet\Settings\AppSettings;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Auth\Middleware\Authenticate as BaseAuthenticationMiddleware;
use Illuminate\Http\Request;

class ApiPublicOrProtected extends BaseAuthenticationMiddleware
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure(Request):mixed $next
* @param string ...$guards
* @return mixed
*
* @throws AuthenticationException
*/
public function handle($request, Closure $next, ...$guards)
{
$protected = AppSettings::getOrDefault('api_protected', false);
if ($protected) {
return parent::handle($request, $next, ...$guards);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is trying to redirect to a login route which doesn't exist in Cachet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do

Copy link
Contributor Author

@icecoldPHP icecoldPHP Mar 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbrooksuk This is something that is managed by Sanctum.
We could override this because this is currently a global issue.

So do you want me to override the sanctum routes within the Core?
or do you want me to fix this in the Cachet repo?
Or both

Because on my instance i changed this on the cachet repo by hand (sanctum config).
So it redirect to the correct login page

}
return $next($request);
}
}
26 changes: 26 additions & 0 deletions src/Settings/AppSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Cachet\Settings;

use Spatie\LaravelSettings\Exceptions\MissingSettings;
use Spatie\LaravelSettings\Settings;

class AppSettings extends Settings
Expand Down Expand Up @@ -32,8 +33,33 @@ class AppSettings extends Settings

public int $recent_incidents_days = 7;

public bool $api_enabled = true;

public bool $api_protected = false;

public static function group(): string
{
return 'app';
}


/**
* Retrieve settings without triggering an exception
* unless a migration has to be run
* @param $name
* @param $default
* @return mixed
* @throws \Exception
*/
public static function getOrDefault($name, $default = null): mixed
{
try {
return app(self::class)->$name;
} catch (MissingSettings $exception) {
if(! app()->runningInConsole()) {
throw new \Exception("Please run `php artisan migrate` to load missing settings.");
}
}
return $default;
}
}
87 changes: 87 additions & 0 deletions tests/Feature/Api/ApiFunctionalityTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

use Cachet\Settings\AppSettings;
use Laravel\Sanctum\Sanctum;
use Workbench\App\User;
use function Pest\Laravel\getJson;


it('has api disabled', function () {
$settings = app(AppSettings::class);
$settings->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();
});