Skip to content

Commit

Permalink
authorization refactor for consistency
Browse files Browse the repository at this point in the history
  • Loading branch information
ZenSoftware committed Nov 25, 2021
1 parent f199d44 commit 0d31a5d
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 56 deletions.
25 changes: 25 additions & 0 deletions apps/api/src/app/auth/auth-logic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Role } from '@prisma/client';

type RoleType = string[] | Role[] | undefined;

export function authLogic(
userRoles: RoleType,
classRoles: RoleType,
handlerRoles: RoleType
): boolean {
const _userRoles = userRoles ?? [];
const _classRoles = classRoles ?? [];
const _handlerRoles = handlerRoles ?? [];

// Give super users unlimited access
if (_userRoles.includes(Role.Super)) return true;

if (_classRoles.length > 0) {
if (!_userRoles.some(r => _classRoles.includes(r as Role))) return false;
if (_handlerRoles.length > 0 && !_userRoles.some(r => _handlerRoles.includes(r))) return false;
} else if (_handlerRoles.length > 0 && !_userRoles.some(r => _handlerRoles.includes(r))) {
return false;
}

return true;
}
2 changes: 1 addition & 1 deletion apps/api/src/app/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '../jwt';
import { PrismaModule } from '../prisma';
import { AuthService } from './auth.service';
import { GqlGuard } from './gql';
import { GqlGuard } from './gql.guard';
import { JwtStrategy } from './jwt.strategy';

@Module({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ExecutionContext, UnauthorizedException, createParamDecorator } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';

import { RequestUser } from '../request-user';
import { RequestUser } from './request-user';

export const GqlUser = createParamDecorator((data, context: ExecutionContext) => {
const user = GqlExecutionContext.create(context).getContext().req.user;
Expand Down
30 changes: 30 additions & 0 deletions apps/api/src/app/auth/gql.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';
import { Role } from '@prisma/client';

import { authLogic } from './auth-logic';
import { ROLES_KEY } from './roles.decorator';

@Injectable()
/**
* Replicates RBAC rules for [ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-6.0).
* **Super** users are granted unlimited access.
*/
export class GqlGuard extends AuthGuard('jwt') {
constructor(private readonly reflector: Reflector) {
super();
}

async canActivate(context: ExecutionContext) {
await super.canActivate(context);

const ctx = GqlExecutionContext.create(context);
const user = ctx.getContext().req.user;
const classRoles = this.reflector.get<Role[]>(ROLES_KEY, ctx.getClass());
const handlerRoles = this.reflector.get<Role[]>(ROLES_KEY, ctx.getHandler());

return authLogic(user.roles, classRoles, handlerRoles);
}
}
47 changes: 0 additions & 47 deletions apps/api/src/app/auth/gql/gql.guard.ts

This file was deleted.

2 changes: 0 additions & 2 deletions apps/api/src/app/auth/gql/index.ts

This file was deleted.

24 changes: 24 additions & 0 deletions apps/api/src/app/auth/http.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { Role } from '@prisma/client';

import { authLogic } from './auth-logic';
import { ROLES_KEY } from './roles.decorator';

@Injectable()
export class HttpGuard extends AuthGuard('jwt') {
constructor(private readonly reflector: Reflector) {
super();
}

async canActivate(ctx: ExecutionContext) {
await super.canActivate(ctx);

const { user } = ctx.switchToHttp().getRequest();
const classRoles = this.reflector.get<Role[]>(ROLES_KEY, ctx.getClass());
const handlerRoles = this.reflector.get<Role[]>(ROLES_KEY, ctx.getHandler());

return authLogic(user.roles, classRoles, handlerRoles);
}
}
10 changes: 6 additions & 4 deletions apps/api/src/app/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export { AuthGuard } from '@nestjs/passport';
export * from './auth.module';
export * from './gql';
export { Role } from '@prisma/client';
export { RequestUser } from './request-user';
export * from './roles.decorator';
export * from './auth.module';
export * from './auth.service';
export * from './gql-user.decorator';
export * from './gql.guard';
export * from './http-user.decorator';
export { Role } from '@prisma/client';
export * from './http.guard';
export * from './roles.decorator';
3 changes: 2 additions & 1 deletion apps/api/src/app/auth/roles.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SetMetadata } from '@nestjs/common';
import { Role } from '@prisma/client';

export const Roles = (...roles: Array<Role>) => SetMetadata('roles', roles);
export const ROLES_KEY = 'roles';
export const Roles = (...roles: Array<Role>) => SetMetadata(ROLES_KEY, roles);

0 comments on commit 0d31a5d

Please sign in to comment.