Protecting a List Endpoint
# spicedb
a
Hey all! Working on the m2m solution here. Already had a few advices previously + read this doc https://authzed.com/docs/spicedb/modeling/protecting-a-list-endpoint Q1: I do like Materialize idea, but since we are using Open Source there is no support for that yet(btw, any plans for making it public as well?) The task is basically to be able to get all collaborators for all documents. To be more specific: We have documents, each document might have collaborators with different roles (e.g. viewer, editor). I want to have a json as a response in my Postman, kinda like this
Copy code
{
"document_1": {
 "editors": [{explicit_user_1: ..}, {explicit_user_2: ...}, {}],
 "viewers": [{explicit_user_3: ...}, {implicit_user_1: ...}]
 }
}
one of the suggestions I had previously in this chat is to create a new table in the Spanner and just duplicate what I'm sending to SpiceDB. So when I need m2m list - I go do to that table and do JOIN, and when I need to answer a question "if user A is a collaborator on doc C" I can ask SpiceDB Q2: Is anybody doing that? and last but not least (tricky one) Q3: what if some of collaborators have access to the doc not explicitly, but by being admins of folders, groups, organization, etc? Materialize would return that data too, but in my case I can't pre-compute all of that by myself and store it in the prod DB. What can you suggest please in this case? (or LookupResources and CheckBulkPermissions are the only options left? <- will slow down the response time)
v
Hey 👋 Q1. No plans to opensource Materialize right now Q2. I think the answer depends on whether you are using "computed permissions" or not. I'd suggesting reading [this](https://authzed.com/docs/spicedb/modeling/access-control-management). There is a difference between querying user grants (e.g. user1 has "editor") versus querying computed permissions (user1 has edit permission over doc1). The former is something that does not require computing permissions can can be answered with APIs like
ReadRelationships
. The second one requires you to compute permissions, so there is no no way to keep computed permissions in your Spanner table unless you essentially build your own Materialize. Q3. This touches on what I mentioned on Q2: it's not the same building an API that returns access grants (e.g. a user has a role assigned) versus an API that returns the side-effects of a grant (user1 can perform "edit" because they have been granted role "editor", or because they are org admin). Your schema should make explicit distinction between the roles granted (usually modeled with
relation
, and the actions they can perform, usually modeled with a computed permission using
permission
). So assuming you have permissions like
edit
,
view
and the like, and you assuming you the API example you described wants to show all users organized by permission, then you'd need to do multiple
LookupSubjects
calls, one per each permission your want to return as part of the API response. This is likely not going to scale as you'll have to do N times
LookupSubjects
. Even if it did, that API response needs some sort of pagination, as the list of users can get pretty large.
LookupSubjects
does not currently support pagination, but we have some ideas on how to do it.
it also sounds like you want to do that for all resources, so that'd be N resources x N permissions x LookupSubjects. That's N^2 LookupSubjects call: it's very likely not going to scale.
And now, with all of that said, I'd like to ask: what are you trying to achieve? You've described the API, but not exactly the product feature you are intending to build with this.
So either you are building: - an Access Control Management API, for which you shouldn't need computed permissions, and can be solved scalably using
ReadRelationships
(see [the docs](https://authzed.com/docs/spicedb/modeling/access-control-management).)) - an Audit API (i.e. return all the effective permissions for all resources and all permissions) for which I don't know of a way to make it scale without denormalizing everything (like Materialize does)
a
@vroldanbet hey! This is pretty much a good summary for all the questions I've asked. I'll definitely read all the links you've attached Answering your question about what product feature I'm building: we have a document. Somebody with "editors" permissions (or even a creator, doesn't matter here) decided to grant "view" permission to the admin of the organization. That admin by default has edit access. Now I want to still show my admin in the list of collaborators, but with "editor" (not "view" permissions) and saying "sorry, this guy already has something better and you can't change it). 2 options I see there: 1. As you mentioned: separation by schema. I'm returning "explicit" collaborators (can be stored in the Spanner) and "implicit" (dynamic). And then display only explicit ones, but when email from implicit list is typed - show the message "sorry" 2. I can fetch only "explicit" collaborators and return it to the client, but when some email is typed in - I make and API call to the server to make sure this guy doesn't have any other implicit roles
v
Yeah, so all of that sounds to me like an [access control management UI](https://authzed.com/docs/spicedb/modeling/access-control-management), and I think you'd want to rely on
ReadRelationships
as much as possible, and I'd probably tackle it as you described in 2). Let's assume a design as such:
Copy code
definition org {
  relation owner: user

  permission admin = owner
}

definition document {
  relation org: org
  relation viewer: user
  relation editor: user

  permission view: viewer + edit
  permission edit: editor + org->admin
}

definition user {}
If you wanted to build a UI to manage permission of an individual document (let's say
document1
), you could do the following (I'm going to use
zed
as a way to issue these calls, but you'd do the equivalent via the API call): - read all collaborators to document1:
zed relationship read document:document1
. This would return all relationsihps for
org
,
viewer
and
editor
relations - you'd render for each user assigned their corresponding role, e.g.
Andrii -> editor, vroldanbet->viewer
- you can do several things to handle the owner scenario: - you canallow the UI user to add a new user, for which you'd check if they are already an owner (which you could check with
zed relationship read org:my_org owner user:candidate_user
, and if that's the case, it will rendered with the highest possible permission. - alternatively, you could to show the users that have implicit permissions in the list already and not allowing anyone to grant those owner users permission - another thing you can do is use
WriteRelationship
preconditions. For example, you could allow granting a role to a user, but only if the are not an owner in the org. This way you don't need to think about computed permissions if you stick to the idea of "access grants". As a side note: I'd recommend explicitly differentiating the concept of grant (e.g. role a user is assigned) from permissions (actions a user can perform on a doc).
7 Views