Skip to content

Commit f61514b

Browse files
committed
fix(security): eliminate pre-commit side-effects and stale IP in RevokeUserGrants
Audit records (AddUserAction, EmitAuditLogJob) were dispatched before revokeUsersToken() ran, so a transient failure would leave duplicate and misleading entries in the audit history on each retry. Move both dispatches to after revokeUsersToken() returns cleanly so audit records are only emitted on success. Capture IPHelper::getUserIp() in the constructor where the originating request context is still valid, and store it as a job property. Replace the two in-handle IPHelper calls with the stored value so the correct client IP is recorded regardless of when the worker processes the job.
1 parent 630d325 commit f61514b

File tree

2 files changed

+28
-16
lines changed

2 files changed

+28
-16
lines changed

app/Jobs/RevokeUserGrants.php

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,22 @@ abstract class RevokeUserGrants implements ShouldQueue
4949
*/
5050
private string $reason;
5151

52+
/**
53+
* @var string
54+
*/
55+
private string $client_ip;
56+
5257
/**
5358
* @param User $user
5459
* @param string|null $client_id null = revoke across all clients
5560
* @param string $reason audit message suffix
5661
*/
5762
public function __construct(User $user, ?string $client_id, string $reason)
5863
{
59-
$this->user_id = $user->getId();
60-
$this->client_id = $client_id;
61-
$this->reason = $reason;
64+
$this->user_id = $user->getId();
65+
$this->client_id = $client_id;
66+
$this->reason = $reason;
67+
$this->client_ip = IPHelper::getUserIp();
6268
Log::debug(sprintf(
6369
"RevokeUserGrants::constructor user %s client_id %s reason %s",
6470
$this->user_id,
@@ -71,6 +77,14 @@ public function handle(ITokenService $service): void
7177
{
7278
Log::debug("RevokeUserGrants::handle");
7379

80+
try {
81+
$service->revokeUsersToken($this->user_id, $this->client_id);
82+
} catch (\Exception $ex) {
83+
Log::warning(sprintf("RevokeUserGrants::handle attempt %d failed: %s",
84+
$this->attempts(), $ex->getMessage()));
85+
throw $ex;
86+
}
87+
7488
$scope = !empty($this->client_id)
7589
? sprintf("client %s", $this->client_id)
7690
: "all clients";
@@ -82,7 +96,7 @@ public function handle(ITokenService $service): void
8296
$this->reason
8397
);
8498

85-
AddUserAction::dispatch($this->user_id, IPHelper::getUserIp(), $action);
99+
AddUserAction::dispatch($this->user_id, $this->client_ip, $action);
86100

87101
// Emit to OTEL audit log (Elasticsearch) if enabled
88102
if (config('opentelemetry.enabled', false)) {
@@ -95,18 +109,10 @@ public function handle(ITokenService $service): void
95109
'audit.reason' => $this->reason,
96110
'audit.scope' => $scope,
97111
'auth.user.id' => $this->user_id,
98-
'client.ip' => IPHelper::getUserIp(),
112+
'client.ip' => $this->client_ip,
99113
'elasticsearch.index' => config('opentelemetry.logs.elasticsearch_index', 'logs-audit'),
100114
]);
101115
}
102-
103-
try {
104-
$service->revokeUsersToken($this->user_id, $this->client_id);
105-
} catch (\Exception $ex) {
106-
Log::warning(sprintf("RevokeUserGrants::handle attempt %d failed: %s",
107-
$this->attempts(), $ex->getMessage()));
108-
throw $ex;
109-
}
110116
}
111117

112118
public function failed(\Throwable $exception): void

app/Services/OpenId/UserService.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,9 @@ public function create(array $payload): IEntity
293293
*/
294294
public function update(int $id, array $payload): IEntity
295295
{
296-
$user = $this->tx_service->transaction(function () use ($id, $payload) {
296+
$password_changed = false;
297+
298+
$user = $this->tx_service->transaction(function () use ($id, $payload, &$password_changed) {
297299

298300
$user = $this->repository->getById($id);
299301

@@ -373,13 +375,17 @@ public function update(int $id, array $payload): IEntity
373375

374376
if ($former_password != $user->getPassword()) {
375377
Log::warning(sprintf("UserService::update use id %s - password changed", $id));
376-
request()->session()->regenerate();
377-
Event::dispatch(new UserPasswordResetSuccessful($user->getId()));
378+
$password_changed = true;
378379
}
379380

380381
return $user;
381382
});
382383

384+
if ($password_changed) {
385+
request()->session()->regenerate();
386+
Event::dispatch(new UserPasswordResetSuccessful($user->getId()));
387+
}
388+
383389
try {
384390
if (Config::get("queue.enable_message_broker", false) == true)
385391
PublishUserUpdated::dispatch($user)->onConnection('message_broker');

0 commit comments

Comments
 (0)