nestjsx / crud

NestJs CRUD for RESTful APIs
https://github.com/nestjsx/crud/wiki
MIT License
4.09k stars 540 forks source link

Check Ownerchip of the resource[Solved] #418

Open loicgeek opened 4 years ago

loicgeek commented 4 years ago

Hello everyone, nestjsx crud is really helpful. thanks for a great job. I have opened this issue just to show what I have done. So when working with nesjtsx crud , i wanted a way to decorate my methods, so that will check whether the current user is the owner of the resource or not. Here is what I have done :

import {
  CallHandler,
  ExecutionContext,
  NestInterceptor,
  Inject,
  mixin,
  UnauthorizedException,
  NotImplementedException,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { TypeOrmCrudService } from '@nestjsx/crud-typeorm';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

export function mixinCheckOwnerchip<T extends new (...args: any[]) => any>(
  entityClass: T,
  relationName: string = 'user',
) {
  class CheckOwnerchipInterceptor extends TypeOrmCrudService<T>
    implements NestInterceptor {
    constructor(
      @Inject(getRepositoryToken(entityClass))
      protected readonly repo: Repository<T>,
    ) {
      super(repo);
    }
    async intercept(
      context: ExecutionContext,
      next: CallHandler,
    ): Promise<Observable<any>> {
      const req = context.switchToHttp().getRequest();
      const item: any = await this.findOne(req.params.id, {
        relations: [relationName],
      });
      if (!req.user) {
        throw new NotImplementedException(
          'You Must Implement the Auth guard first',
        );
      }
      if (item && item[relationName] && item[relationName].id !== req.user.id) {
        throw new UnauthorizedException(
          'You are not the owner of this resource',
        );
      }
      return next.handle();
    }
  }
  return mixin(CheckOwnerchipInterceptor);
}

Usage

    @Override()
  @UseGuards(AuthGuard('jwt'))
  @UseInterceptors(mixinCheckOwnerchip(Entityclass,relationName)) // Here
  @ApiBearerAuth()
  async updateOne(
    @ParsedRequest() req: CrudRequest,
    @ParsedBody() dto: Entityclass,
  ) {
    return this.base.createOneBase(req, dto);
  }

Entityclass : It's the current entity we are working on. relationName : it's the property name you have used to make the relation between the entity class and the owner. the default value is "user"

Hope you will integrate it in the library, that is my little contribution to this big work. thanks!

philippTheCat commented 4 years ago

Hey, many thanks for this interceptor.

if all you need to add is the Interceptor you can do this through the @Crud decorator as well, and dont need to overwrite the methods:

@Crud({
  model: {
    type: Song
  },
  routes: {
    createOneBase: {
      interceptors: [mixinCheckOwnerchip(Song, "owner")]
    }
)