diff options
Diffstat (limited to 'server/providers')
-rw-r--r-- | server/providers/guards/auth.guard.ts | 17 | ||||
-rw-r--r-- | server/providers/guards/roles.guard.ts | 37 | ||||
-rw-r--r-- | server/providers/services/roles.service.ts | 25 | ||||
-rw-r--r-- | server/providers/services/users.service.ts | 6 |
4 files changed, 83 insertions, 2 deletions
diff --git a/server/providers/guards/auth.guard.ts b/server/providers/guards/auth.guard.ts index d7da81e..8c03ad8 100644 --- a/server/providers/guards/auth.guard.ts +++ b/server/providers/guards/auth.guard.ts @@ -1,13 +1,28 @@ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { JwtService } from '../services/jwt.service'; +import { SKIP_KEY } from 'server/decorators/skip.decorator'; +import { Reflector } from '@nestjs/core'; +import { Class } from 'server/dto/class.dto'; @Injectable() export class AuthGuard implements CanActivate { - constructor(private jwtService: JwtService) {} + constructor(private reflector: Reflector, private jwtService: JwtService) {} canActivate(context: ExecutionContext) { + const skippedGuards = this.reflector.getAllAndOverride<Class<CanActivate>[]>(SKIP_KEY, [ + context.getHandler(), + context.getClass(), + ]); + if (skippedGuards) { + const skippedGuard = skippedGuards.find((guard) => this instanceof guard); + if (skippedGuard) { + return true; + } + } const req = context.switchToHttp().getRequest(); const authHeader = req.headers.authorization; + if (!authHeader) return false; + const jwt = authHeader.split(' ')[1]; try { req.jwtBody = this.jwtService.parseToken(jwt); diff --git a/server/providers/guards/roles.guard.ts b/server/providers/guards/roles.guard.ts index e69de29..3ecc392 100644 --- a/server/providers/guards/roles.guard.ts +++ b/server/providers/guards/roles.guard.ts @@ -0,0 +1,37 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { ROLES_CONTEXT_KEY } from 'server/decorators/roles.decorator'; +import { JwtBodyDto } from 'server/dto/jwt_body.dto'; +import { RoleKey } from 'server/entities/role.entity'; +import { RolesService } from '../services/roles.service'; +import { UsersService } from '../services/users.service'; +import { some } from 'lodash'; + +@Injectable() +export class RolesGuard implements CanActivate { + constructor(private reflector: Reflector, private usersService: UsersService, private rolesService: RolesService) {} + + async canActivate(context: ExecutionContext): Promise<boolean> { + const requiredRoles = this.reflector.getAllAndOverride<RoleKey[]>(ROLES_CONTEXT_KEY, [ + context.getHandler(), + context.getClass(), + ]); + console.log(requiredRoles); + + if (!requiredRoles) { + return true; + } + + const jwtBody: JwtBodyDto = context.switchToHttp().getRequest().jwtBody; + + if (!jwtBody) return false; // unauthenticated users are not authorized + + const user = await this.usersService.find(jwtBody.userId, ['userRoles']); + const roles = await this.rolesService.findByKey(...requiredRoles); + const roleMatches = user.userRoles.map((userRole) => { + return !!roles.find((role) => role.id === userRole.roleId); + }); + + return some(roleMatches); + } +} diff --git a/server/providers/services/roles.service.ts b/server/providers/services/roles.service.ts new file mode 100644 index 0000000..7cd17ef --- /dev/null +++ b/server/providers/services/roles.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { In, Repository } from 'typeorm'; +import * as bcrypt from 'bcrypt'; +import { Role, RoleKey } from 'server/entities/role.entity'; + +@Injectable() +export class RolesService { + constructor( + @InjectRepository(Role) + private rolesRepository: Repository<Role>, + ) {} + + findByKey(...keys: RoleKey[]) { + return this.rolesRepository.find({ where: { key: In(keys) } }); + } + + findByIds(ids: number[]) { + return this.rolesRepository.findByIds(ids); + } + + find(id: number, relations: string[] = []) { + return this.rolesRepository.findOne(id, { relations }); + } +} diff --git a/server/providers/services/users.service.ts b/server/providers/services/users.service.ts index 47a0360..c3ee086 100644 --- a/server/providers/services/users.service.ts +++ b/server/providers/services/users.service.ts @@ -11,6 +11,10 @@ export class UsersService { private usersRespository: Repository<User>, ) {} + findAll(relations: string[] = []) { + return this.usersRespository.find({ relations }); + } + findBy(options: Record<string, any>, relations: string[] = []) { return this.usersRespository.findOne(options, { relations }); } @@ -24,7 +28,7 @@ export class UsersService { } async verify(email: string, password: string) { - const user = await this.usersRespository.findOne({ email }, { relations: ['refreshTokens'] }); + const user = await this.usersRespository.findOne({ email }, { relations: ['refreshTokens', 'userRoles'] }); if (!user) return { verified: false, user: null }; const verified: boolean = await bcrypt.compare(password, user.passwordHash); return { verified, user: verified ? user : null }; |