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

Queued Auditing #846

Merged
merged 14 commits into from
Dec 14, 2023
15 changes: 15 additions & 0 deletions config/audit.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,21 @@
],
],

/*
|--------------------------------------------------------------------------
| Audit Queue Configurations
|--------------------------------------------------------------------------
|
| Available audit queue configurations.
|
*/

'queue' => [
'connection' => 'sync',
'queue' => 'default',
'delay' => 0,
],

/*
|--------------------------------------------------------------------------
| Audit Console
Expand Down
18 changes: 17 additions & 1 deletion src/Auditable.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ trait Auditable
*/
public $isCustomEvent = false;

/**
* @var array Preloaded data to be used by resolvers
*/
public $preloadedResolverData = [];

/**
* Auditable boot logic.
*
Expand Down Expand Up @@ -372,7 +377,7 @@ protected function resolveUser()
}

if (is_subclass_of($userResolver, \OwenIt\Auditing\Contracts\UserResolver::class)) {
return call_user_func([$userResolver, 'resolve']);
return call_user_func([$userResolver, 'resolve'], $this);
}

throw new AuditingException('Invalid UserResolver implementation');
Expand Down Expand Up @@ -403,6 +408,17 @@ protected function runResolvers(): array
return $resolved;
}

public function preloadResolverData()
{
$this->preloadedResolverData = $this->runResolvers();

if (!empty ($this->resolveUser())) {
$this->preloadedResolverData['user'] = $this->resolveUser();
}

return $this;
}

/**
* Determine if an attribute is eligible for auditing.
*
Expand Down
36 changes: 30 additions & 6 deletions src/AuditableObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
namespace OwenIt\Auditing;

use OwenIt\Auditing\Contracts\Auditable;
use OwenIt\Auditing\Facades\Auditor;
use OwenIt\Auditing\Events\DispatchAudit;
use OwenIt\Auditing\Events\DispatchingAudit;

class AuditableObserver
{
Expand All @@ -23,7 +24,7 @@ class AuditableObserver
*/
public function retrieved(Auditable $model)
{
Auditor::execute($model->setAuditEvent('retrieved'));
$this->dispatchAudit($model->setAuditEvent('retrieved'));
}

/**
Expand All @@ -35,7 +36,7 @@ public function retrieved(Auditable $model)
*/
public function created(Auditable $model)
{
Auditor::execute($model->setAuditEvent('created'));
$this->dispatchAudit($model->setAuditEvent('created'));
}

/**
Expand All @@ -49,7 +50,7 @@ public function updated(Auditable $model)
{
// Ignore the updated event when restoring
if (!static::$restoring) {
Auditor::execute($model->setAuditEvent('updated'));
$this->dispatchAudit($model->setAuditEvent('updated'));
}
}

Expand All @@ -62,7 +63,7 @@ public function updated(Auditable $model)
*/
public function deleted(Auditable $model)
{
Auditor::execute($model->setAuditEvent('deleted'));
$this->dispatchAudit($model->setAuditEvent('deleted'));
}

/**
Expand All @@ -89,10 +90,33 @@ public function restoring(Auditable $model)
*/
public function restored(Auditable $model)
{
Auditor::execute($model->setAuditEvent('restored'));
$this->dispatchAudit($model->setAuditEvent('restored'));

// Once the model is restored, we need to put everything back
// as before, in case a legitimate update event is fired
static::$restoring = false;
}

protected function dispatchAudit(Auditable $model)
{
if (!$model->readyForAuditing() || !$this->fireDispatchingAuditEvent($model)) {
return;
}

// Unload the relations to prevent large amounts of unnecessary data from being serialized.
DispatchAudit::dispatch($model->preloadResolverData()->withoutRelations());
}

/**
* Fire the Auditing event.
*
* @param \OwenIt\Auditing\Contracts\Auditable $model
*
* @return bool
*/
protected function fireDispatchingAuditEvent(Auditable $model): bool
{
return app()->make('events')
->until(new DispatchingAudit($model)) !== false;
}
}
10 changes: 9 additions & 1 deletion src/AuditingEventServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ class_alias(\Illuminate\Foundation\Support\Providers\EventServiceProvider::class
} else {
class_alias(\Laravel\Lumen\Providers\EventServiceProvider::class, '\OwenIt\Auditing\ServiceProvider');
}

use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Config;
use OwenIt\Auditing\Events\AuditCustom;
use OwenIt\Auditing\Events\DispatchAudit;
use OwenIt\Auditing\Listeners\RecordCustomAudit;
use OwenIt\Auditing\Listeners\ProcessDispatchAudit;

class AuditingEventServiceProvider extends ServiceProvider
{
protected $listen = [
AuditCustom::class => [
RecordCustomAudit::class,
]
],
DispatchAudit::class => [
ProcessDispatchAudit::class,
],
];

