Nestjs access control cannot read roles

39 Views Asked by At

so I have a nestjs application with session authentication that I set to session by role. Then I followed one of other people's tutorials or repositories like this:

https://github.com/vladwulf/cwv-nestjs-rbac-tutorial/blob/main/apps/api

Then I get an error like this: AccessControlError: Invalid role(s): []

I set the role based on the data in the database to make it more dynamic

and this is how I configured my security

local.strategy.ts

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
    constructor(
        @Inject('AUTH_SERVICE') private authService: AuthService,
        @Inject('ROLE_REPOSITORY') private readonly roleRepository: RoleRepository
    ){
        super({
            usernameField: 'username'
        });
    }

    async validate(username: string, password: string):  Promise<any>{
        const user = await this.authService.validateUser(username, password);
        if(!user) {
            throw new UnauthorizedException("Username or password isn't valid.");
        }

        const isAdmin = user.role === this.findRole("ADMIN");
        const isOther = user.role === this.findRole("CREATOR");
        let userRole = await this.roleRepository.findRoleByName("USER"); 

        if(isAdmin) userRole = await this.roleRepository.findRoleByName("ADMIN");
        if(isOther) userRole = await this.roleRepository.findRoleByName("CREATOR");

        console.log({
            id: user.id,
            username: user.username,
            role: userRole
        })
        return {
            id: user.id,
            username: user.username,
            role: userRole
        };
    }

    async findRole(roleName: string): Promise<Roles>{
        const role = await this.roleRepository.findRoleByName(roleName);
        if(!role) throw new DataNotFoundException("Role not found.", 400);

        return role;
    }
}

session.serializer.ts

export class SessionSerializer extends PassportSerializer {
    constructor(
        @Inject('USER_SERVICE') private userService: UserService
    ){
        super();
    }

    serializeUser(user: User, done: (err: Error, user: User) => void) {
        done(null, user);
    }

    async deserializeUser(payload: User, done: Function) {
        const userDB = await this.userService.findByUsername(payload.username);
        if(!userDB) {
            return done(
                `Could not deserialize user: user with ${payload.username} coundn't be found.`,
                null
            );
        }

        done(null, userDB);
    }
}

local.guard.ts

@Injectable()
export class LocalGuard extends AuthGuard('local') {
  constructor(){
    super();
  }

  async canActivate(context: ExecutionContext){
      const result = (await super.canActivate(context)) as boolean;
      await super.logIn(context.switchToHttp().getRequest());

      return result;
  }
}

authenticated.guard.ts

@Injectable()
export class AuthenticatedGuard implements CanActivate {
  canActivate(context: ExecutionContext,): boolean | Promise<boolean> | Observable<boolean> {
    const req = context.switchToHttp().getRequest<Request>();
    
    return req.isAuthenticated();
  }
}

auth.service.ts

@Injectable()
export class AuthService implements IAuthService { 
    constructor(
        @Inject('USER_SERVICE') private userService: UserService,
        @Inject('USER_REPOSITORY') private readonly userRepsitory: UserRepository,
        @Inject('ROLE_REPOSITORY') private readonly roleRepository: RoleRepository
    ){}

    public async validateUser(username: string, password: string): Promise<any> {
        const user: User = await this.userService.findByUsername(username);
        const isPasswordMatch: boolean = await comparePassword(password, user.password);

        if(user && isPasswordMatch){
            const {password, ...rest} = user;
            return rest;
        }

        throw new ForbiddenException();
    }

rbac.policy.ts

import { RolesBuilder } from "nest-access-control";

export const RBAC_POLICY: RolesBuilder = new RolesBuilder();

//define rbac policy
RBAC_POLICY
    .grant('USER')
        .readOwn('userData')
        .createOwn('userData')
        .updateOwn('userData')
    .grant('CREATOR')
        .extend("USER")
        .read('creatorPosts')
        .create('creatorPosts')
        .update('creatorPosts')
        .delete('creatorPosts')
    .grant('ADMIN')
        .extend('CREATOR')
        .read('creatorData')
        .delete('creatorData')
    .deny('ADMIN')
        .create('creatorPosts')
        .update('creatorPosts')

config.module.ts

@Module({
    imports: [
        PassportModule.register({
            session: true,
        }),
        ConfigModule.forRoot(
            {
              envFilePath: '.env',
              isGlobal: true
            }
        ),
        AccessControlModule.forRoles(RBAC_POLICY)
    ]
})
export class ConfigurationAppModule {}

and then I tryna hit this API:

@UseRoles({
    resource: 'userData',
    action: 'read',
    possession: 'own'
})
@UseGuards(AuthenticatedGuard)
@Get('index')
public async index(
    @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number = 1,
    @Query('size', new DefaultValuePipe(5), ParseIntPipe) limit: number,
    @Req() request: Request
): Promise<Pagination<User>>{
    const options: IPaginationOptions = {
        limit: limit, 
        page: page, 
        route: request.url
    };

    return await this.userService.index(options);
}

Got error roles not know from access control. It makes me so confused why does the role not known by access control, even though I already wrote it in rbac.policy.ts

Is the rbac policy is false? or any else?

0

There are 0 best solutions below