authorizer-tech/access-controller

SubjectSet advanced rewrites edge case

gruuya opened this issue · 2 comments

Is your feature request related to a problem? Please describe.

I've been testing a more advanced scenario for specifying SubjectSet rewrites, basically combining concentric relationships on a single namespace with hierarchical permissions between two related namespaces, and ran into a edge case. The problem is that the relation tuples of the inner/nested namespace are not being honored (or perhaps I'm not using correct syntax for rewrite config). Here's a repro:

  1. Configure namespaces (files and folders with reader and writer relations)
$ grpcurl -plaintext -d \
'{
    "config": {
        "name": "folders",
        "relations": [
            {
                "name": "..."
            },
            {
                "name": "writer"
            },
            {
            	"name": "reader",
            	"rewrite": {
                    "union": {
                        "children": [
                            { "this": {} },
                            {
                              	"computedSubjectset": { "relation": "writer" }
                            }
                        ]
                    }
                }
            }
        ]
    }
 }' localhost:50052 authorizer.accesscontroller.v1alpha1.NamespaceConfigService.WriteConfig
{

}
$ grpcurl -plaintext -d \
'{
    "config": {
        "name": "files",
        "relations": [
            {
                "name": "parent"
            },
            {
                "name": "writer",
                "rewrite": {
                    "union": {
                        "children": [
                            {
                                "tupleToSubjectset": {
                                    "tupleset": { "relation": "parent" },
                                    "computedSubjectset": { "relation": "writer" }
                                }
                            }
                        ]
                    }
                }
            },
            {
                "name": "reader",
                "rewrite": {
                    "union": {
                        "children": [
                            { "this": {} },
                            {
                              	"computedSubjectset": { "relation": "writer" }
                            },
                            {
                                "tupleToSubjectset": {
                                    "tupleset": { "relation": "parent" },
                                    "computedSubjectset": { "relation": "reader" }
                                }
                            }
                        ]
                    }
                }
            }
        ]
    }
 }' localhost:50052 authorizer.accesscontroller.v1alpha1.NamespaceConfigService.WriteConfig
{

}
  1. Insert a couple of relation tuples
$ grpcurl -plaintext -d \
'{
    "relationTupleDeltas": [
        {
            "action": "ACTION_INSERT",
            "relationTuple": {
                "namespace": "files",
                "object": "file1",
                "relation": "parent",
                "subject": {
                    "set": {
                        "namespace": "folders",
                        "object": "folder1",
                        "relation": "..."
                    }
                }
            }
        },
        {
            "action": "ACTION_INSERT",
            "relationTuple": {
                "namespace": "folders",
                "object": "folder1",
                "relation": "writer",
                "subject": {
                    "id": "edmund"
                }
            }
        },
        {
            "action": "ACTION_INSERT",
            "relationTuple": {
                "namespace": "files",
                "object": "file1",
                "relation": "writer",
                "subject": {
                    "id": "baldrick"
                }
            }
        }
    ]
 }' localhost:50052 authorizer.accesscontroller.v1alpha1.WriteService.WriteRelationTuplesTxn
{

}
  1. Check for permissions
$ grpcurl -plaintext -d \
'{
    "namespace": "folders",
    "object": "folder1",
    "relation": "writer",
    "subject": {
        "id": "edmund"
    }
 }' localhost:50052 authorizer.accesscontroller.v1alpha1.CheckService.Check
{
  "allowed": true
}  # OK - edmund is a writer on folder1
$ grpcurl -plaintext -d \
'{
    "namespace": "folders",
    "object": "folder1",
    "relation": "reader",
    "subject": {
        "id": "edmund"
    }
 }' localhost:50052 authorizer.accesscontroller.v1alpha1.CheckService.Check
{
  "allowed": true
}  # OK - edmund is a reader on folder1 since he is a writer on this folder too
$ grpcurl -plaintext -d \
'{
    "namespace": "files",
    "object": "file1",
    "relation": "writer",
    "subject": {
        "id": "edmund"
    }
 }' localhost:50052 authorizer.accesscontroller.v1alpha1.CheckService.Check
{
  "allowed": true
}  # OK - edmund is a writer on file1 since he is a writer on the parent folder
$ grpcurl -plaintext -d \
'{
    "namespace": "files",
    "object": "file1",
    "relation": "reader",
    "subject": {
        "id": "edmund"
    }
 }' localhost:50052 authorizer.accesscontroller.v1alpha1.CheckService.Check
{
  "allowed": true
}  # OK - edmund is a reader on file1 since he is a writer on the parent folder
$ grpcurl -plaintext -d \
'{
    "namespace": "files",
    "object": "file1",
    "relation": "writer",
    "subject": {
        "id": "baldrick"
    }
 }' localhost:50052 authorizer.accesscontroller.v1alpha1.CheckService.Check
{

}  # NOT OK - baldrick is a writer on file1
$ grpcurl -plaintext -d \
'{
    "namespace": "files",
    "object": "file1",
    "relation": "reader",
    "subject": {
        "id": "baldrick"
    }
 }' localhost:50052 authorizer.accesscontroller.v1alpha1.CheckService.Check
{

}  # NOT OK - baldrick is a reader on file1 since he is a writer too

Describe the solution you'd like
The inserted relation tuples on the inner/nested namespace should be correctly reflected through check calls.

@gruuya thanks for the question! Though, I actually believe this is working as expected.

The namespace config for the files namespace defines the 'writer' relation as a tupleToSubjectset and only a tupleToSubjectset. So the only condition in which 'baldrick' will have the writer relation is when he has the write relation on the parent folder. Since baldrick does not have the writer relation on the parent folder, he does not have the writer relation on the file. And since he does not have the writer relation on the file, he therefore does not have the reader relation on it either.

I believe what you mean to do is this instead:

$ grpcurl -plaintext -d \
'{
    "config": {
        "name": "files",
        "relations": [
            {
                "name": "parent"
            },
            {
                "name": "writer",
                "rewrite": {
                    "union": {
                        "children": [
                            { "this": {} },
                            {
                                "tupleToSubjectset": {
                                    "tupleset": { "relation": "parent" },
                                    "computedSubjectset": { "relation": "writer" }
                                }
                            }
                        ]
                    }
                }
            },
            {
                "name": "reader",
                "rewrite": {
                    "union": {
                        "children": [
                            { "this": {} },
                            {
                              	"computedSubjectset": { "relation": "writer" }
                            },
                            {
                                "tupleToSubjectset": {
                                    "tupleset": { "relation": "parent" },
                                    "computedSubjectset": { "relation": "reader" }
                                }
                            }
                        ]
                    }
                }
            }
        ]
    }
 }' localhost:50052 authorizer.accesscontroller.v1alpha1.NamespaceConfigService.WriteConfig

In the above case, if baldrick has explicit writer access, then he'll be allowed for both the writer and reader relation. And if he has the writer relation on the parent folder, then he'll also have writer and reader access.

Does this answer your question?

Oh right, that makes sense - thanks for a quick turnaround!