javascript - Cómo acceder a la base de datos desde un guard de nestjs
Actualmente intento aprender NestJS, para eso seguí el tutorial oficial (gato-)para construir un sistema básico de autenticación de usuarios.
Cuando accedas a la API para cambiar el nombre de usuario o la contraseña, la solicitud debe verificar si el usuario es un usuario válido (leyendo el usuario desde la base de datos). Para almacenar el usuario, ya tengo una conexión a MongoDB.
Actualmente estoy teniendo dificultades sobre cómo puedo acceder al módulo/base de datos en el guardia.
El guardia está funcionando pero solo devuelve verdadero, intenté inyectar mi módulo de base de datos pero sin éxito.
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return this.validateRequest(request);
}
validateRequest(execContext: ExecutionContext): boolean | Promise<boolean> | Observable<boolean>{
const request = execContext.switchToHttp().getRequest();
const user = request.user;
return true;
}
}
¿Es el enfoque correcto extender la clase UserService con un método findOne e inyectarlo en el servicio?
@Injectable()
export class UserService{
constructor(@Inject('USER_MODEL') private readonly userModel: Model<User>){}
async create(createUserDto: CreateUserDto): Promise<User>{
const createdUser = new this.userModel(createUserDto);
return await createdUser.save();
}
async update(updateUserDto: UpdateUserDto): Promise<User>{
const updateUser = new this.userModel(updateUserDto);
return await this.userModel.updateOne({loginname: updateUser.loginname}, this.getUpdateQueryFromUserModel(updateUser));
}
async delete(deleteUserDto: DeleteUserDto): Promise<User>{
const deleteUser = new this.userModel(deleteUserDto);
return await this.userModel.deleteAll({loginname:deleteUser.loginname}).exec();
}
async findAll(): Promise<User[]> {
return await this.userModel.find().exec();
}
getUpdateQueryFromUserModel(userModel: Model<User>): Object {
let query = {};
if(userModel.dispname !== undefined && userModel.dispname !== null){
query["dispname"] = userModel.dispname;
}
if(userModel.pw !== undefined && userModel.pw !== null){
query["pw"] = hash.sha256().update(userModel.pw).digest('hex');
}
return query;
}
}
¿Puedes decirme por favor cómo se ve el enfoque correcto? ¡Gracias de antemano!
Respuesta #1
Encontré una solución para el acceso a la base de datos, puedes inyectarla directamente (lo intenté antes pero falló debido a otro problema).
@Injectable()
export class AuthGuard implements CanActivate {
constructor(@Inject('UserService')private readonly userService:UserService){}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return this.validateRequest(request);
}
async validateRequest(execContext: ExecutionContext): Promise<boolean>{
const request = execContext.switchToHttp().getRequest();
const user = request.user;
console.log(await this.userService.findAll());
return new Promise<boolean>((resolve, reject) => {
return true;
});
}
}
Mi código ahora se ve así con el servicio inyectado en el constructor, pero todavía no funciona completamente, porque cuando intento ejecutar la línea execContext.switchToHttp().getRequest(), obtengo un error switchToHttp no es una función, ¿alguien puede ayudar?
Saludos Pascal
Respuesta #2
esta es mi implementación y funciona bien
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { IS_PUBLIC_KEY } from 'src/decorators'
import { IResourcePermission, RESOURCE_PERMISSION_KEY } from 'src/decorators/resource-permission.decorator'
import { RolesPermissionsLogicService } from 'src/modules/roles-permissions/services/roles-permissions-logic.service'
@Injectable()
export class AuthorizeGuard implements CanActivate {
constructor(
private readonly rolesPermissionsLogicService: RolesPermissionsLogicService,
private reflector: Reflector,
) {}
canActivate(context: ExecutionContext): Promise<boolean> {
return this.validateRequest(context)
}
async validateRequest(ctx: ExecutionContext): Promise<boolean> {
const isRoutePublic = this.reflector.get<string>(IS_PUBLIC_KEY, ctx.getHandler())
const resourcePermission = this.reflector.get<IResourcePermission>(RESOURCE_PERMISSION_KEY, ctx.getHandler())
if (isRoutePublic || !resourcePermission) {
return true
}
const { user } = ctx.switchToHttp().getRequest()
const rolePermissions = await this.rolesPermissionsLogicService.getOneRole({
name: user.userRole,
permissions: {
$elemMatch: {
name: resourcePermission.resource,
[`actions.${resourcePermission.scope}`]: true,
},
},
})
if (!rolePermissions) {
throw new UnauthorizedException('Not authorized to access this resource')
}
return true
}
}
