hi :wave:! are there any plans to restart developm...
# help
b
hi 👋! are there any plans to restart development on the Cerbos Prisma adapter?
a
Hey, The query plan adapters we have are reference implementations with most users taking them and refining based on the actual conditions they use in their policies. Whats your use case needing an update?
b
Interesting! Is there any particular reason users aren’t contributing back to the open source repo? I’d love to contribute if I can. I need support for
exists
. I thought I might be able to accomplish this with
in
, but the adapter only supports one column. Policy expression
Copy code
R.attr.userRoles.exists(userRole,
                        userRole.role == V.WORKFLOW_ROLE_OWNER && userRole.userId == P.id
                      )
Prisma I would expect
Copy code
where: {
  userRoles: {
    some: {
      role: WORKFLOW_ROLE_OWNER,
      userId: “1234”
    }
  }
}
a
We are always open to contributions! Your message prompted me to update the mapper with some of the new conditions PR is up https://github.com/cerbos/query-plan-adapters/pull/94
b
Awesome! From a quick glance (on my phone 😅 ), it looks like I’ll still need to add support for
exists
to support my use case. Does that seem correct to you? I’ll take a better look once I’m in the office. Thank you so much!
a
Yeah it doesn't handle that case currently
b
Are there any challenges you anticipate I might encounter when implementing
exists
? I’d like to do it in a way that I can contribute it back
a
The biggest thing I think is that the query plan will return a lambda expression which needs to be converted into the right structure of the prisma filter - handling that in a generic way is going to be tricky
Actually I think I can add this pretty quickly - its just a nested condition set of sorts
b
That would be amazing! My team and I would appreciate it very much. We’re working under some tight deadlines, and this would be so helpful.
a
@Brandon Choe could you share an example of your prisma model and the cerbos policy you want to apply to it
@cerbos/orm-prisma@v2.0.0-alpha.1
has been published which reworks a large portion of this mapper and simplifies providing the relationships mapper and should handle your use case. https://www.npmjs.com/package/@cerbos/orm-prisma/v/2.0.0-alpha.1
👍 1
🎉 1
❤️ 1
🙌 1
b
of course!
Copy code
// Pruned version of our Prisma schema
model User {
  id                  String    @id @default(cuid())
  workflows         Workflow[]
  workflowUserRoles WorkflowUserRole[]
}


model Workflow {
  id               String    @id @default(uuid())
  userRoles WorkflowUserRole[]
}

model WorkflowUserRole {
  workflowId String
  workflow   Workflow @relation(fields: [workflowId], references: [id], onDelete: Restrict)
  userId     String
  user       User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  role       String
}
Copy code
# Pruned version of Cerbos policy

# yaml-language-server: $schema=<https://api.cerbos.dev/latest/cerbos/policy/v1/Policy.schema.json>
apiVersion: api.cerbos.dev/v1
derivedRoles:
  name: workflow_derived_roles
  definitions:
    - name: workflow_owner
      parentRoles: ["user"]
      condition:
        match:
          all:
            of:
              - any:
                  of:
                  - expr: >
                      R.attr.userRoles.exists(userRole,
                        userRole.role == ("OWNER") && userRole.workflowId == R.id
                      )
                  # Other conditions...
              # Other conditions...
thank you!!!!!!! I'll test it out today!!!
hope this doesn't ping you given it's late over there! I believe
exists
is working as intended (at least on an immediate relation)!! thank you so much!! but now I'm struggling with the new way that nested relations are handled. in the previous version I was on (1.1.1), I was able to do the following. maybe this wasn't how we were supposed to do it, but it did work.
Copy code
return queryPlanToPrisma({
    queryPlan,
    fieldNameMapper: {
      'request.resource.attr.creator.org.settings.externalSharingEnabled':
        'creator.org.settings.externalSharingEnabled'
    },
  });
I've tried a few things with the new version (2.0.0-alpha.1), but I'm struggling a bit.
Copy code
return queryPlanToPrisma({
    queryPlan,
    mapper: {
      'request.resource.attr.creator.org.settings.externalSharingEnabled': {
        relation: {
          name: 'creator',
          type: 'one',
          fields: {
            org: {
              relation: {
                name: 'org',
                type: 'one',
                fields: {
                  settings: {
                    relation: {
                      name: 'settings',
                      type: 'one',
                      field: 'externalSharingEnabled'
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  });

// policy expression
- expr: R.attr.creator.org.settings.externalSharingEnabled == true
this is the Prisma I get
Copy code
{ creator: { is: { equals: true } } }
but what I'm expecting is a deeply nested version of that. I'm gonna try to debug myself, but just wanted to share to see if I'm missing something obvious or if there might be a bug (probably the former).
maybe I shouldn't be configuring relation based on this code? if I have
relation
, I don't get the reduce 🤔
Copy code
if ("path" in left) {
                const { path, relation } = left;
                if (relation) {
                    const relationOperator = getPrismaRelationOperator(relation);
                    if (relation.field) {
                        return {
                            [relation.name]: {
                                [relationOperator]: {
                                    [relation.field]: { [prismaOperator]: right.value },
                                },
                            },
                        };
                    }
                    return {
                        [relation.name]: {
                            [relationOperator]: { [prismaOperator]: right.value },
                        },
                    };
                }
                // Create nested filter for field path
                return path.reduceRight((acc, key, index) => index === path.length - 1
                    ? { [key]: { [prismaOperator]: right.value } }
                    : { [key]: acc }, {});
            }
I might be missing something here, but should
resolveFieldReference
be recursive?
a
Ah yes. That’s likely it. I didn’t have a test case for multiple nesting. I’ll try and give it a go over the weekend.
🙌 1
@Brandon Choe
@cerbos/orm-prisma@2.0.0-alpha.2
is up and should cover this use case
b
gonna test it out right now! thank you so much for this!
looks like it's working!!! can't thank you enough Alex!!
a
Excellent. Let me tidy up the readme and publish a 2.0.0 tomorrow
❤️ 1
🎉 1
🥳 1
b
I left a comment. feel free to ignore! just ran into something minor while working with the return type of queryPlanToPrisma https://github.com/cerbos/query-plan-adapters/pull/94/files#r1968394415
a
Good spot!
b
I can’t thank you enough Alex!! Thank you!! I chose Cerbos to be our centralized source of authorization 2 years ago, and the responsiveness of the Cerbos team has only reaffirmed my decision!
a
No problem 🙂