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

[Feature] Audit Purging #45

Merged
merged 8 commits into from
Aug 21, 2023
Merged
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
2 changes: 1 addition & 1 deletion app-modules/assist-data-model/src/Models/Student.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function engagementFiles(): MorphToMany
table: 'engagement_file_entities',
foreignPivotKey: 'entity_id',
relatedPivotKey: 'engagement_file_id',
relation: 'student',
relation: 'engagementFiles',
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
return new class () extends SettingsMigration {
public function up(): void
{
$this->migrator->add('audit.retention_duration', 129600);
$this->migrator->add('audit.retention_duration_in_days', 90);
$this->migrator->add('audit.audited_models', AuditableModels::all()->keys());
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Assist\Audit\Console\Commands;

use Assist\Audit\Models\Audit;
use Illuminate\Console\Command;
use Assist\Audit\Settings\AuditSettings;

class PurgePastRetentionAuditRecordsCommand extends Command
{
protected $signature = 'audit:purge-past-retention-audit-records';

protected $description = 'Purge Audit records that are older than the retention duration.';

public function handle(AuditSettings $auditSettings): void
{
Audit::whereDate(
column: 'created_at',
operator: '<',
value: now()->subDays($auditSettings->retention_duration_in_days)
)->delete();
}
}
23 changes: 22 additions & 1 deletion app-modules/audit/src/Filament/Pages/ManageAuditSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Filament\Pages\SettingsPage;
use Filament\Forms\Components\Select;
use Assist\Audit\Settings\AuditSettings;
use Filament\Forms\Components\TextInput;
use Assist\Audit\Actions\Finders\AuditableModels;

class ManageAuditSettings extends SettingsPage
Expand All @@ -20,7 +21,27 @@ public function form(Form $form): Form
->schema([
Select::make('audited_models')
->options(AuditableModels::all())
->multiple(),
->multiple()
->in(AuditableModels::all()->keys()->toArray())
->rules(
[
'array',
]
)
->hintIcon(
icon: 'heroicon-m-question-mark-circle',
tooltip: 'Items added here will be tracked by the audit trail.'
),
TextInput::make('retention_duration_in_days')
->label('Retention Duration')
->integer()
->minValue(1)
->step(1)
->suffix('Day/s')
->hintIcon(
icon: 'heroicon-m-question-mark-circle',
tooltip: 'Audit trail records older than the retention duration will be deleted.'
),
]);
}
}
25 changes: 25 additions & 0 deletions app-modules/audit/src/Overrides/Concerns/AttachOverrides.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Assist\Audit\Overrides\Concerns;

use ReflectionClass;
use Illuminate\Support\Facades\Event;
use OwenIt\Auditing\Events\AuditCustom;
use OwenIt\Auditing\Contracts\Auditable;
Expand All @@ -12,6 +13,13 @@ public function attach($id, array $attributes = [], $touch = true)
{
/** @var Auditable $parentModel */
$parentModel = $this->getParent();

if (! $this->isAuditable($parentModel::class)) {
parent::attach($id, $attributes, $touch);

return;
}

$relationName = $this->relationName;

$parentModel->auditEvent = 'attach';
Expand All @@ -33,6 +41,11 @@ public function detach($ids = null, $touch = true)
{
/** @var Auditable $parentModel */
$parentModel = $this->getParent();

if (! $this->isAuditable($parentModel::class)) {
return parent::detach($ids, $touch);
}

$relationName = $this->relationName;

$parentModel->auditEvent = 'detach';
Expand All @@ -56,6 +69,11 @@ public function sync($ids, $detaching = true)
{
/** @var Auditable $parentModel */
$parentModel = $this->getParent();

if (! $this->isAuditable($parentModel::class)) {
return parent::sync($ids, $detaching);
}

$relationName = $this->relationName;

$parentModel->auditEvent = 'sync';
Expand All @@ -81,4 +99,11 @@ public function sync($ids, $detaching = true)

return $changes;
}

private function isAuditable(string $class)
{
$reflection = new ReflectionClass($class);

return $reflection->implementsInterface(Auditable::class);
}
}
8 changes: 8 additions & 0 deletions app-modules/audit/src/Providers/AuditServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Assist\Audit\AuditPlugin;
use Assist\Audit\Models\Audit;
use Illuminate\Support\ServiceProvider;
use Illuminate\Console\Scheduling\Schedule;
use Assist\Authorization\AuthorizationRoleRegistry;
use Illuminate\Database\Eloquent\Relations\Relation;
use Assist\Authorization\AuthorizationPermissionRegistry;
Expand Down Expand Up @@ -47,5 +48,12 @@ public function boot(AuthorizationPermissionRegistry $permissionRegistry, Author
module: 'audit',
path: 'roles/web'
);

$this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
$schedule->command('audit:purge-past-retention-audit-records')
->daily()
->evenInMaintenanceMode()
->onOneServer();
});
}
}
2 changes: 1 addition & 1 deletion app-modules/audit/src/Settings/AuditSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class AuditSettings extends Settings
{
public int $retention_duration;
public int $retention_duration_in_days;

public array $audited_models;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

use Assist\Audit\Models\Audit;

use function Pest\Laravel\artisan;
use function Pest\Laravel\travelTo;

use Illuminate\Support\Facades\Event;
use Assist\Audit\Settings\AuditSettings;
use Illuminate\Console\Events\ScheduledTaskStarting;

test('PurgePastRetentionAuditRecordsCommand properly deletes records', function () {
$auditSettings = resolve(AuditSettings::class);
$auditSettings->retention_duration_in_days = 90;
$auditSettings->save();

$retainedAudits = Audit::factory()
->count(5)
->create(
[
'created_at' => now()->subDays(30),
]
);

$purgedAudits = Audit::factory()
->count(5)
->create(
[
'created_at' => now()->subDays(100),
]
);

$auditCount = Audit::count();

artisan('audit:purge-past-retention-audit-records');

expect(Audit::count())->toBe($auditCount - $purgedAudits->count());

$retainedAudits->each(
function ($audit) {
expect(Audit::where('id', $audit->id)->exists())->toBeTrue();
}
);

$purgedAudits->each(
function ($audit) {
expect(Audit::where('id', $audit->id)->exists())->toBeFalse();
}
);
});

test('PurgePastRetentionAuditRecordsCommand is properly scheduled', function () {
Event::fake();

travelTo(now()->startOfDay());

artisan('schedule:run');

Event::assertDispatched(function (ScheduledTaskStarting $event) {
return str($event->task->command)->contains('audit:purge-past-retention-audit-records');
});
});
2 changes: 1 addition & 1 deletion app-modules/engagement/src/Models/EngagementFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public function students(): MorphToMany
table: 'engagement_file_entities',
foreignPivotKey: 'engagement_file_id',
relatedPivotKey: 'entity_id',
relation: 'student',
relation: 'engagementFiles',
Orrison marked this conversation as resolved.
Show resolved Hide resolved
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@
namespace App\Filament\Resources\UserResource\RelationManagers;

use Filament\Forms\Form;
use Illuminate\View\View;
use Filament\Tables\Table;
use Filament\Actions\Action;
use Filament\Tables\Columns\TextColumn;
use Filament\Forms\Components\TextInput;
use Assist\Authorization\Enums\ModelHasRolesViaEnum;
use Assist\Authorization\Events\RoleRemovedFromUser;
use Filament\Resources\RelationManagers\RelationManager;

class RolesRelationManager extends RelationManager
Expand Down