marcusgrass
01/12/2023, 9:33 AMdefinition user {}
definition organization {
relation member: user
relation admin: user
permission read_any_module = member + admin
permission edit_any_module = admin
}
definition module {
relation creator: user
relation owner: organization
relation sudo: user:* with escape_hatch
permission read = sudo + creator + owner->read_any_module
permission edit = sudo + creator + owner->edit_any_module
}
/** disregard using a string thats actually a bool when bool is a datatype, it's just messing around, ideally we'd be able to have a no-arg caveat here, like escape_hatch() but that doesn't parse right **/
caveat escape_hatch(open string) {
open == "true"
}
What we'd like to achieve is that any user can access any resource if the context is provided, but since we can't wildcard resources we have to create a relationship with every module that specifically maps a wildcard user with escape_hatch to sudo. We don't have to do data-duplication so it's workable, but it isn't very economical in terms of data-storage. Is there a better way to achieve a similar setup?vroldanbet
01/12/2023, 12:42 PM`
definition user {}
definition platform {
relation sudo: user with escape_hatch
}
definition organization {
relation sudo: platform#sudo
relation member: user
relation admin: user
permission read_any_module = member + admin
permission edit_any_module = admin
}
definition module {
relation platform: organization#platform
relation creator: user
relation owner: organization
permission read = owner->sudo + creator + owner->read_any_module
permission edit = owner->sudo + creator + owner->edit_any_module
}
caveat escape_hatch(open string) {
open == "true"
}
marcusgrass
01/12/2023, 2:06 PMmodule:* edit user:* with escape_hatch
only once. And what I think that expresses is, for any module, any user with the caveat escape_hatch
is allowed to edit. Instead of what I tried module:specific-module edit user:* with escape_hatch
which needs to be repeated for all modules. Ideally I wouldn't even need to persist that relationship at all, all the information necessary to make the decision is in the schema, it isn't even strictly a relation
it's a special case of permission
. Can you use caveats in permissions? I realize now that that is actually what I'm getting at here, being able to specify permission edit = creator + owner->edit_any_module + user:* with escape_hatch
and not saving any relationship there at all.
A practical case here is ownership, Ideally we wouldn't have that data, as a saved relation, but provide it at check-time, since the authorization-service doesn't store ownership data, that's persisted somewhere else.
So the service that knows about ownership,does user:<user> have permission:<edit> on module:<this-module> given that user:<user> is the creator
? And our caveat handles the case that a user that is the creator has a certain set of permissions, regardless of other relationship detailsvroldanbet
01/12/2023, 2:23 PMplatform
object and instead would use user:*
. That's one relation at the platform level, plus one relation per module to link it to the platform. The module
already has an owner
. At first glance that'd seem like the minimum amount of relationships you would need to achieve this, but maybe my colleagues have other ideas cc @Jake who likes to do these modelling exercises
But being quite frank, you are basically opening a hole in the system: the client becomes in entire control of the result of the operation as long as they provide the right context. You may as well simply completely bypass SpiceDB in the client side at that point.marcusgrass
01/12/2023, 2:46 PMfn edit_endpoint(user, module) {
let module_metadata = module_db.get_metadata(module);
let has_permission = authorization_service.has_permission(user, "edit", module, {"is_creator": module_metadata.creator == user});
if has_permission {
module_db.upsert(module);
} else {
return Http.403;
}
}
It doesn't really matter if we can bypass authorization by providing context, since the module service could just not care about the response if it wishes, or just not ask, the authorization_service is a tool that the module service can use how it wishes, the burden of security falls on that implementation.vroldanbet
01/12/2023, 3:11 PMmarcusgrass
01/12/2023, 4:04 PMfn authorize_module(user, action, module, ctx) {
spicedb.ensure_relationship(user, module, ctx.is_creator);
spicedb.check_permission(...)
}
This will obviously affect query times but for our case, query times are a smaller issue than long-term data integrity in most cases.vroldanbet
01/13/2023, 9:37 AMTOUCH
in case that could help with duplication