hi :wave:. how do one-to-many relationships work w...
# help
b
hi 👋. how do one-to-many relationships work with the Prisma adapter? I'm getting
Unsupported operator exists
when I try to pass in a relation to
fieldNameMapper
Copy code
queryPlanToPrisma({
    queryPlan,
    fieldNameMapper: {
      'request.resource.attr.workflowUserRoles': 'workflowUserRoles'
    }
  });

// yaml
- expr: >
    R.attr.workflowUserRoles.exists(workflowUserRole,
      workflowUserRole.userId == P.id && workflowUserRole.role == "OWNER"
    )

// schema.prisma
model Workflow {
  workflowUserRoles WorkflowUserRole[]
}
looks like because
exists
isn't a supported operator here, it's throwing. should the queryPlan I get from
cerbos.planResources
be mapping my
.exists
call to one of these operators?
Copy code
const OPERATORS = {
    eq: {
        relationalCondition: "is",
        fieldCondition: "equals",
    },
    ne: {
        relationalCondition: "isNot",
        fieldCondition: "not",
    },
    in: {
        relationalCondition: "some",
        fieldCondition: "in",
    },
    lt: {
        fieldCondition: "lt",
    },
    gt: {
        fieldCondition: "gt",
    },
    le: {
        fieldCondition: "lte",
    },
    ge: {
        fieldCondition: "gte",
    },
};
s
Hi! I'm not super familiar with the Prisma adapter, but it looks like it doesn't yet cover the query plan output generated by
exists
clauses. I'll raise an issue for this. For the
relationship
component, from reading the README, it looks like you might need to specify the model relationship via the
relationMapper
third arg to
queryPlanToPrisma
, e.g:
Copy code
const result = queryPlanToPrisma({
  queryPlan,
  fieldNameMapper: {
    "request.resource.attr.aFieldName": "prismaModelFieldName"
  },
  relationMapper: {
    "request.resource.attr.aRelatedModel": {
      "relation": "aRelatedModel",
      "field": "id" // the column it is joined on
    }
  }
});
I think, given my assumption that
R.attr.workflowUserRoles
is an array of objects -- modelling your original
exists
based expression in a a supported, equivalent way might not be feasible 🤔. One basic workaround might be to represent each object attribute as individual arrays, so you can do something like:
Copy code
expr: P.id in R.attr.ownedByIds
Then checking each returned
ownedBy
object for the appropriate roles.
I'm intrigued about your particular case, though. Rather than approaching this with
one -> many
, could you not reverse it and instead check the principal for access to the resource? E.g:
Copy code
expr: R.attr.id in P.attr.workflowIds
b
hi @Sam Lock (Cerbos)! thanks for getting back to me. yes, since
workflowUserRoles
is an attribute of both the resource and the principal, checking the attributes on the principal would work in this case. however, in the case where something is only an attribute of the resource, this solution would not work. for example, let's say a Workflow has a bunch of Tags and I want to check the Tags of a Workflow using
queryPlanToPrisma
.
One basic workaround might be to represent each object attribute as individual arrays, so you can do something like:
how would this work using
queryPlanToPrisma
? I'm going to try the
relationMapper
. thinking about it from a SQL perspective, it should be accomplishable with a join.
sorry for the ping! I forgot that you guys are based in Europe. I'm in California. please don't feel obligated to respond until tomorrow!
😆 1
🤷‍♂️ 1
s
how would this work using
queryPlanToPrisma
?
I don't think you can do this entirely with the query planner, given the currently supported adapter operators 🤔. My thinking was that you could retrieve a superset of the required DB rows using a partial condition in the policy, and then post-process in the application layer to reduce it down further. However, I think this is a bit useless given you'd need a "query planner only" rule in your policy, plus the need to further reduce your result set outside of the DB. I think, given the requirement you describe above, you probably do require the
exists
operator.
It might be that you'd need to apply this particular bit of filtering manually for now. Would that be feasible?
b
aw, that's unfortunate. I do love relying entirely on Cerbos for authorization logic. I'll look into apllying the logic manually for now. are you thinking we'll be able to get
exists
support into the adapter soon?
s
aw, that's unfortunate. I do love relying entirely on Cerbos for authorization logic
Totally understand.
are you thinking we'll be able to get
exists
support into the adapter soon?
I don't think we can really commit to specific timings because we're doubled down on some other priorities at the minute, but this is clearly a useful feature and it's now very much on our radar. I know it's cliché, but if you did happen to have a crack at it yourself, we always welcome community contributions!
b
I'll try to take a look if I get a chance. doubt I'll ever get a chance 😂 😭