Policy Enforcer
Policy enforcer is a open source tool that allows you to easily create complex authorization policy. Supports RBAC, ABAC and resource filtering based on them.
Β Β Β Β Β Β Β Β
Features
- Generate your complex authorization policy easily with code.
- Export the rego (policy language for defining rules that are evaluated by the OPA engine) you created with the code.
- Filter data based on your authorization logic.
- Make decisions about multiple resources from one policy.
- Get the details of the decisions made.
- Add custom messages and handle decision messages.
π Setup
Install
go get github.com/Permify/policy-enforcer
Run Tests
go test
Import enforcer.
import enforcer `github.com/Permify/policy-enforcer`
π Usage
var user = User{
ID: 1,
Roles: []string{"admin"},
Attributes: map[string]interface{}{
"tenure": 8,
},
}
blog := map[string]interface{}{
"id": 1,
"status": "PUBLIC",
}
var isAdmin = enforcer.NewRule("'admin' in user.roles").SetFailMessage("user is not an admin").SetKey("is admin")
var isSenior = enforcer.NewRule("user.tenure > 8").SetFailMessage("user is not senior")
var isManager = enforcer.NewRule("'manager' in user.roles").SetFailMessage("user is not manager")
var isPublic = enforcer.NewRule("blog.status == 'PUBLIC'").SetFailMessage("blog is not public")
policy := enforcer.New()
// set user object
policy.Set("user", user)
policy.Set("blog", blog)
// its means the user must be either an admin or a senior manager or blog is public
policy.Option(isAdmin).Option(isSenior, isManager).Option(isPublic)
result, err := policy.IsAuthorized()
Output
{
Allows: {
{
Allow: true // final result
},
},
Details: {
{
Allow: true, // is admin result
Key: "is_admin",
Message: ""
},
{
Allow: true, // is senior result
Key: "tcuaxhxkqfdafplsjfbc", // if the key is not set it is created automatically
Message: ""
},
{
Allow: true, // is public result
Key: (string) (len=20) "lgtemapezqleqyhyzryw",
Message: (string) ""
},
{
Allow: false, // is manager result
Key: "xoeffrswxpldnjobcsnv", // if the key is not set it is created automatically
Message: "user is not manager"
}
}
}
π Multiple Resource Response
You can check authorization of multiple resources at once.
example scenario
- If the user is admin, can edit all listed resources. (Admin)
- If the user owns the resource, they can only edit their own resources. (Resource Owner)
Admin
policy := enforcer.New()
policy.SetUser(enforcer.User{
ID: "1",
Roles: []string{"admin"}, // admin
Attributes: map[string]interface{}{
"tenure": 9,
},
})
policy.SetResources(
enforcer.Resource{
ID: "1",
Type: "posts",
Attributes: map[string]interface{}{
"owner_id": "1",
},
},
enforcer.Resource{
ID: "2",
Type: "posts",
Attributes: map[string]interface{}{
"owner_id": "2",
},
},
)
var isAdmin = enforcer.NewRule("'admin' in user.roles").SetFailMessage("user is not an admin")
var isResourceOwner = enforcer.NewRule("resource.attributes.owner_id == '1'")
// its means the user must be either an admin or a resource owner
policy.Option(isAdmin).Option(isResourceOwner)
var r, err = policy.IsAuthorized()
{
Allows: {
{
Allow: true, // its true because user is admin
Meta: {
"id": "1"
"type": "posts",
}
},
{
Allow: true, // its true because user is admin
Meta: {
"id": "2",
"type": "posts"
}
}
},
Details: {
{
Allow: true,
Key: "lgtemapezqleqyhyzryw",
Message: ""
}
}
}
Resource Owner
policy := New()
policy.SetUser(enforcer.User{
ID: "1",
Roles: []string{"manager"},
Attributes: map[string]interface{}{
"tenure": 9,
},
})
policy.SetResources(
enforcer.Resource{
ID: "1",
Type: "posts",
Attributes: map[string]interface{}{
"owner_id": "1",
},
},
enforcer.Resource{
ID: "2",
Type: "posts",
Attributes: map[string]interface{}{
"owner_id": "2",
},
},
)
var isAdmin = enforcer.NewRule("'admin' in user.roles").SetFailMessage("user is not an admin")
var isResourceOwner = enforcer.NewRule("resource.attributes.owner_id == '1'")
// its means the user must be either an admin or a resource owner
policy.Option(isAdmin).Option(isResourceOwner)
var r, err = policy.IsAuthorized()
Output
{
Allows: {
{
Allow: true, // its true because user is owner of the this resource
Meta: {
"id": "1"
"type": "posts",
}
},
{
Allow: false, // its false because user is not owner of the this resource
Meta: {
"id": "2",
"type": "posts"
}
}
},
Details: {
{
Allow: false,
Key: "lgtemapezqleqyhyzryw",
Message: "user is not an admin"
}
}
}
π Data Filtering
Filter resources that match the rules you created
example scenario
- User can only edit their own posts. Fetch all of the posts it can edit
policy := New()
policy.SetUser(enforcer.User{
ID: "1",
Roles: []string{"manager"},
Attributes: map[string]interface{}{
"tenure": 9,
},
})
policy.SetResources(
enforcer.Resource{
ID: "1",
Type: "posts",
Attributes: map[string]interface{}{
"owner_id": "1",
},
},
enforcer.Resource{
ID: "2",
Type: "posts",
Attributes: map[string]interface{}{
"owner_id": "2",
},
},
)
var isAdmin = enforcer.NewRule("'admin' in user.roles").SetFailMessage("user is not an admin")
var isResourceOwner = enforcer.NewRule("resource.attributes.owner_id == '1'")
// its means the user must be either an admin or a resource owner
policy.Option(isAdmin).Option(isResourceOwner)
resources, err := policy.AuthorizedResources()
Output
{
{
ID: "1",
Type: "posts",
Attributes: {
"owner_id": "1"
}
}
}
π¨ Create New Rule
the user should a manager role among their roles
var isManager = NewRule("'manager' in user.roles")
The user's tenure must be at least 8 years and the user should a manager role among their roles.
var isSeniorManager = NewRule("user.attributes.tenure > 8", "'manager' in user.roles")
The user is the owner of the resource or resources.
var isResourceOwner = NewRule("resource.attributes.owner.id == '1'")
βοΈ Set Fail Message
After the set fail message function decides on the policy, if the rule is false, this message will be printed on the error
var isAdmin = enforcer.NewRule("'admin' in user.roles").SetFailMessage("user is not an admin")
Output
Details: {
{
Allow: false, // result
Key: "xoeffrswxpldnjobcsnv",
Message: "user is not an admin" // when it fails the message will appear here
},
}
π Set Key
You can use it when you do not want the key to be generated automatically. This will allow you to perceive your details better.
var isAdmin = enforcer.NewRule("'admin' in user.roles").SetKey("is admin").SetFailMessage("user is not an admin")
Output
Details: {
{
Allow: true, // result
Key: "is_admin",
Message: "" // when true, the message does not appear
},
}
Options
Options allow you to establish an or relationship between rules and create a more organized and legible authorization structure.
// its means the user must be either an admin or a senior and manager
enforcer.Option(isAdmin).Option(isSenior, isManager)
To Rego Function
You can export the rego policies you created with the code with this function.
Example 1
var isAdmin = enforcer.NewRule("'admin' in user.roles").SetFailMessage("user is not an admin").SetKey("is admin")
var isSenior = enforcer.NewRule("user.tenure > 8").SetFailMessage("user is not senior")
var isManager = enforcer.NewRule("'manager' in user.roles").SetFailMessage("user is not manager")
policy := enforcer.New()
// its means the user must be either an admin or a senior manager
policy.Option(isAdmin).Option(isSenior, isManager)
fmt.Println(policy.ToRego())
Output
package app.permify
import future.keywords.every
# imports
import input.user as user
default allow = false
# options
allow {
is_admin
}
allow {
tcuaxhxkqfdafplsjfbc
xoeffrswxpldnjobcsnv
}
# rules
tcuaxhxkqfdafplsjfbc {
user.attributes.tenure > 8
}
xoeffrswxpldnjobcsnv {
"manager" in user.roles
}
is_admin {
"admin" in user.roles
}
Example 2
policy := enforcer.New()
var isAdmin = enforcer.NewRule("'admin' in user.roles").SetFailMessage("user is not an admin")
var isResourceOwner = enforcer.NewRule("resource.attributes.owner_id == '1'")
// its means the user must be either an admin or a resource owner
policy.Option(isAdmin).Option(isResourceOwner)
fmt.Println(policy.ToRego())
Output
package app.permify
import future.keywords.every
# imports
import input.user as user
import input.resources as resources
# options
allows[output] {
resource := resources[_]
lgtemapezqleqyhyzryw
output := {"id": resource.id, "type": resource.type}
}
allows[output] {
resource := resources[_]
jjpjzpfrfegmotafeths(resource)
output := {"id": resource.id, "type": resource.type}
}
# rules
lgtemapezqleqyhyzryw {
"admin" in user.roles
}
jjpjzpfrfegmotafeths(resource) {
resource.attributes.owner_id == "1"
}
Iterator
You can review allow and meta with iterator.
result, err := policy.IsAuthorized()
for result.hasNext() {
allow := result.getNext()
fmt.Sprintf("is authorized: %s", allow.Allow)
}
Validate
Validate your rules and debug.
err := NewRule("'admin i user.roles").Validate() // return error
Need More, Check Out our API
Permify API is an authorization API which you can add complex rbac and abac solutions.
:heart: Let's get connected: