Skip to content

Commit cac184d

Browse files
Orrisondanharrin
andauthored
Queued Auditing (#846)
* Setup the dispatching event Signed-off-by: Kevin Ullyott <[email protected]> * Setup Audit dispatching and queuing Signed-off-by: Kevin Ullyott <[email protected]> * Finish customization and tests Signed-off-by: Kevin Ullyott <[email protected]> * Remove ray Signed-off-by: Kevin Ullyott <[email protected]> * Change the config description Signed-off-by: Kevin Ullyott <[email protected]> * Fix config issues Signed-off-by: Kevin Ullyott <[email protected]> * Fix resolving users in the queue Signed-off-by: Kevin Ullyott <[email protected]> * 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 <[email protected]> * Remove retrieved Signed-off-by: Kevin Ullyott <[email protected]> * Only run listener test in Laravel 8 and above Signed-off-by: Kevin Ullyott <[email protected]> * Fix PHP 7.4 issues Signed-off-by: Kevin Ullyott <[email protected]> --------- Signed-off-by: Kevin Ullyott <[email protected]> Co-authored-by: Dan Harrin <[email protected]>
1 parent 1a9cc9a commit cac184d

16 files changed

+330
-31
lines changed

config/audit.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,21 @@
156156
],
157157
],
158158

159+
/*
160+
|--------------------------------------------------------------------------
161+
| Audit Queue Configurations
162+
|--------------------------------------------------------------------------
163+
|
164+
| Available audit queue configurations.
165+
|
166+
*/
167+
168+
'queue' => [
169+
'connection' => 'sync',
170+
'queue' => 'default',
171+
'delay' => 0,
172+
],
173+
159174
/*
160175
|--------------------------------------------------------------------------
161176
| Audit Console

src/Auditable.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ trait Auditable
5656
*/
5757
public $isCustomEvent = false;
5858

59+
/**
60+
* @var array Preloaded data to be used by resolvers
61+
*/
62+
public $preloadedResolverData = [];
63+
5964
/**
6065
* Auditable boot logic.
6166
*
@@ -372,7 +377,7 @@ protected function resolveUser()
372377
}
373378

374379
if (is_subclass_of($userResolver, \OwenIt\Auditing\Contracts\UserResolver::class)) {
375-
return call_user_func([$userResolver, 'resolve']);
380+
return call_user_func([$userResolver, 'resolve'], $this);
376381
}
377382

378383
throw new AuditingException('Invalid UserResolver implementation');
@@ -403,6 +408,17 @@ protected function runResolvers(): array
403408
return $resolved;
404409
}
405410

411+
public function preloadResolverData()
412+
{
413+
$this->preloadedResolverData = $this->runResolvers();
414+
415+
if (!empty ($this->resolveUser())) {
416+
$this->preloadedResolverData['user'] = $this->resolveUser();
417+
}
418+
419+
return $this;
420+
}
421+
406422
/**
407423
* Determine if an attribute is eligible for auditing.
408424
*

src/AuditableObserver.php

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
namespace OwenIt\Auditing;
44

55
use OwenIt\Auditing\Contracts\Auditable;
6-
use OwenIt\Auditing\Facades\Auditor;
6+
use OwenIt\Auditing\Events\DispatchAudit;
7+
use OwenIt\Auditing\Events\DispatchingAudit;
78

89
class AuditableObserver
910
{
@@ -23,7 +24,7 @@ class AuditableObserver
2324
*/
2425
public function retrieved(Auditable $model)
2526
{
26-
Auditor::execute($model->setAuditEvent('retrieved'));
27+
$this->dispatchAudit($model->setAuditEvent('retrieved'));
2728
}
2829

2930
/**
@@ -35,7 +36,7 @@ public function retrieved(Auditable $model)
3536
*/
3637
public function created(Auditable $model)
3738
{
38-
Auditor::execute($model->setAuditEvent('created'));
39+
$this->dispatchAudit($model->setAuditEvent('created'));
3940
}
4041

4142
/**
@@ -49,7 +50,7 @@ public function updated(Auditable $model)
4950
{
5051
// Ignore the updated event when restoring
5152
if (!static::$restoring) {
52-
Auditor::execute($model->setAuditEvent('updated'));
53+
$this->dispatchAudit($model->setAuditEvent('updated'));
5354
}
5455
}
5556

@@ -62,7 +63,7 @@ public function updated(Auditable $model)
6263
*/
6364
public function deleted(Auditable $model)
6465
{
65-
Auditor::execute($model->setAuditEvent('deleted'));
66+
$this->dispatchAudit($model->setAuditEvent('deleted'));
6667
}
6768

6869
/**
@@ -89,10 +90,33 @@ public function restoring(Auditable $model)
8990
*/
9091
public function restored(Auditable $model)
9192
{
92-
Auditor::execute($model->setAuditEvent('restored'));
93+
$this->dispatchAudit($model->setAuditEvent('restored'));
9394

9495
// Once the model is restored, we need to put everything back
9596
// as before, in case a legitimate update event is fired
9697
static::$restoring = false;
9798
}
99+
100+
protected function dispatchAudit(Auditable $model)
101+
{
102+
if (!$model->readyForAuditing() || !$this->fireDispatchingAuditEvent($model)) {
103+
return;
104+
}
105+
106+
// Unload the relations to prevent large amounts of unnecessary data from being serialized.
107+
DispatchAudit::dispatch($model->preloadResolverData()->withoutRelations());
108+
}
109+
110+
/**
111+
* Fire the Auditing event.
112+
*
113+
* @param \OwenIt\Auditing\Contracts\Auditable $model
114+
*
115+
* @return bool
116+
*/
117+
protected function fireDispatchingAuditEvent(Auditable $model): bool
118+
{
119+
return app()->make('events')
120+
->until(new DispatchingAudit($model)) !== false;
121+
}
98122
}

src/AuditingEventServiceProvider.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,23 @@ class_alias(\Illuminate\Foundation\Support\Providers\EventServiceProvider::class
77
} else {
88
class_alias(\Laravel\Lumen\Providers\EventServiceProvider::class, '\OwenIt\Auditing\ServiceProvider');
99
}
10+
11+
use Illuminate\Support\Facades\Event;
12+
use Illuminate\Support\Facades\Config;
1013
use OwenIt\Auditing\Events\AuditCustom;
14+
use OwenIt\Auditing\Events\DispatchAudit;
1115
use OwenIt\Auditing\Listeners\RecordCustomAudit;
16+
use OwenIt\Auditing\Listeners\ProcessDispatchAudit;
1217

1318
class AuditingEventServiceProvider extends ServiceProvider
1419
{
1520
protected $listen = [
1621
AuditCustom::class => [
1722
RecordCustomAudit::class,
18-
]
23+
],
24+
DispatchAudit::class => [
25+
ProcessDispatchAudit::class,
26+
],
1927
];
2028

2129
/**

src/AuditingServiceProvider.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use OwenIt\Auditing\Console\InstallCommand;
1010
use OwenIt\Auditing\Contracts\Auditor;
1111

12-
class AuditingServiceProvider extends ServiceProvider implements DeferrableProvider
12+
class AuditingServiceProvider extends ServiceProvider
1313
{
1414
/**
1515
* Bootstrap the service provider.
@@ -64,14 +64,4 @@ private function registerPublishing()
6464
}
6565
}
6666
}
67-
68-
/**
69-
* {@inheritdoc}
70-
*/
71-
public function provides()
72-
{
73-
return [
74-
Auditor::class,
75-
];
76-
}
7767
}

src/Contracts/UserResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ interface UserResolver
99
*
1010
* @return \Illuminate\Contracts\Auth\Authenticatable|null
1111
*/
12-
public static function resolve();
12+
public static function resolve(Auditable $auditable);
1313
}

src/Events/DispatchAudit.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace OwenIt\Auditing\Events;
4+
5+
use OwenIt\Auditing\Contracts\Auditable;
6+
use Illuminate\Foundation\Events\Dispatchable;
7+
8+
class DispatchAudit
9+
{
10+
use Dispatchable;
11+
12+
/**
13+
* The Auditable model.
14+
*
15+
* @var Auditable
16+
*/
17+
public $model;
18+
19+
/**
20+
* Create a new DispatchAudit event instance.
21+
*
22+
* @param Auditable $model
23+
*/
24+
public function __construct(Auditable $model)
25+
{
26+
$this->model = $model;
27+
}
28+
}

