fix(access): honor finance sections and team-member data scope
Role treasury keys now open the first allowed finance page, and team-member ownership limits lists, dashboard, and reports instead of the whole organization. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import type {
|
||||
SectionDataAccess,
|
||||
UserRole,
|
||||
} from '@mento/shared';
|
||||
import { DATA_SCOPES, canViewAllRecords } from '@mento/shared';
|
||||
import { DATA_SCOPES, canViewAllRecords, sectionHasDataAccessLimits } from '@mento/shared';
|
||||
import type { Prisma } from '@prisma/client';
|
||||
import {
|
||||
activitiesDeepWhere,
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
notesDeepWhere,
|
||||
projectsDeepWhere,
|
||||
routinesDeepWhere,
|
||||
sectionOwnerIdUnion,
|
||||
ticketsDeepWhere,
|
||||
} from './data-access';
|
||||
|
||||
@@ -60,6 +61,65 @@ export type Actor = {
|
||||
|
||||
export type ScopedActor = Pick<Actor, 'userId' | 'role' | 'dataVisibility' | 'visibleUserIds' | 'scopes'>;
|
||||
|
||||
type DeepAccessActor = ScopedActor &
|
||||
Partial<
|
||||
Pick<
|
||||
Actor,
|
||||
| 'customersAccess'
|
||||
| 'leadsAccess'
|
||||
| 'salesAccess'
|
||||
| 'activitiesAccess'
|
||||
| 'notesAccess'
|
||||
| 'routinesAccess'
|
||||
| 'projectsAccess'
|
||||
| 'ticketsAccess'
|
||||
| 'teamPeerIds'
|
||||
>
|
||||
>;
|
||||
|
||||
function sectionForScope(actor: DeepAccessActor, scope: DataScope): SectionDataAccess | undefined {
|
||||
switch (scope) {
|
||||
case 'contacts':
|
||||
return actor.customersAccess;
|
||||
case 'leads':
|
||||
return actor.leadsAccess;
|
||||
case 'deals':
|
||||
return actor.salesAccess;
|
||||
case 'activities':
|
||||
return actor.activitiesAccess;
|
||||
case 'notes':
|
||||
return actor.notesAccess;
|
||||
case 'routines':
|
||||
return actor.routinesAccess;
|
||||
case 'projects':
|
||||
return actor.projectsAccess;
|
||||
case 'tickets':
|
||||
return actor.ticketsAccess;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* شناسههای مالک از قانون عمق داده.
|
||||
* legacy = بخش عمیق روی این Actor نیست.
|
||||
* open = محدودیت مالکی ندارد (همه، یا فقط فیلترهای غیرمالک).
|
||||
*/
|
||||
function deepOwnerIds(
|
||||
actor: DeepAccessActor,
|
||||
scope: DataScope,
|
||||
): { kind: 'legacy' } | { kind: 'open' } | { kind: 'ids'; ids: string[] } {
|
||||
const section = sectionForScope(actor, scope);
|
||||
if (!section) return { kind: 'legacy' };
|
||||
if (!sectionHasDataAccessLimits(section)) return { kind: 'open' };
|
||||
const ids = sectionOwnerIdUnion(section, {
|
||||
userId: actor.userId,
|
||||
teamPeerIds: actor.teamPeerIds ?? null,
|
||||
});
|
||||
if (ids === null) return { kind: 'open' };
|
||||
return { kind: 'ids', ids };
|
||||
}
|
||||
|
||||
/** Actor با سطح دید مخصوص یک بخش (اگر تنظیم شده باشد). */
|
||||
export function forScope<T extends ScopedActor>(actor: T, scope: DataScope): T {
|
||||
const s = actor.scopes?.[scope];
|
||||
@@ -109,9 +169,16 @@ export function scopedOwnerIds(
|
||||
|
||||
/** فیلتر مالکیت؛ پیشفرض بخش «مشتریان» (Person / Company). برای فاکتور/پیشفاکتور scope=finance. */
|
||||
export function ownerWhere(
|
||||
actor: ScopedActor,
|
||||
actor: DeepAccessActor,
|
||||
scope: DataScope = 'contacts',
|
||||
): { ownerId: string } | { ownerId: { in: string[] } } | Record<string, never> {
|
||||
const deep = deepOwnerIds(actor, scope);
|
||||
if (deep.kind === 'open') return {};
|
||||
if (deep.kind === 'ids') {
|
||||
if (deep.ids.length === 1) return { ownerId: deep.ids[0]! };
|
||||
if (!deep.ids.length) return { ownerId: '__no_access__' };
|
||||
return { ownerId: { in: deep.ids } };
|
||||
}
|
||||
const ids = scopedOwnerIds(actor, scope);
|
||||
if (!ids) return {};
|
||||
if (ids.length === 1) return { ownerId: ids[0]! };
|
||||
@@ -153,11 +220,16 @@ export function projectCardAccessWhere(
|
||||
}
|
||||
|
||||
/** فعالیتها بر اساس مسئول اصلی یا مسئولان مشترک */
|
||||
export function assigneeWhere(
|
||||
actor: ScopedActor,
|
||||
): Prisma.ActivityWhereInput {
|
||||
const ids = scopedOwnerIds(actor, 'activities');
|
||||
export function assigneeWhere(actor: DeepAccessActor): Prisma.ActivityWhereInput {
|
||||
const deep = deepOwnerIds(actor, 'activities');
|
||||
const ids =
|
||||
deep.kind === 'open'
|
||||
? null
|
||||
: deep.kind === 'ids'
|
||||
? deep.ids
|
||||
: scopedOwnerIds(actor, 'activities');
|
||||
if (!ids) return {};
|
||||
if (!ids.length) return { assigneeId: '__no_access__' };
|
||||
return {
|
||||
OR: [
|
||||
{ assigneeId: { in: ids } },
|
||||
|
||||
Reference in New Issue
Block a user