Reputation: 3451
We are building a cloud based application, using C# as our main language and running on Microsoft Azure. One of the key pieces of the architecture is to have fine grained authorization rights implemented for business logic in the application.
We are looking at Open Policy Agent, as that seems to be a promising technology for these purposes. The example scenario/rules are described below. But it boils down to the scenario in something like a SharePoint library, or a Windows folder on the file system.
You have a hierarchy and you want to assign users with specific permissions to places in the hierarchy, where rights get inherited, but can be overridden deeper in the tree.
The platform we have will frequently update the permissions of users in the tree, and will constantly evaluate the policy agent to ask if a user has rights to perform a specific action on our platform.
The question we have:
We have a tenant hierarchy that lists devices, defined like a tree structure:
+- Plant01
+- Line01
+- Device01
- Line02
+- Device02
- Device03
- Device04
+- Plant02
+- Line03
+- Device05
- Device06
- Device07
We also have people, defined in groups/users that we want to assign a specific role on a place in that hierarchy above.
Example:
Plant01-Line01
Plant01-Line02
Plant01
Plant01-Line02-Device04
Upvotes: 4
Views: 2125
Reputation: 17496
Are there other alternatives to OPA that are better suited for this?
ReBAC solutions (such as those based on Google's Zanzibar paper) might offer a good alternative for your use-case.
Some of the current offerings that are based of the paper and offer Fine Grained Authorization Services include:
They all allow you to define a policy and represent the system state in order to make a decision on whether someone is authorized to access a resource.
For example in Auth0 FGA, you might define an authorization model like so:
type plant
relations
define parent as self
define viewer as self
define stopper as self
define can_stop as stopper
type line
relations
define parent as self
define viewer as self or viewer from parent
define stopper as self or stopper from parent
define can_stop as stopper
type device
relations
define parent as self
define viewer as self or viewer from parent
define stopper as self or stopper from parent
define can_stop as stopper but not can_not_stop
define can_not_stop as self
Then you can add the following relationship tuples to reflect your current state:
write({ "user": "plant:01", "relation": "parent_plant", "object": "line:01" })
write({ "user": "plant:01", "relation": "parent_plant", "object": "line:02" })
write({ "user": "line:01", "relation": "parent_line", "object": "device:01" })
write({ "user": "line:02", "relation": "parent_line", "object": "device:02" })
write({ "user": "line:02", "relation": "parent_line", "object": "device:03" })
write({ "user": "line:02", "relation": "parent_line", "object": "device:04" })
write({ "user": "plant:02", "relation": "parent_plant", "object": "line:03" })
write({ "user": "line:03", "relation": "parent_line", "object": "device:05" })
write({ "user": "line:03", "relation": "parent_line", "object": "device:06" })
write({ "user": "line:03", "relation": "parent_line", "object": "device:07" })
write({ "user": "user01", "relation": "stopper", "object": "line:01" })
write({ "user": "user01", "relation": "viewer", "object": "line:02" })
write({ "user": "user00", "relation": "stopper", "object": "plant:01" })
write({ "user": "user00", "relation": "can_not_stop", "object": "device:04" })
And then you can ask questions like "is user1
related to X as can_stop
?":
check("user01", "can_stop", "device:01") // { "allowed": true }
check("user01", "can_stop", "line:01") // { "allowed": true }
check("user01", "can_stop", "device:02") // { "allowed": false }
You can try this out in the Playground: https://play.fga.dev/stores/create/?id=01GGQYNRHK8EM07146HB8SSHXD
Disclaimer: I'm currently part of the Auth0 FGA team.
Upvotes: 4
Reputation: 4200
One alternative to OPA for fine-grained authZ is Styra (which is actually built by the creators of OPA). Another one was build.security, but this was acquired by Elastic. When I spoke to Elastic (they're a vendor we use at the company I work for), it seemed like they had various plans for build.security, but fine-grained authZ was a lower priority - not sure where this is at now...
FWIW we started building a bespoke entitlements layer (that uses OPA under-the-hood as the fine-grained authZ policy engine), but I'd be keen to migrate to more of a dedicated COTS offering at some stage...
Upvotes: 0
Reputation: 118
I believe OPA is an excellent choice for solving this kind of problem.
I don't know if this is a good example, but it gets the job done on your particular example, I think.
Given the input:
{
"user": "User00",
"action": "stop",
"device": ["Plant01", "Line02", "Device04"]
}
and the data:
{
"roles": {
"User00": ["Role00"],
"User01": ["Role01"],
"User99": ["Role00", "Role01"]
},
"grants": {
"Role00": [
{
"action": "stop",
"path": ["Plant01"]
},
{
"allow": false,
"action": "stop",
"path": ["Plant01", "Line02", "Device04"]
}
],
"Role01": [
{
"action": "stop",
"path": ["Plant01", "Line01"]
},
{
"action": "view",
"path": ["Plant01", "Line02"]
}
]
}
}
, this policy verifies that the input is allowed given the data:
package play
import future.keywords.in
default allow = false
allow {
# Check that the 'grantResults' is a set containing only 'true'.
grantResults == {true}
}
# This creates a set of all matching grant results. In the end, we want only one 'true' entry.
grantResults[granted] {
some grant
grants_for_user[grant]
grant.action == input.action
array.slice(input.device, 0, count(grant.path)) == grant.path
granted = isAllowed(grant)
}
grants_for_user[grant] {
some role in data.roles[input.user]
some grant in data.grants[role]
}
# Undefined allow is implicitly true
isAllowed(grant) = allowed {
allowed = grant.allow
} else = true
You can check out a live example here: https://play.openpolicyagent.org/p/YGRvm90KRU
The above policy is naïve, and will evaluate all applicable paths in the tree for the given input. With some thinking, an early exit strategy can probably also be made.
Upvotes: 1