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:
@@ -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() }],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 = [
|
||||||
|
|||||||
Reference in New Issue
Block a user