This commit is contained in:
2026-06-28 20:32:52 +03:30
parent 7a5b3f0bea
commit bf35179d55
45 changed files with 1555 additions and 46 deletions
+14
View File
@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthorizationService } from './authorization.service';
import { Permission } from './entities/permission.entity';
import { UserPermission } from './entities/user-permission.entity';
import { CustomerPolicy } from './policies/customer.policy';
import { SalonPolicy } from './policies/salon.policy';
@Module({
imports: [TypeOrmModule.forFeature([Permission, UserPermission])],
providers: [AuthorizationService, SalonPolicy, CustomerPolicy],
exports: [AuthorizationService, SalonPolicy, CustomerPolicy],
})
export class AuthorizationModule {}
@@ -0,0 +1,31 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserRole } from '../users/enums/user-role.enum';
import { AuthUser } from '../auth/interfaces/auth-user.interface';
import { UserPermission } from './entities/user-permission.entity';
@Injectable()
export class AuthorizationService {
constructor(
@InjectRepository(UserPermission)
private readonly userPermissionsRepository: Repository<UserPermission>,
) {}
async loadPermissions(userId: string): Promise<string[]> {
const userPermissions = await this.userPermissionsRepository.find({
where: { userId },
relations: { permission: true },
});
return userPermissions.map((up) => up.permission.code);
}
hasPermission(user: AuthUser, code: string): boolean {
return user.permissions.includes(code);
}
isAdmin(user: AuthUser): boolean {
return user.role === UserRole.ADMIN;
}
}
@@ -0,0 +1,13 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('permissions')
export class Permission {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ unique: true })
code: string;
@Column()
description: string;
}
@@ -0,0 +1,31 @@
import {
Column,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
Unique,
} from 'typeorm';
import { User } from '../../users/entities/user.entity';
import { Permission } from './permission.entity';
@Entity('user_permissions')
@Unique(['userId', 'permissionId'])
export class UserPermission {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
userId: string;
@Column()
permissionId: string;
@ManyToOne(() => User, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'userId' })
user: User;
@ManyToOne(() => Permission, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'permissionId' })
permission: Permission;
}
@@ -0,0 +1,10 @@
export enum PermissionCode {
USERS_READ = 'users:read',
USERS_WRITE = 'users:write',
USERS_DELETE = 'users:delete',
SALONS_READ = 'salons:read',
SALONS_WRITE = 'salons:write',
SALONS_DELETE = 'salons:delete',
CUSTOMERS_READ = 'customers:read',
CUSTOMERS_WRITE = 'customers:write',
}
@@ -0,0 +1,30 @@
import { Injectable } from '@nestjs/common';
import { AuthUser } from '../../auth/interfaces/auth-user.interface';
import { Customer } from '../../customers/entities/customer.entity';
import { UserRole } from '../../users/enums/user-role.enum';
import { AuthorizationService } from '../authorization.service';
@Injectable()
export class CustomerPolicy {
constructor(private readonly authorizationService: AuthorizationService) {}
canRead(user: AuthUser, customer: Customer): boolean {
if (this.authorizationService.isAdmin(user)) {
return true;
}
if (user.role === UserRole.CUSTOMER) {
return customer.userId === user.id;
}
return false;
}
canUpdate(user: AuthUser, customer: Customer): boolean {
if (this.authorizationService.isAdmin(user)) {
return true;
}
if (user.role === UserRole.CUSTOMER) {
return customer.userId === user.id;
}
return false;
}
}
@@ -0,0 +1,43 @@
import { Injectable } from '@nestjs/common';
import { AuthUser } from '../../auth/interfaces/auth-user.interface';
import { Salon } from '../../salons/entities/salon.entity';
import { UserRole } from '../../users/enums/user-role.enum';
import { AuthorizationService } from '../authorization.service';
@Injectable()
export class SalonPolicy {
constructor(private readonly authorizationService: AuthorizationService) {}
canRead(user: AuthUser, salon: Salon): boolean {
if (this.authorizationService.isAdmin(user)) {
return true;
}
if (user.role === UserRole.SALON) {
return salon.ownerId === user.id;
}
if (user.role === UserRole.CUSTOMER) {
return true;
}
return false;
}
canUpdate(user: AuthUser, salon: Salon): boolean {
if (this.authorizationService.isAdmin(user)) {
return true;
}
if (user.role === UserRole.SALON) {
return salon.ownerId === user.id;
}
return false;
}
canDelete(user: AuthUser, salon: Salon): boolean {
if (this.authorizationService.isAdmin(user)) {
return true;
}
if (user.role === UserRole.SALON) {
return salon.ownerId === user.id;
}
return false;
}
}