next-crud icon indicating copy to clipboard operation
next-crud copied to clipboard

onRequest callback unable to mutate request at all

Open jacobclarke92 opened this issue 2 years ago • 2 comments

Heya, first of all, thanks for all your hard work on this lib, I've been following it for ages and it just keeps getting better.

Minor issue -- because the handler function destructures req before the onRequest callback fires, any meaningful changes the onRequest callback makes to req are ignored.

Docs state intended use case for this callback could be: "You can use it to add some custom content to your query params".
So I presume this may be an oversight?

Also, I found it odd that the handler looks at req.url instead of potentially looking at req.query, any particular reason for that?

Happy to do a pull request later just wanted to drop a line first!

Cheers!

jacobclarke92 avatar Oct 09 '22 06:10 jacobclarke92

Totally agreed on the last comment: the library needs some way to mutate incoming request in a middleware or callback, to enforce access-control (adding a parameter or a body-field).

As a workaround for now, you can do:

in your src/pages/api/[...nextcrud].js:

const handler = async (req:NextApiRequest, res) => {

  const nextCrudHandler = await NextCrud({
...
})

if(req.method == 'GET'){
// manipulate req.url
const newurl = req.url + `where={user_id=${current_user.id}`
    req.url = newurl;
  } else if(req.method == 'POST'){
    req.body.user_id = user.id;
  }

  return nextCrudHandler(req, res);
};

eliasbaixas avatar Oct 14 '22 15:10 eliasbaixas

A better alternative (until solved) is to create an alternative "prisma adapter" that enforces access-control from the input parameters, because the current getOne, delete, etc won't enforce access control for you, and patching req.query and req.body is such a burden.

eliasbaixas avatar Oct 14 '22 15:10 eliasbaixas

This is working for me, but it's such a dirty patch:

class AccessControlPrismaAdapter extends PrismaAdapter< userprofile | Space, Prisma.ModelName > {
  prismaDelegate: any;
  pk: any;

  constructor({ modelName, prismaClient, primaryKey = "id", options, manyRelations = [], }: any) {
    super({ primaryKey, prismaClient, manyRelations });
    const pc = prismaClient;
    this.prismaDelegate = prismaClient[modelName];
    this.pk = primaryKey;
  }

  async getOne( resourceName, resourceId, query?: IPrismaParsedQueryParams,): Promise<userprofile | Space> {
    const delegate = this.getPrismaDelegate2(resourceName);
    const findFn = /*delegate.findUnique || */ delegate.findFirst;

    const resource = await findFn({
      where: { AND: [query.where, { [this.pk]: resourceId }] },
      select: query.select,
      include: query.include,
    });

    return resource;
  }

  getPrismaDelegate2( resourceName: Prisma.ModelName,): Record<PrismaAction, (...args: any[]) => Promise<userprofile | Space>> {
    // @ts-ignore
    return this.prismaClient[
      `${resourceName.charAt(0).toLowerCase()}${resourceName.slice(1)}`
    ];
  }

then in your nextcrud config:

  const nextCrudHandler = await NextCrud({
    adapter: new AccessControlPrismaAdapter({ prismaClient }),
    models: {
      [Prisma.ModelName.userprofile]: {

then you export from [nextcrud]:

  if (["GET", "DELETE", "PATCH"].includes(req.method)) {

    const where = req.query.where ? JSON.parse(req.query.where as string) : {};
    let newhere;

    if(url.pathname.match(/\/api\/usuarios/)){
      newhere = JSON.stringify({
        $and: { id: { $eq: user.id }, ...where },
      });
    }else if(url.pathname.match(/\/api\/spaces/)){
      newhere = JSON.stringify({
        $and: { creatorId: { $eq: user.id }, ...where },
      });
    }
    console.log(req.method, newhere);

    params.set("where", newhere);

    const newurl = `${url.pathname}?${params.toString()}`;
    console.log("new url:", newurl);
    req.url = newurl;
  } else if (req.method == "POST") {
    if(url.pathname.match(/\/api\/usuarios/)){
      req.body.creatorId = user.id;
    } else if(url.pathname.match(/\/api\/spaces/)){
      req.body.id = user.id;
    }
    console.log("POST:", req.body);
  }

  return nextCrudHandler(req, res);
};
export default handler;

but as I was saying... feels such a hack 😝

eliasbaixas avatar Nov 10 '22 16:11 eliasbaixas

Hello,

Sorry for the delay. It is now fixed in 2.3.2. Let me now if it works fine now

foyarash avatar May 22 '23 16:05 foyarash

Hello,

Sorry for the delay. It is now fixed in 2.3.2. Let me now if it works fine now

Hey @foyarash, no worries!
I had a look over the commit and it's practically identical to how I'd patch-packaged it in the end so I'm stoked!
Thanks for getting around to it <3

jacobclarke92 avatar May 22 '23 23:05 jacobclarke92