Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions app/Filament/Admin/Resources/Roles/Pages/EditRole.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
namespace App\Filament\Admin\Resources\Roles\Pages;

use App\Enums\TablerIcon;
use App\Facades\Activity;
use App\Filament\Admin\Resources\Roles\RoleResource;
use App\Models\Role;
use App\Observers\AuditObserver;
use App\Traits\Filament\CanCustomizeHeaderActions;
use App\Traits\Filament\CanCustomizeHeaderWidgets;
use Filament\Actions\Action;
Expand Down Expand Up @@ -42,6 +44,8 @@ protected function mutateFormDataBeforeSave(array $data): array

protected function afterSave(): void
{
$oldPermissions = $this->record->permissions()->pluck('name')->sort()->values()->all();

$permissionModels = collect();
$this->permissions->each(function ($permission) use ($permissionModels) {
$permissionModels->push(Permission::firstOrCreate([
Expand All @@ -51,6 +55,17 @@ protected function afterSave(): void
});

$this->record->syncPermissions($permissionModels);

$newPermissions = $this->record->permissions()->pluck('name')->sort()->values()->all();

// Permissions are a relation, so the AuditObserver never sees them change.
if ($oldPermissions !== $newPermissions) {
Activity::event('role:update')
->subject($this->record)
->property(AuditObserver::identify($this->record))
->property('changes', ['permissions' => ['old' => $oldPermissions, 'new' => $newPermissions]])
->log();
}
}

/** @return array<Action|ActionGroup> */
Expand Down
29 changes: 26 additions & 3 deletions app/Observers/AuditObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class AuditObserver
*
* @var string[]
*/
protected static array $identifyingAttributes = ['id', 'uuid', 'name', 'username', 'email'];
protected static array $identifyingAttributes = ['id', 'uuid', 'name', 'username', 'email', 'identifier', 'endpoint', 'database', 'ip', 'port'];
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Attribute changes that are never worth an audit row.
Expand Down Expand Up @@ -112,7 +112,9 @@ public static function activityKey(Model $record): string
*/
public static function identify(Model $record): array
{
return array_intersect_key($record->getAttributes(), array_flip(static::$identifyingAttributes));
$attributes = array_intersect_key($record->getAttributes(), array_flip(static::$identifyingAttributes));

return collect($attributes)->map(fn ($value, $key) => static::redact($key, $value))->all();
}

/**
Expand Down Expand Up @@ -142,7 +144,28 @@ public static function buildDiff(array $old, array $new): array

public static function redact(string $key, mixed $value): mixed
{
if ($value !== null && $value !== '' && Str::is(static::$redactedAttributePatterns, strtolower($key))) {
if ($value === null || $value === '') {
return $value;
}

// URLs can carry credentials in user-info or the query string.
if (strtolower($key) === 'endpoint' && is_string($value)) {
$parts = parse_url($value);

if (!is_array($parts) || !isset($parts['host'])) {
return '********';
}

return sprintf(
'%s%s%s%s',
isset($parts['scheme']) ? $parts['scheme'] . '://' : '',
$parts['host'],
isset($parts['port']) ? ':' . $parts['port'] : '',
$parts['path'] ?? '',
);
}

if (Str::is(static::$redactedAttributePatterns, strtolower($key))) {
return '********';
}

Expand Down
13 changes: 12 additions & 1 deletion app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@
use App\Models\Allocation;
use App\Models\ApiKey;
use App\Models\Backup;
use App\Models\BackupHost;
use App\Models\Database;
use App\Models\DatabaseHost;
use App\Models\Egg;
use App\Models\EggVariable;
use App\Models\Mount;
use App\Models\Node;
use App\Models\Role;
use App\Models\Schedule;
use App\Models\Server;
use App\Models\Task;
use App\Models\User;
use App\Models\UserSSHKey;
use App\Models\WebhookConfiguration;
use App\Observers\AuditObserver;
use App\Services\Helpers\PluginService;
use App\Services\Helpers\SoftwareVersionService;
Expand Down Expand Up @@ -81,9 +85,16 @@ public function boot(
'user' => User::class,
'mount' => Mount::class,
'node' => Node::class,
'backup_host' => BackupHost::class,
'database_host' => DatabaseHost::class,
'role' => Role::class,
'webhook' => WebhookConfiguration::class,
]);

foreach ([Node::class, Egg::class, Mount::class, User::class, Server::class] as $model) {
foreach ([
Node::class, Egg::class, Mount::class, User::class, Server::class,
Role::class, ApiKey::class, DatabaseHost::class, BackupHost::class, WebhookConfiguration::class, Database::class,
] as $model) {
$model::observe(AuditObserver::class);
}

Expand Down
27 changes: 27 additions & 0 deletions lang/en/activity.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,23 @@
'update' => 'Updated user <b>:username</b>',
'delete' => 'Deleted user <b>:username</b>',
],
'apiKey' => [
'create' => 'Created API key <b>:identifier</b>',
'delete' => 'Deleted API key <b>:identifier</b>',
],
'backupHost' => [
'create' => 'Created backup host <b>:name</b>',
'update' => 'Updated backup host <b>:name</b>',
'delete' => 'Deleted backup host <b>:name</b>',
],
'database' => [
'delete' => 'Deleted database <b>:database</b>',
],
'databaseHost' => [
'create' => 'Created database host <b>:name</b>',
'update' => 'Updated database host <b>:name</b>',
'delete' => 'Deleted database host <b>:name</b>',
],
'egg' => [
'create' => 'Created egg <b>:name</b>',
'update' => 'Updated egg <b>:name</b>',
Expand All @@ -56,6 +73,16 @@
'update' => 'Updated node <b>:name</b>',
'delete' => 'Deleted node <b>:name</b>',
],
'role' => [
'create' => 'Created role <b>:name</b>',
'update' => 'Updated role <b>:name</b>',
'delete' => 'Deleted role <b>:name</b>',
],
'webhook' => [
'create' => 'Created webhook <b>:endpoint</b>',
'update' => 'Updated webhook <b>:endpoint</b>',
'delete' => 'Deleted webhook <b>:endpoint</b>',
],
'server' => [
'console' => [
'command' => 'Executed "<b>:command</b>" on the server',
Expand Down
Loading
Loading