في أملاكي (amlakire.com)، عندنا ٦ أنواع مستخدمين على نفس المنصة. كيف نبني نظام صلاحيات مرن ودقيق؟
التحدي
كل نوع مستخدم له صلاحيات مختلفة. المكتب يدير كل شيء. المالك المستقل يدير عقاراته فقط. المالك التابع يقرأ فقط. المستأجر يرى بياناته فقط.
الحل: Custom Guards + Decorators
1. Permission Decorator
// decorators/permissions.decorator.ts
import { SetMetadata } from '@nestjs/common';export const RequirePermissions = (...permissions: string[]) =>
SetMetadata('permissions', permissions);
2. Permissions Guard
// guards/permissions.guard.ts
@Injectable()export class PermissionsGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const permissions = this.reflector.get(
'permissions', context.getHandler()
);
if (!permissions) return true;
const { user } = context.switchToHttp().getRequest();
// SUPER_ADMIN & AGENCY_ADMIN = full access
if (['SUPER_ADMIN', 'AGENCY_ADMIN'].includes(user.userType))
return true;
// OWNER = full on own properties
if (user.userType === 'OWNER') {
const ownerPermissions = [
'properties.*', 'units.*', 'tenants.*',
'contracts.*', 'payments.*', 'reports.view'
];
return permissions.every(p =>
ownerPermissions.some(op => this.matches(p, op))
);
}
// LINKED_OWNER = read only
if (user.userType === 'LINKED_OWNER')
return permissions.every(p => p.endsWith('.view'));
// AGENCY_STAFF = role-based
if (user.userType === 'AGENCY_STAFF')
return this.staffService.hasPermission(user.sub, permissions);
return false;
}
}
3. Controller Usage
@Controller('properties')@UseGuards(JwtAuthGuard, PermissionsGuard)
export class PropertiesController {
@Post() @RequirePermissions('properties.create') create() {}
@Get() @RequirePermissions('properties.view') findAll() {}
@Delete(':id') @RequirePermissions('properties.delete') delete() {}
}
4. Data Isolation in Service
@Injectable()export class PropertiesService {
async findAll(user: UserPayload) {
const where: any = {};
if (user.userType === 'AGENCY_ADMIN' || user.userType === 'AGENCY_STAFF')
where.agencyId = user.agencyId;
else if (user.userType === 'OWNER' || user.userType === 'LINKED_OWNER')
where.owner = { userId: user.sub };
return this.prisma.property.findMany({ where });
}
}
النتيجة
- ٦ أنواع مستخدمين على نفس الكود
- كل واحد يرى فقط ما يحق له
- الصلاحيات تُفحص في طبقتين: Guard (HTTP) + Service (Data)
مبني في amlakire.com
Top comments (0)