Skip to content

Commit

Permalink
Queued Auditing (#846)
Browse files Browse the repository at this point in the history
* Setup the dispatching event

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

* Setup Audit dispatching and queuing

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

* Finish customization and tests

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

* Remove ray

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

* Change the config description

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

* Fix config issues

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

* Fix resolving users in the queue

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

* Revert "Fix resolving users in the queue"

This reverts commit 6c44a2e.

* Revert "Revert "Fix resolving users in the queue""

This reverts commit 22cfbb9.

* Fix the observer

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

* Remove retrieved

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

* Only run listener test in Laravel 8 and above

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

* Fix PHP 7.4 issues

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>

---------

Signed-off-by: Kevin Ullyott <ullyott.kevin@gmail.com>
Co-authored-by: Dan Harrin <git@danharrin.com>
  • Loading branch information
Orrison and danharrin committed Dec 14, 2023
1 parent 1a9cc9a commit cac184d
Show file tree
Hide file tree
Showing 16 changed files with 330 additions and 31 deletions.
15 changes: 15 additions & 0 deletions config/audit.php
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
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
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
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
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
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
@@ -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
@@ -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
@@ -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
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
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
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
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
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,
];
}

0 comments on commit cac184d

Please sign in to comment.