Hi. I'm working through different
# spicedb
t
Hi. I'm working through different examples of SpiceDB, conceptually, and ran into some roadblocks. I wonder if the folk here can help. So here's an example: Let's say you're making permissions for a starship, and the starship has starship_systems. Also, the universe has users but some of these users are assigned roles (relationships?) on the starship. Now here's the part where I'm confused --> I want anyone assigned to a certain role on the starship to have access to certain starship_systems, so... I think I want to create a relationship between the starship_system and the starship relation? Or do I have to define each starship_system manually so I can refer to a starship relationship directly? That seems not great for maintainability as the systems change over time.
v
you want to create a relationships from the system to the starship, so that in the system's permissions you can reference relations on the starship
Copy code
definition starship {
  relation commander: user
  
  permission view = commander
}

definition system {
  relation starship: starship

  permission view = starship->view 
}
t
Ah, okay, not quite because there are many systems and many roles on the ship. So let’s say you have a science officer, security officer, tactical, etc. And each of them have different levels of access to different systems. Do we have to manually define each system in the schema, basically hardcoding them? Or is there a way to define the roles’ access via a relationship?
y
it's basically modeling google/amazon IAM within spicedb
your "systems" would be the entities (database, whatever)
and the roles are the roles
so it'd be "engineering officer" instead of "database admin"
l
I dont see a way around dynamically generating the schema in non-trivial cases since you have a combination of systems and roles (assuming we do indeed want fine grained permissions)
y
@Lee Trout have you seen the blog post i linked above>
it lets you separate the "what" from the "how" in a pretty clean way
a "role" is basically a group of relations, and you can apply them in any combination to any combination of entities through role bindings, and then you can define the permissions on those entities in whatever way you want
l
My point is that, in the google iam pattern, a non trivial set of roles + "things" has the potential to be quite large and unlikely to be manually managed. In the schema definition itself.
v
what yetitwo said ☝️ you want to look into the custom roles pattern
y
i'm not sure i understand... you mean that the schema itself definition gets large and unwieldy?
v
I don't see a way around modeling "things" unless you abstract them in a way, but the point of "things" is that they will be conceptually different busineess domain concepts
l
I dont see a big difference between custom roles pattern and the google iam pattern
v
the google iam pattern is the custom roles pattern
there is no such thing as "google iam pattern", it was just an example on how to do custom roles
l
Yes, it gets very large and from another recent thread it is not obvious how well migrations are going to work 😁
v
I think im missing something, was the initial question how to implement that starship / system domain or how to implement concise schemas?
y
Pilot hasn't chimed in for a sec
t
I'm reading the article a few times and thinking. : )
v
ok so it's two different convos happening in the same thread got it
@Lee Trout can you perhaps formulate the question in a different thread, I think I know what you mean and would be interested in hearing more about your concerns
t
Okay. So lets say a ship would have a relation that is a role_system_binding, which lets us link together systems for that particular ship and roles on that particular ship. Then there is another relation that is a role_user_binding that links together roles to users on that particular ship. Then you can create a relationship from a system to a role, and another relationship from a role to a user, on that particular ship? And then you have a permission called "allowed" that somehow wants a role and a system for this ship for a user.
y
if i understand you correctly, yeah, that's more or less it
the
role_binding
is the thing that connects the user ("who") to the entity ("what") and the role ("how")
and a
role_binding
can technically be connected to any number of roles, users, and entities
like if every engineer in a given ship should have the same level of access to all of the ship consoles (?)
t
Ah, I hadn't considered a three-way binding. Hm.
No, two separate relationships is right (system to role, role to user) because as you said, having every engineer have access to certain systems would be the goal. Nice. Thanks!
Just to summarize: There is a ship definition. The ship has systems. There is a role_system_binding relation on the ship that ties them together. Then the ship has another relation that is a role_user_binding on the ship. What would the permission look like to merge the two bindings together to determine if a user can access a system on the ship?
y
there aren't two different kinds of role bindings
it's just one kind of role binding
your schema is going to look very similar to the one in the blog post
t
Are you assuming that the systems on each ship will be the same?
That is, I think that's what makes this problem unique from the article.
I also think that's why there are so many permissions in the article. If we can solve this, we might only need one.
y
no, you can also have different systems be treated differently
you can also have a separate role binding for every user + system + role combination
t
So each ship has a relation which is a binding that is a triple of (user, role, system)? I don’t think that works, since then you’re tying a user directly to a system, and the role becomes unnecessary, right? If you’re saying a ship has a system —> role —> user, but then lock them in a (system, role, user), then you might as well just say system —> user with a (system, user) tuple. So assuming that we want systems access associated with roles on the ship, and then we want users with roles on the ship, and we have different systems and different users on each ship… can you think of a way to bind them together like you were saying?
y
i think that comes down to how you define your entities
as i understand your question, this schema is 100% capable of it
but it'd help to have a concrete example to talk through
t
That makes sense. I think it’s possible too. Let me go away for a day or two and play with schemas and come back with something concrete. Thanks for your help so far! It’s good to know I’m on the right path. 🌟
I think what I wanted is this:
Copy code
schema: >-
  definition user {}

  definition starship {
    relation crew_member: user
  }

  definition starship_role {
    relation starship: starship
    relation user: user
  }

  definition starship_system {
    relation starship: starship
    relation role: starship_role#user

    permission operate = starship->crew_member & role
  }

relationships: >-
  starship_role:captain#starship@starship:enterprise

  starship_role:captain#user@user:picard

  starship_role:starfleet#user@user:picard

  starship_role:captain#user@user:kirk

  starship_role:starfleet#user@user:kirk

  starship_role:starfleet#user@user:wesley

  starship:enterprise#crew_member@user:picard
  
  starship:enterprise#crew_member@user:wesley

  starship_system:enterprise_bridge#starship@starship:enterprise

  starship_system:enterprise_bridge#role@starship_role:captain#user

  starship_system:sickbay#starship@starship:enterprise

  starship_system:sickbay#role@starship_role:starfleet#user

assertions:
  assertTrue:
    - "starship_role:captain#user@user:picard"
    - "starship_system:enterprise_bridge#operate@user:picard"
    - "starship_system:sickbay#operate@user:picard"
    - "starship:enterprise#crew_member@user:wesley"
    - "starship_system:sickbay#operate@user:wesley"

  assertFalse:
    - "starship_system:enterprise_bridge#operate@user:kirk"
    - "starship_system:sickbay#operate@user:kirk"
    - "starship_system:enterprise_bridge#operate@user:wesley"
That is, both Kirk and Picard are captains. Wesley and Picard are crew members on the ship currently. Picard and Wesley are allowed in the sickbay. Wesley is NOT allowed on the bridge. Following this pattern, we can define different systems on different ships, and the systems determine which roles are allowed to operate them while also only allowing those that are associated with their particular ship. (edited a few times to make the example simpler.)
Is this... right? Is this recommended?
Because if so, this is much easier than the article example. You don't need 20+ permissions -- you need 20+ relations, and these relations can be dynamic and change over time or be different per customer/tenant/project/system.
I think.
a
What's the function of the starship relation on the role? Effectively it looks like your role is essentially just a user group. The starship relation on starship role isn't used and could be different than the starship crew member relations, possibly causing some confusion.
Do you intend on having many different permissions on starship system or just "operate"?
t
Ah, I simplified too much. Imagine many starships from all the different Star Trek races. They all have bridges, generally, but each bridge may have different roles based on their own hierarchies. Or think of it like a company. Each company may have an S3 or a document or a payroll system. Some might have different hierarchies, but our software has a limited set of operations, and we want to know who can do what to what we're providing them.
In that case, we've given them systems with specific operations, but the roles and the confines of each role on our systems and how we've divided our systems are unique per client/company.
Additionally (for example), we could do the same relationship based roles on the starships as well as the system, and require both for an operation to be allowed.
The reason I'm here and asking about this... is that it's very different from what I see on the site 😄 and this code seems very small compared to some of the examples. Is this wrong somehow? Will the performance be right?
You're right about the
relation starship: starship
in the
starship_role
. It's unnecessary. 🤔
y
i wouldn't worry about performance - in general a simpler schema will perform better, so writing the simplest schema that can capture all of the features of your auth system is good
the reason you'd use something like the custom roles pattern is if you want the roles to be definable in data rather than in schema
if you want to be able to change what a captain or engineer can do without making code changes to your schema, you need something like the custom role pattern
if you're fine with making code changes in order to make those changes, what you have is fine
t
Hm. What do you mean? I thought I was getting away from having to make changes to the schema since I was defining captains and such in the relationships. You've thought about this more and have a lot more experience with these kinds of systems.
y
you are defining what a captain can do in the schema - that's what i mean
so your schema will be able to say "a captain can use the intercom and sit in the bridge chair"
but what happens when the comms person has had enough of the captain reading poetry on the intercom and wants to revoke that particular privilege?
under your schema, the comms person would need to modify the schema in the code and make a deploy
under the custom role pattern, the comms person would be able to make a
WriteRelationships
request that removes the
use_intercom
permission from the captain's role, which can happen at runtime, without needing to modify code
the custom role pattern is also a little confusing to talk about, because its "permissions" are defined in terms of SpiceDB relations, which is a different concept than a SpiceDB permission
t
I think that if we want to remove the ability of the captain from operating the bridge, they’d remove the relationship between the bridge and captains. Alternatively, they’d have to remove the captain from being a captain by removing that relationship, or they’d have to remove them from the crew. To make it clearer, I should probably rename the “role” relation on the system to “operator_role”?
You could rewrite the custom role example using systems instead, perhaps even a system per permission, and it would move a lot of the words from your schemas into relationships. It’s making your example more dynamic.
y
yes, but then your roles aren't particularly reusable - you're more granting a specific set of relations on each individual user
each captain is going to need
comms_operator
and
bridge_operator
defined separately
to be clear: there's nothing wrong with that approach
and it's one of the simplest and best use cases for SpiceDB and ReBAC generally
t
? I'm confused. What do you mean?
Copy code
starship_role:captain#user@user:picard

starship_system:enterprise_bridge#role@starship_role:captain#user
y
where
starship_role
is your name for the
role_binding
definition in the custom role schema?
t
It seems like you're saying that I would need to manually assign a person to a system. But I'm assigning a role to a system, and then assigning multiple persons to the role (Kirk and Picard). It's one-directional. The role-binding schema was doing something different, where it was creating two sets of users, where one was a
user:*
, which is then used in a kind of any/never set math to do logic, and then intersected that with the actual list of users who should have access.
The key difference to what I'm doing is using the
#
operator in a relationship, which is weird and I didn't see examples of it in the blogs, only references in psuedocode, but it works.
y
hmmm... yeah i haven't wrapped my head around subject relations (which is what the
#
is called iirc)
if it works it works ^.^
a
There's nothing wrong with subject relations. It's a very typical way to do user groups, which are what your roles effectively are. For each verb you probably want your corresponding relation to just be the noun of that verb. E.g. operate = starship->member & operator Absolutely nothing wrong with this. What might be confusing is just that what you're calling a role is usually just called a group in other patterns.
t
> What might be confusing is just that what you're calling a role is usually just called a group in other patterns. You're right. I was thinking about this too. Could also call it an "attribute" or "attribute group" or "tag", which I'm then using to grant permission for an operation.
Like someone needing to be a "doctor" or "pharmacist" for some permission, and therefore having a "doctor" or "pharmacist" relation and using that as the thing in the permission.
You can then query "all the doctors" and "all the pharmacists", or say things like "must be a doctor to do this" or "must be a pharmacist to do this".
But the main benefit is that you can add new attributes/tags/categories/roles to permissions without editing the schema by just adding a new relation.
Or, I suppose, deleting relations to remove permission too. Like if a particular company is no longer a customer, or a particular role no longer should have access based on payment or a restructuring on their side.