Hi I am working on a unix-style permissions system where every row in the database has a unique id so that a resource_user_permissions table like [resource_id][user_id][permissions]
can be implemented to provide user-level access to resources. Each resource is analogous to an inode from unix.
I’m running into a situation where a resource may contain other resources that are public. I can’t reveal the other resource’s id, because any user can go to a /resources/{resource_id}
endpoint to look that resource up by id. I think I am hitting a problem similar to wanting read permissions but not execute (list contents) permissions on a unix directory. I need something even more granular to limit listing a container’s specific relationships.
So for example imagine a user’s directory (1 my_user
) that contains references to other resources on the site owned by other users. If I want to make it so that a different user (5 some_user
) can’t see a resource in the first directory, I can’t assign a no_access
permission to resource 5
because I’m not that resource’s owner. I need to somehow assign no_access
to the directory-resource relationship. It appears I have two options:
OPTION 1) list resources as relationships so that multiple ids correspond to one resource and permissions can be applied to the relationship id:
Structure:
2 my_directory
37 my_resource (a relationship resource that points to 3 my_resource)
42 someone_elses_resource (a relationship resource that points to 4 someone_elses_resource)
JSON:
{
"data": {
"type": "directories",
"id": 2,
"relationships": {
"resources": {
"data": [
{
"type": "resources",
"id": 37
},
{
"type": "resources",
"id": 42
}
]
}
}
}
}
So a request to add 5 some_user
to a blocks collection to prevent it from reading 4 someone_elses_resource
might look like:
POST /directories/2/resources/42/blocks
{
"data": {
"type": "users",
"id": 5
}
}
This would add a row like [42][5][no_access]
to the permissions table.
The downside is that all ids are now auto-generated and unique per path so none of the usual POST/PUT/DELETE calls to endpoints would work (because the client only knows resource ids, not path-dependent relationship ids). Requests would require two trips to fetch a relationship id for a resource and then submit that relationship id.
OPTION 2) list resources directly and set permissions on relationships:
Structure:
2 my_directory
3 my_resource
4 someone_elses_resource
JSON:
{
"data": {
"type": "directories",
"id": 2,
"relationships": {
"resources": {
"data": [
{
"type": "resources",
"id": 3
},
{
"type": "resources",
"id": 4
}
]
}
}
}
}
So a request to add 5 some_user
to a blocks collection to prevent it from reading 4 someone_elses_resource
might look like:
POST /directories/2/relationships/resources/4/blocks
{
data:
{
type: "users",
id: "5"
}
}
This would add a row like [42][5][no_access]
to the permissions table by looking up relationship id 42
for 4 someone_elses_resource
behind the scenes.
I’m leaning towards option 2 but I’m having a hard time understanding how relationships work from the documentation. I understand how a one-to-one relationship is specified, but are we allowed to append additional path elements beyond the /relationships/{relationship}/
portion of the URL? For example blocking 5 some_user
from reading 1 my_user
's profile might look like:
POST /users/1/relationships/profile/blocks
{
data:
{
type: "users",
id: "5"
}
}
But I am not clear if it’s ok to use an endpoint like /directories/2/relationships/resources/4/blocks
where I specify the related resource’s id in the URL.
I think the gist of the problem is that I would like to set permissions on a relationship like it’s a first-class resource, but I don’t want to reveal the relationship id to the client (due to the added complexity of doing so).
I have found some examples on the web where REST APIs have attribute-level permissions. I’m concerned though that this could be a can of worms and nonstandard. If it’s possible to treat everything as an inode and stick with unix-style permissions, I’d prefer to do that.
I realize this touches on both permissions and relationships which can be nebulous, and that my examples and terminology might not be that great. I feel like I’m not seeing something fundamental here. Any insights you can provide about permissions with respect to REST API design would be greatly appreciated, thanks!