Skip to content

Commit 9cd211c

Browse files
committed
chore: revamp OTEL audit log
1 parent a531804 commit 9cd211c

File tree

10 files changed

+304
-151
lines changed

10 files changed

+304
-151
lines changed

app/Audit/AuditContext.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php namespace App\Audit;
2+
/**
3+
* Copyright 2025 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
class AuditContext
15+
{
16+
public function __construct(
17+
public ?int $userId = null,
18+
public ?string $userEmail = null,
19+
public ?string $userFirstName = null,
20+
public ?string $userLastName = null,
21+
public ?string $uiApp = null,
22+
public ?string $uiFlow = null,
23+
public ?string $route = null,
24+
public ?string $httpMethod = null,
25+
public ?string $clientIp = null,
26+
public ?string $userAgent = null,
27+
) {}
28+
}

app/Audit/AuditEventListener.php

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
<?php
2-
3-
namespace App\Audit;
4-
1+
<?php namespace App\Audit;
52
/**
6-
* Copyright 2022 OpenStack Foundation
3+
* Copyright 2025 OpenStack Foundation
74
* Licensed under the Apache License, Version 2.0 (the "License");
85
* you may not use this file except in compliance with the License.
96
* You may obtain a copy of the License at
@@ -15,7 +12,7 @@
1512
* limitations under the License.
1613
**/
1714

18-
use App\Audit\AuditLogOtlpStrategy;
15+
use App\Audit\Interfaces\IAuditStrategy;
1916
use Doctrine\ORM\Event\OnFlushEventArgs;
2017
use Illuminate\Support\Facades\App;
2118
use Illuminate\Support\Facades\Log;
@@ -33,26 +30,26 @@ public function onFlush(OnFlushEventArgs $eventArgs): void
3330
$uow = $em->getUnitOfWork();
3431
// Strategy selection based on environment configuration
3532
$strategy = $this->getAuditStrategy($em);
36-
33+
$ctx = $this->buildAuditContext();
3734
if (!$strategy) {
3835
return; // No audit strategy enabled
3936
}
4037

4138
try {
4239
foreach ($uow->getScheduledEntityInsertions() as $entity) {
43-
$strategy->audit($entity, [], AuditLogOtlpStrategy::EVENT_ENTITY_CREATION);
40+
$strategy->audit($entity, [], IAuditStrategy::EVENT_ENTITY_CREATION, $ctx);
4441
}
4542

4643
foreach ($uow->getScheduledEntityUpdates() as $entity) {
47-
$strategy->audit($entity, $uow->getEntityChangeSet($entity), AuditLogOtlpStrategy::EVENT_ENTITY_UPDATE);
44+
$strategy->audit($entity, $uow->getEntityChangeSet($entity), IAuditStrategy::EVENT_ENTITY_UPDATE, $ctx);
4845
}
4946

5047
foreach ($uow->getScheduledEntityDeletions() as $entity) {
51-
$strategy->audit($entity, [], AuditLogOtlpStrategy::EVENT_ENTITY_DELETION);
48+
$strategy->audit($entity, [], IAuditStrategy::EVENT_ENTITY_DELETION, $ctx);
5249
}
5350

5451
foreach ($uow->getScheduledCollectionUpdates() as $col) {
55-
$strategy->audit($col, [], AuditLogOtlpStrategy::EVENT_COLLECTION_UPDATE);
52+
$strategy->audit($col, [], IAuditStrategy::EVENT_COLLECTION_UPDATE, $ctx);
5653
}
5754
} catch (\Exception $e) {
5855
Log::error('Audit event listener failed', [
@@ -78,9 +75,36 @@ private function getAuditStrategy($em)
7875
]);
7976
}
8077
}
81-
78+
8279
// Use database strategy (either as default or fallback)
8380
return new AuditLogStrategy($em);
81+
}
82+
83+
private function buildAuditContext(): AuditContext
84+
{
85+
$resourceCtx = app(\models\oauth2\IResourceServerContext::class);
86+
$userExternalId = $resourceCtx->getCurrentUserId();
87+
$member = null;
88+
if ($userExternalId) {
89+
$memberRepo = app(\models\main\IMemberRepository::class);
90+
$member = $memberRepo->findOneBy(["user_external_id" => $userExternalId]);
91+
}
92+
93+
//$ui = app()->bound('ui.context') ? app('ui.context') : [];
94+
95+
$req = request();
8496

97+
return new AuditContext(
98+
userId: $member?->getId(),
99+
userEmail: $member?->getEmail(),
100+
userFirstName: $member?->getFirstName(),
101+
userLastName: $member?->getLastName(),
102+
uiApp: $ui['app'] ?? null,
103+
uiFlow: $ui['flow'] ?? null,
104+
route: $req?->path(),
105+
httpMethod: $req?->method(),
106+
clientIp: $req?->ip(),
107+
userAgent: $req?->userAgent(),
108+
);
85109
}
86110
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php namespace App\Audit;
2+
/**
3+
* Copyright 2025 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
use App\Audit\ConcreteFormatters\ChildEntityFormatters\ChildEntityFormatterFactory;
15+
use App\Audit\ConcreteFormatters\EntityCollectionUpdateAuditLogFormatter;
16+
use App\Audit\ConcreteFormatters\EntityCreationAuditLogFormatter;
17+
use App\Audit\ConcreteFormatters\EntityDeletionAuditLogFormatter;
18+
use App\Audit\ConcreteFormatters\EntityUpdateAuditLogFormatter;
19+
use App\Audit\Interfaces\IAuditStrategy;
20+
21+
class AuditLogFormatterFactory implements IAuditLogFormatterFactory
22+
{
23+
24+
public function make($subject, $eventType): ?IAuditLogFormatter
25+
{
26+
$formatter = null;
27+
switch ($eventType) {
28+
case IAuditStrategy::EVENT_COLLECTION_UPDATE:
29+
$child_entity = null;
30+
if (count($subject) > 0) {
31+
$child_entity = $subject[0];
32+
}
33+
if (is_null($child_entity) && count($subject->getSnapshot()) > 0) {
34+
$child_entity = $subject->getSnapshot()[0];
35+
}
36+
$child_entity_formatter = $child_entity != null ? ChildEntityFormatterFactory::build($child_entity) : null;
37+
$formatter = new EntityCollectionUpdateAuditLogFormatter($child_entity_formatter);
38+
break;
39+
case IAuditStrategy::EVENT_ENTITY_CREATION:
40+
$formatter = new EntityCreationAuditLogFormatter();
41+
break;
42+
case IAuditStrategy::EVENT_ENTITY_DELETION:
43+
$child_entity_formatter = ChildEntityFormatterFactory::build($subject);
44+
$formatter = new EntityDeletionAuditLogFormatter($child_entity_formatter);
45+
break;
46+
case IAuditStrategy::EVENT_ENTITY_UPDATE:
47+
$child_entity_formatter = ChildEntityFormatterFactory::build($subject);
48+
$formatter = new EntityUpdateAuditLogFormatter($child_entity_formatter);
49+
break;
50+
}
51+
return $formatter;
52+
}
53+
}

0 commit comments

Comments
 (0)