feat: owner read-only view-as from team users table
Allow org owners to inspect any member's data scope via dual JWT; server blocks mutations and team page stays reachable for return. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
ForbiddenException,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
} from '@nestjs/common';
|
||||
import type { Request } from 'express';
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Actor } from '../common/record-scope';
|
||||
|
||||
const VIEW_AS_MESSAGE =
|
||||
'در حالت مشاهده بهجای کاربر دیگر امکان تغییر وجود ندارد.';
|
||||
|
||||
function normalizePath(url: string): string {
|
||||
const path = (url.split('?')[0] || '').replace(/\/+$/, '') || '/';
|
||||
return path;
|
||||
}
|
||||
|
||||
/** مسیرهایی که در حالت مشاهده مجاز به تغییرند (خروج از مشاهده و لنز بایگانی). */
|
||||
function isAllowedWhileViewing(method: string, url: string): boolean {
|
||||
const path = normalizePath(url);
|
||||
const m = method.toUpperCase();
|
||||
if (m === 'POST' && (path.endsWith('/auth/view-as') || path.endsWith('/auth/view-as/stop'))) {
|
||||
return true;
|
||||
}
|
||||
if (m === 'PATCH' && path.endsWith('/auth/me/prior-archive')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ViewAsReadOnlyInterceptor implements NestInterceptor {
|
||||
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
|
||||
const req = context.switchToHttp().getRequest<Request & { user?: Actor }>();
|
||||
const method = (req.method || 'GET').toUpperCase();
|
||||
if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS') {
|
||||
return next.handle();
|
||||
}
|
||||
const actor = req.user;
|
||||
if (!actor?.viewAsReadOnly) {
|
||||
return next.handle();
|
||||
}
|
||||
const url = req.originalUrl || req.url || '';
|
||||
if (isAllowedWhileViewing(method, url)) {
|
||||
return next.handle();
|
||||
}
|
||||
throw new ForbiddenException(VIEW_AS_MESSAGE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user