/**
Expand Down
12 changes: 1 addition & 11 deletions src/AuditingServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use OwenIt\Auditing\Console\InstallCommand;
use OwenIt\Auditing\Contracts\Auditor;

class AuditingServiceProvider extends ServiceProvider implements DeferrableProvider
class AuditingServiceProvider extends ServiceProvider
{
/**
* Bootstrap the service provider.
Expand Down Expand Up @@ -64,14 +64,4 @@ private function registerPublishing()
}
}
}

/**
* {@inheritdoc}
*/
public function provides()
{
return [
Auditor::class,
];
}
}
2 changes: 1 addition & 1 deletion src/Contracts/UserResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ interface UserResolver
*
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public static function resolve();
public static function resolve(Auditable $auditable);
}
28 changes: 28 additions & 0 deletions src/Events/DispatchAudit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace OwenIt\Auditing\Events;

use OwenIt\Auditing\Contracts\Auditable;
use Illuminate\Foundation\Events\Dispatchable;

class DispatchAudit
{
use Dispatchable;

/**
* The Auditable model.
*
* @var Auditable
*/
public $model;

/**
* Create a new DispatchAudit event instance.
*
* @param Auditable $model
*/
public function __construct(Auditable $model)
{
$this->model = $model;
}
}
25 changes: 25 additions & 0 deletions src/Events/DispatchingAudit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace OwenIt\Auditing\Events;

use OwenIt\Auditing\Contracts\Auditable;

class DispatchingAudit
{
/**
* The Auditable model.
*
* @var Auditable
*/
public $model;

/**
* Create a new DispatchingAudit event instance.
*
* @param Auditable $model
*/
public function __construct(Auditable $model)
{
$this->model = $model;
}
}
31 changes: 31 additions & 0 deletions src/Listeners/ProcessDispatchAudit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace OwenIt\Auditing\Listeners;

use OwenIt\Auditing\Facades\Auditor;
use Illuminate\Support\Facades\Config;
use OwenIt\Auditing\Events\DispatchAudit;
use Illuminate\Contracts\Queue\ShouldQueue;

class ProcessDispatchAudit implements ShouldQueue
{
public function viaConnection(): string
{
return Config::get('audit.queue.connection', 'sync');
}

public function viaQueue(): string
{
return Config::get('audit.queue.queue', 'default');
}

public function withDelay(DispatchAudit $event): int
{
return Config::get('audit.queue.delay', 0);
}

public function handle(DispatchAudit $event)
{
Auditor::execute($event->model);
}
}
2 changes: 1 addition & 1 deletion src/Resolvers/IpAddressResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ class IpAddressResolver implements Resolver
{
public static function resolve(Auditable $auditable): string
{
return Request::ip();
return $auditable->preloadedResolverData['ip_address'] ?? Request::ip();
}
}
4 changes: 4 additions & 0 deletions src/Resolvers/UrlResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class UrlResolver implements \OwenIt\Auditing\Contracts\Resolver
*/
public static function resolve(Auditable $auditable): string
{
if (! empty($auditable->preloadedResolverData['url'])) {
return $auditable->preloadedResolverData['url'];
}

if (App::runningInConsole()) {
return self::resolveCommandLine();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Resolvers/UserAgentResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ class UserAgentResolver implements Resolver
{
public static function resolve(Auditable $auditable)
{
return Request::header('User-Agent');
return $auditable->preloadedResolverData['user_agent'] ?? Request::header('User-Agent');
}
}
7 changes: 6 additions & 1 deletion src/Resolvers/UserResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use OwenIt\Auditing\Contracts\Auditable;

class UserResolver implements \OwenIt\Auditing\Contracts\UserResolver
{
/**
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public static function resolve()
public static function resolve(Auditable $auditable)
{
if (! empty($auditable->preloadedResolverData['user'])) {
return $auditable->preloadedResolverData['user'];
}

$guards = Config::get('audit.user.guards', [
\config('auth.defaults.guard')
]);
Expand Down
19 changes: 11 additions & 8 deletions tests/Unit/AuditTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -415,14 +415,7 @@ public function itReturnsAuditableModifiedAttributesAsJsonString()
*/
public function itReturnsDecodedAuditableAttributes()
{
$article = new class () extends Article {
protected $table = 'articles';

protected $attributeModifiers = [
'title' => Base64Encoder::class,
'content' => LeftRedactor::class,
];
};
$article = new itReturnsDecodedAuditableAttributesArticle();

// Audit with redacted/encoded attributes
$audit = factory(Audit::class)->create([
Expand Down Expand Up @@ -489,3 +482,13 @@ public function itReturnsEmptyTags()
$this->assertEmpty($audit->getTags());
}
}

class itReturnsDecodedAuditableAttributesArticle extends Article
{
protected $table = 'articles';

protected $attributeModifiers = [
'title' => Base64Encoder::class,
'content' => LeftRedactor::class,
];
}
Loading