Nest: Make @SetMetadata() work with controllers

Created on 18 Apr 2019  路  6Comments  路  Source: nestjs/nest

I'm submitting a...


[ ] Regression 
[ ] Bug report
[ x ] Feature request
[ x ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

At the moment @SetMetadata() only works with methods. It would be neat if it would work with controllers as well. Especially for CRUD functionality where we might want metadata set on a on the entire controller scope without having to mock every single method.

example

@SetMetadata('roles', ['admin', 'editor'])
@Crud(establishment)
@Controller('establishment'){
}

Most helpful comment

When you say it works with controllers, do you mean controller methods?

I meant that it works with controllers. Controller metadata is different than this one registered on top of methods (has to be reflected using reflector.getClass() not reflector.getHandler()).

All 6 comments

@SetMetadata() works with controllers. I'd rather create an issue in the nestjsx repo (perhaps, @Crud() decorator is not compatible with this feature).

Thanks @kamilmysliwiec

I'll redirect the issue.

@kamilmysliwiec When you say it works with controllers, do you mean controller methods?

As pointed out here https://github.com/nestjsx/crud/issues/71, @SetMetadata only seems to work in the scope of individual methods and not the entire controller.

When you say it works with controllers, do you mean controller methods?

I meant that it works with controllers. Controller metadata is different than this one registered on top of methods (has to be reflected using reflector.getClass() not reflector.getHandler()).

Hey people as @kamilmysliwiec says and for anyone faced this issue. I succeed by doing this:

// Crud Controller
@UseGuards(AuthGuard(), RoleGuard)
@Roles(["admin"]) // Here the class decorator
@Crud({
  model: { type: Product },
  routes: { getManyBase: { decorators: [Roles(["seller"])] } }, // Here a method decorator
})
@Controller("products")
export class ProductsController implements CrudController<Product> {
  constructor(public service: ProductsService) {}

Now you decorator look like this:
export const Roles = (roles: string[]) => ReflectMetadata("roles", roles);

Then you're Role guard could look like this:

@Injectable()
export class RoleGuard implements CanActivate {
  constructor(private readonly reflector: Reflector) {}

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const roles =
      this.reflector.get<string[]>("roles", context.getHandler()) || [];

    const rolesClass =
      this.reflector.get<string[]>("roles", context.getClass()) || [];

    const req = context.switchToHttp().getRequest();

    if (!roles.length && !rolesClass.length) {
      return true;
    }

    const allRoles = [...roles, ...rolesClass];

    const user: User = req.user;
    if (!user) {
      throw new InternalServerErrorException(
        "Cannot verify user authorization",
      );
    }
    if (!user.roles.some(role => allRoles.includes(role.name))) {
      throw new UnauthorizedException("You have not enough permission");
    }
    return true;
  }
}

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cojack picture cojack  路  3Comments

yanshuf0 picture yanshuf0  路  3Comments

mishelashala picture mishelashala  路  3Comments

menme95 picture menme95  路  3Comments

artaommahe picture artaommahe  路  3Comments