src/Events/DispatchingAudit.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace OwenIt\Auditing\Events;
4+
5+
use OwenIt\Auditing\Contracts\Auditable;
6+
7+
class DispatchingAudit
8+
{
9+
/**
10+
* The Auditable model.
11+
*
12+
* @var Auditable
13+
*/
14+
public $model;
15+
16+
/**
17+
* Create a new DispatchingAudit event instance.
18+
*
19+
* @param Auditable $model
20+
*/
21+
public function __construct(Auditable $model)
22+
{
23+
$this->model = $model;
24+
}
25+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace OwenIt\Auditing\Listeners;
4+
5+
use OwenIt\Auditing\Facades\Auditor;
6+
use Illuminate\Support\Facades\Config;
7+
use OwenIt\Auditing\Events\DispatchAudit;
8+
use Illuminate\Contracts\Queue\ShouldQueue;
9+
10+
class ProcessDispatchAudit implements ShouldQueue
11+
{
12+
public function viaConnection(): string
13+
{
14+
return Config::get('audit.queue.connection', 'sync');
15+
}
16+
17+
public function viaQueue(): string
18+
{
19+
return Config::get('audit.queue.queue', 'default');
20+
}
21+
22+
public function withDelay(DispatchAudit $event): int
23+
{
24+
return Config::get('audit.queue.delay', 0);
25+
}
26+
27+
public function handle(DispatchAudit $event)
28+
{
29+
Auditor::execute($event->model);
30+
}
31+
}

src/Resolvers/IpAddressResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ class IpAddressResolver implements Resolver
1010
{
1111
public static function resolve(Auditable $auditable): string
1212
{
13-
return Request::ip();
13+
return $auditable->preloadedResolverData['ip_address'] ?? Request::ip();
1414
}
1515
}

src/Resolvers/UrlResolver.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ class UrlResolver implements \OwenIt\Auditing\Contracts\Resolver
1313
*/
1414
public static function resolve(Auditable $auditable): string
1515
{
16+
if (! empty($auditable->preloadedResolverData['url'])) {
17+
return $auditable->preloadedResolverData['url'];
18+
}
19+
1620
if (App::runningInConsole()) {
1721
return self::resolveCommandLine();
1822
}

src/Resolvers/UserAgentResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ class UserAgentResolver implements Resolver
1010
{
1111
public static function resolve(Auditable $auditable)
1212
{
13-
return Request::header('User-Agent');
13+
return $auditable->preloadedResolverData['user_agent'] ?? Request::header('User-Agent');
1414
}
1515
}

src/Resolvers/UserResolver.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,19 @@
44

55
use Illuminate\Support\Facades\Auth;
66
use Illuminate\Support\Facades\Config;
7+
use OwenIt\Auditing\Contracts\Auditable;
78

89
class UserResolver implements \OwenIt\Auditing\Contracts\UserResolver
910
{
1011
/**
1112
* @return \Illuminate\Contracts\Auth\Authenticatable|null
1213
*/
13-
public static function resolve()
14+
public static function resolve(Auditable $auditable)
1415
{
16+
if (! empty($auditable->preloadedResolverData['user'])) {
17+
return $auditable->preloadedResolverData['user'];
18+
}
19+
1520
$guards = Config::get('audit.user.guards', [
1621
\config('auth.defaults.guard')
1722
]);

tests/Unit/AuditTest.php

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -415,14 +415,7 @@ public function itReturnsAuditableModifiedAttributesAsJsonString()
415415
*/
416416
public function itReturnsDecodedAuditableAttributes()
417417
{
418-
$article = new class () extends Article {
419-
protected $table = 'articles';
420-
421-
protected $attributeModifiers = [
422-
'title' => Base64Encoder::class,
423-
'content' => LeftRedactor::class,
424-
];
425-
};
418+
$article = new itReturnsDecodedAuditableAttributesArticle();
426419

427420
// Audit with redacted/encoded attributes
428421
$audit = factory(Audit::class)->create([
@@ -489,3 +482,13 @@ public function itReturnsEmptyTags()
489482
$this->assertEmpty($audit->getTags());
490483
}
491484
}
485+
486+
class itReturnsDecodedAuditableAttributesArticle extends Article
487+
{
488+
protected $table = 'articles';
489+
490+
protected $attributeModifiers = [
491+
'title' => Base64Encoder::class,
492+
'content' => LeftRedactor::class,
493+
];
494+
}

0 commit comments

Comments
 (0)