Crud: Filter join request to return only data of current user

Created on 26 Aug 2019  路  11Comments  路  Source: nestjsx/crud

Currently I got his in my Controller:

@Crud({
  model: {
    type: Broker
  },
  query: {
    join: {
      user: {
        eager: true,
        exclude: ['password']
      }
    }
  }
})
@Controller('broker')
@UseGuards(AuthGuard('jwt'))
export class BrokerController {
  constructor(public service: BrokerService) {}
}

There is a one to many relation between user and broker. I am authenticating the users by a jwt token and I need to filter the data on the id of the user. Is there any way to do this through the Crud configuration?

Most helpful comment

@chriszrc working on something similar that will be built in the lib

All 11 comments

Does no one use nestjsx/crud with authorization and user management or am I missing anything?

Idk, but if I understand you correctly...
This solution might feel a bit hacky, but... maybe it'll be the right fit.

@Injectable()
export class SomeInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const req = context.switchToHttp().getRequest();
    req.query.filter = 'userId||eq||' + req.user.id
    return next.handle();
  }
}
@Controller('broker')
@UseInterceptors(SomeInterceptor, CrudRequestInterceptor)
@UseGuards(AuthGuard('jwt'))
export class BrokerController {
  constructor(public service: BrokerService) {}
}

This works by adding to the query before the CrudRequestInterceptor.
Also before using this, please think about the consequences for the other routes in the controller. Maybe you'd have to do something a bit different because of that.
Alternatively, you could modify the parsed query. Then you could add the interceptor as described in the documentation.

Thanks for your answer. This will only work for GET request I assume. But I have the same problem with Post requests. When a user adds a 'broker' the id of the current user should be inserted. In the last days thinking about this problem I came to the conclusion that nestjsx/crud is only for unauthenticated access or general ressources which are not security sensitive.

You can make an interceptor do that to a POST too. I see your point though.

import {
  CallHandler, ExecutionContext, Injectable, NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthorizeCrudInterceptor<T, R> implements NestInterceptor<T, R> {
  // eslint-disable-next-line class-methods-use-this
  public intercept(context: ExecutionContext, next: CallHandler): Observable<R> {
    const req = context.switchToHttp().getRequest();

    if (req.method === 'POST') {
      // Works for createOneBase route.
      req.body.userId = req.user.id;
    } else {
      // Works for getManyBase, getOneBase, updateOneBase, replaceOneBase, deleteOneBase routes.
      req.query.filter = `userId||eq||${req.user.id}`;
    }

    return next.handle();
  }
}

The interceptor is probably the way to go, but I overrode the getMany method for my initial solution, it does the same thing:

@Override()
  getMany(@ParsedRequest() req: CrudRequest, @PrincipalUser() user:PrincipalUser1) {
    //override the supplied user_id with the user_id of the logged in user (principal)
    req.parsed.filter.find(f=>f.field === 'user_id').value = user.user_id;
    return this.base.getManyBase(req);
  }

the @PrincipalUser annotation is just sugar for extracting the req.user:

export const PrincipalUser = createParamDecorator((data, req) => {
  return req.user as User1;
});

@loginov-rocks I think the way you're setting filter, you would override any existing filters that were supplied in the request? Probably not what most people want-

@chriszrc working on something similar that will be built in the lib

@zMotivat0r Great!! I'm kind of surprised that a security related lib/framework for nestjs hasn't already been developed, I related to @MeMeMax's question about people using nestjs with authorization, I struggled a bit too in the beginning to figure out the right ways to go-

Any progress on this guys?
@zMotivat0r ?

I'd also love to hear about progress on this. I'm very surprised this is not a built in feature yet. Is the majority of users really implementing unprotected CRUD APIs? I always assumed this is a basic requirement for any API that manages users. The workarounds suggested here are not satisfactory in my opinion, and I'd hate to have to roll my own CRUD APIs.

Other than that I really like nestjsx-crud, and I'd love to use it in a real project.

Same here, I really don't like to use a workaround to do something so evident in an API. I tried also to modify the requst.options.query.filter from the overridden controller function, to do the same thing, but it doesn't work as expected, the request options object seems to be always the same, and it isn't recreated for every request.
I really like the project too, other than that, thank you for your work.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

basvdijk picture basvdijk  路  5Comments

lalalalaluk picture lalalalaluk  路  4Comments

rainercedric23 picture rainercedric23  路  3Comments

terapepo picture terapepo  路  3Comments

tbrannam picture tbrannam  路  3Comments