policy_enforcer

package module
v1.0.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 1, 2022 License: MIT Imports: 10 Imported by: 0

README ΒΆ


policy enforcer logo

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.

go versionΒ Β go reportΒ Β licenseΒ Β tweetΒ Β tweet

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:

guilyx's LinkdeIN guilyx's Discord

Documentation ΒΆ

Index ΒΆ

Constants ΒΆ

View Source
const (
	MULTIPLE = "multiple"
	SINGLE   = "single"
)

Variables ΒΆ

This section is empty.

Functions ΒΆ

func CleanCondition ΒΆ

func CleanCondition(str string) string

CleanCondition weeds out the places where there might be errors when creating conditions @param string @return string

func GenerateLowerCaseRandomString ΒΆ added in v1.0.5

func GenerateLowerCaseRandomString(n int) string

GenerateLowerCaseRandomString its generate lowercase random string @param int @return string

func Key ΒΆ

func Key(b string) string

Key makes the keys you give while creating a rule compatible with the rego @param string @return string

func Pre ΒΆ

func Pre(x interface{}, y ...interface{})

Pre exit running project. @param interface{} @param ...interface{}

func ToMap ΒΆ

func ToMap(u interface{}) (mp map[string]interface{}, err error)

ToMap turns objects into maps @param interface{} @return map[string]interface{}, error

func ToMapArray ΒΆ added in v1.0.5

func ToMapArray(u interface{}) (mp []map[string]interface{}, err error)

ToMapArray turns objects into map array @param interface{} @return []map[string]interface{}, error

Types ΒΆ

type Allow ΒΆ added in v1.0.5

type Allow struct {
	Allow bool                   `json:"allow"`
	Meta  map[string]interface{} `json:"meta"`
}

Allow */

type EnforcerError ΒΆ added in v1.0.6

type EnforcerError error
var (
	// ErrUnsafeVarError
	ErrUnsafeVarError EnforcerError = errors.New("Unsafe Variable Error")
	// ErrParseError
	ErrParseError EnforcerError = errors.New("Parse Error")
)

type Option ΒΆ added in v1.0.5

type Option struct {
	Rules []Rule
}

Option makes it easy for you to group rules and relate them to and,or

func NewOption ΒΆ added in v1.0.5

func NewOption(rules ...Rule) Option

NewOption creates a new option @param bool @param ...Rule @return Option

func (Option) GetTemplate ΒΆ added in v1.0.5

func (r Option) GetTemplate(s Strategy) string

GetTemplate @param string @return string

type Policy ΒΆ

type Policy struct {
	Error     error
	Statement *Statement
}

Policy */

func New ΒΆ

func New() *Policy

New creates new policy instance

func (*Policy) AuthorizedResources ΒΆ added in v1.0.6

func (p *Policy) AuthorizedResources() (resources []Resource, err error)

AuthorizedResources . @return Result, error

func (*Policy) IsAuthorized ΒΆ

func (p *Policy) IsAuthorized() (result Result, err error)

IsAuthorized It creates a decision about whether the inputs you give comply with the rules you write and a result about the reason @return Result, error

func (*Policy) Option ΒΆ added in v1.0.5

func (p *Policy) Option(rules ...Rule) (policy *Policy)

Option makes it easy for you to group rules and relate them to and @param bool @param ...Rule @return *Policy

func (*Policy) Set ΒΆ

func (p *Policy) Set(key string, value interface{}) (policy *Policy)

Set loads the object into the statement. @param string @param interface{} @return *Policy

func (*Policy) SetPackage ΒΆ added in v1.0.5

func (p *Policy) SetPackage(pg string) (policy *Policy)

SetPackage . @param string @return *Policy

func (*Policy) SetResources ΒΆ added in v1.0.5

func (p *Policy) SetResources(resources ...Resource) (policy *Policy)

SetResources loads the resources into the statement. @param string @param interface{} @return *Policy

func (*Policy) SetUser ΒΆ added in v1.0.5

func (p *Policy) SetUser(user User) (policy *Policy)

SetUser loads the user into the statement. @param string @param interface{} @return *Policy

func (*Policy) ToRego ΒΆ

func (p *Policy) ToRego() string

ToRego Returns the rules you create programmatically as strings in rego language @return string

type Resource ΒΆ added in v1.0.5

type Resource struct {
	ID         string                 `json:"id"`
	Type       string                 `json:"type"`
	Attributes map[string]interface{} `json:"attributes"`
	Group      interface{}            `json:"group"`
}

func NewResource ΒΆ added in v1.0.5

func NewResource(id string, typ string, attributes map[string]interface{}, group interface{}) Resource

NewResource .

type Result ΒΆ

type Result struct {
	Allows  []Allow      `json:"allows"`
	Details []RuleResult `json:"details"`
	// contains filtered or unexported fields
}

Result result */

type Rule ΒΆ

type Rule struct {
	Key         string
	FailMessage error
	Conditions  []string
}

func NewRule ΒΆ added in v1.0.5

func NewRule(conditions ...string) Rule

NewRule creates a new rule @param string @param ...string @return Rule

func (Rule) Evict ΒΆ added in v1.0.5

func (r Rule) Evict(s Strategy) string

Evict @param string @return string

func (Rule) GetBody ΒΆ added in v1.0.6

func (r Rule) GetBody() string

GetBody @param string @return string

func (Rule) GetHead ΒΆ added in v1.0.6

func (r Rule) GetHead(s Strategy) string

GetHead @param string @return string

func (Rule) GetTemplate ΒΆ added in v1.0.5

func (r Rule) GetTemplate() string

GetTemplate @param string @return string

func (Rule) SetFailMessage ΒΆ added in v1.0.5

func (r Rule) SetFailMessage(message string) Rule

SetFailMessage set the message that it displays when the rule you have created return false @param string @return Rule

func (Rule) SetKey ΒΆ added in v1.0.5

func (r Rule) SetKey(key string) Rule

SetKey set the key @param string @return Rule

func (Rule) Validate ΒΆ added in v1.0.6

func (r Rule) Validate() error

Validate @return error

type RuleResult ΒΆ

type RuleResult struct {
	Allow   bool   `json:"allow"`
	Key     string `json:"key"`
	Message string `json:"message"`
}

RuleResult rule result */

type Rules ΒΆ

type Rules []Rule

Rules provides methods for you to manage array data more easily.

func (Rules) Evacuations ΒΆ added in v1.0.5

func (c Rules) Evacuations(s Strategy) (evacuations []string)

Evacuations returns an array of the rule array's raws. @return []String

func (Rules) Heads ΒΆ added in v1.0.7

func (c Rules) Heads(s Strategy) (keys []string)

Heads returns an array of the rule array's heads. @return []String

func (Rules) Len ΒΆ

func (c Rules) Len() (length int64)

Len returns the number of elements of the array. @return int64

func (Rules) Origin ΒΆ

func (c Rules) Origin() []Rule

Origin convert the collection to rule array. @return []models.Rule

type Statement ΒΆ

type Statement struct {
	Package   string
	Imports   []string
	Inputs    map[string]interface{}
	Options   []Option
	Rules     map[string]Rule
	User      User
	Resources []Resource
	Strategy  Strategy
	Context   context.Context
}

Statement statement

type Strategy ΒΆ added in v1.0.5

type Strategy string

type User ΒΆ added in v1.0.5

type User struct {
	ID         string                 `json:"id"`
	Roles      []string               `json:"roles"`
	Attributes map[string]interface{} `json:"attributes"`
}

func NewUser ΒΆ added in v1.0.5

func NewUser(id string, roles []string, attributes map[string]interface{}) User

NewUser .

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL