fix: record real admin identity in Users/Transactions audit trail

audit entries hardcoded actor: 'admin' regardless of who performed the
action. Both local gateways now pull the signed-in admin's displayName
from AdminAuthService, falling back to 'admin' only when unavailable.

Moderation's actor field is a role classifier ('admin' | 'customer'),
not an identity string, and is left unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-08-13 07:15:59 +04:00
parent c3b5820ac9
commit 0646d587eb
2 changed files with 18 additions and 6 deletions

View File

@@ -1,18 +1,24 @@
import { Injectable } from '@angular/core'; import { Injectable, inject } from '@angular/core';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators'; import { delay } from 'rxjs/operators';
import { AdminTransaction, AdminTransactionListFilters, AdminTransactionsListResult } from '../models/admin-transaction.model'; import { AdminTransaction, AdminTransactionListFilters, AdminTransactionsListResult } from '../models/admin-transaction.model';
import { AdminTransactionsGateway } from './admin-transactions-gateway.interface'; import { AdminTransactionsGateway } from './admin-transactions-gateway.interface';
import { AdminOrdersLocalGateway } from '../../orders/services/admin-orders-local.gateway'; import { AdminOrdersLocalGateway } from '../../orders/services/admin-orders-local.gateway';
import { AdminAuthService } from '../../../../core/admin-auth/admin-auth.service';
const METHODS = ['card', 'qr', 'cash_on_delivery']; const METHODS = ['card', 'qr', 'cash_on_delivery'];
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AdminTransactionsLocalGateway implements AdminTransactionsGateway { export class AdminTransactionsLocalGateway implements AdminTransactionsGateway {
private readonly adminAuth = inject(AdminAuthService);
private cache: AdminTransaction[] | null = null; private cache: AdminTransaction[] | null = null;
constructor(private readonly ordersGateway: AdminOrdersLocalGateway) {} constructor(private readonly ordersGateway: AdminOrdersLocalGateway) {}
private get currentActor(): string {
return this.adminAuth.displayName() ?? 'admin';
}
loadTransactions(filters: AdminTransactionListFilters): Observable<AdminTransactionsListResult> { loadTransactions(filters: AdminTransactionListFilters): Observable<AdminTransactionsListResult> {
return new Observable<AdminTransactionsListResult>(subscriber => { return new Observable<AdminTransactionsListResult>(subscriber => {
this.ensureData().then(() => { this.ensureData().then(() => {
@@ -38,7 +44,7 @@ export class AdminTransactionsLocalGateway implements AdminTransactionsGateway {
...tx, ...tx,
status: 'retried', status: 'retried',
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
audit: [...tx.audit, { action: 'Retried failed transaction', actor: 'admin', timestamp: new Date().toISOString() }], audit: [...tx.audit, { action: 'Retried failed transaction', actor: this.currentActor, timestamp: new Date().toISOString() }],
})); }));
} }
@@ -47,7 +53,7 @@ export class AdminTransactionsLocalGateway implements AdminTransactionsGateway {
...tx, ...tx,
fraudFlag: flagged, fraudFlag: flagged,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
audit: [...tx.audit, { action: flagged ? 'Flagged as fraud' : 'Fraud flag cleared', actor: 'admin', timestamp: new Date().toISOString() }], audit: [...tx.audit, { action: flagged ? 'Flagged as fraud' : 'Fraud flag cleared', actor: this.currentActor, timestamp: new Date().toISOString() }],
})); }));
} }

View File

@@ -1,8 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable, inject } from '@angular/core';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators'; import { delay } from 'rxjs/operators';
import { AdminInvitation, AdminRole, AdminSession, AdminUser, AdminUserAuditEntry, AdminUserScope, AdminUserStatus } from '../models/admin-user.model'; import { AdminInvitation, AdminRole, AdminSession, AdminUser, AdminUserAuditEntry, AdminUserScope, AdminUserStatus } from '../models/admin-user.model';
import { AdminUsersGateway } from './admin-users-gateway.interface'; import { AdminUsersGateway } from './admin-users-gateway.interface';
import { AdminAuthService } from '../../../../core/admin-auth/admin-auth.service';
const BUILT_IN_ROLES: AdminRole[] = [ const BUILT_IN_ROLES: AdminRole[] = [
{ id: 'owner', name: 'Owner', permissions: ['*'], builtIn: true }, { id: 'owner', name: 'Owner', permissions: ['*'], builtIn: true },
@@ -13,6 +14,7 @@ const BUILT_IN_ROLES: AdminRole[] = [
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AdminUsersLocalGateway implements AdminUsersGateway { export class AdminUsersLocalGateway implements AdminUsersGateway {
private readonly adminAuth = inject(AdminAuthService);
private users: AdminUser[] | null = null; private users: AdminUser[] | null = null;
private roles: AdminRole[] = [...BUILT_IN_ROLES]; private roles: AdminRole[] = [...BUILT_IN_ROLES];
private invitations: AdminInvitation[] = []; private invitations: AdminInvitation[] = [];
@@ -90,12 +92,16 @@ export class AdminUsersLocalGateway implements AdminUsersGateway {
this.audit[userId] = [ this.audit[userId] = [
...(this.audit[userId] ?? []), ...(this.audit[userId] ?? []),
roleId roleId
? { eventKey: 'roleChanged', roleId, actor: 'admin', timestamp: new Date().toISOString() } ? { eventKey: 'roleChanged', roleId, actor: this.currentActor, timestamp: new Date().toISOString() }
: { eventKey: 'statusChanged', status, actor: 'admin', timestamp: new Date().toISOString() }, : { eventKey: 'statusChanged', status, actor: this.currentActor, timestamp: new Date().toISOString() },
]; ];
return of(updated).pipe(delay(50)); return of(updated).pipe(delay(50));
} }
private get currentActor(): string {
return this.adminAuth.displayName() ?? 'admin';
}
private ensureUsers(): AdminUser[] { private ensureUsers(): AdminUser[] {
if (!this.users) { if (!this.users) {
this.users = [ this.users = [