blob: a5b0f0be3d23b3d6a7ea73683ed94864a8a6a4c0 [file] [log] [blame]
// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package casbin
import (
"errors"
"github.com/Knetic/govaluate"
"github.com/casbin/casbin/config"
"github.com/casbin/casbin/model"
"github.com/casbin/casbin/persist"
"log"
"reflect"
)
// Effect is the result for a policy rule.
type Effect int
const (
EFFECT_ALLOW Effect = iota
EFFECT_INDETERMINATE
EFFECT_DENY
)
// Enforcer is the main interface for authorization enforcement and policy management.
type Enforcer struct {
modelPath string
model model.Model
fm model.FunctionMap
adapter persist.Adapter
enabled bool
}
// NewEnforcer gets an enforcer via CONF, file or DB.
// Note: this function will panic on errors, please use NewEnforcerSafe() instead if you want to get error.
// e := NewEnforcer("path/to/casbin.conf")
// e := NewEnforcer("path/to/basic_model.conf", "path/to/basic_policy.conf")
// e := NewEnforcer("path/to/rbac_model.conf", "mysql", "root:@tcp(127.0.0.1:3306)/")
func NewEnforcer(params ...interface{}) *Enforcer {
e, err := NewEnforcerSafe(params...)
if err != nil {
panic(err)
}
return e
}
// NewEnforcerSafe gets an enforcer via CONF, file or DB.
func NewEnforcerSafe(params ...interface{}) (*Enforcer, error) {
e := &Enforcer{}
if len(params) == 1 {
err := e.InitWithConfig(params[0].(string))
return e, err
} else if len(params) == 2 {
if reflect.TypeOf(params[1]).Kind() == reflect.String {
err := e.InitWithFile(params[0].(string), params[1].(string))
return e, err
} else {
err := e.InitWithAdapter(params[0].(string), params[1].(persist.Adapter))
return e, err
}
} else if len(params) == 3 {
err := e.InitWithDB(params[0].(string), params[1].(string), params[2].(string))
return e, err
} else {
return nil, errors.New("Invalid parameters for enforcer.")
}
}
// InitWithFile initializes an enforcer with a model file and a policy file.
func (e *Enforcer) InitWithFile(modelPath string, policyPath string) error {
e.modelPath = modelPath
e.adapter = persist.NewFileAdapter(policyPath)
e.enabled = true
err := e.LoadModel()
if err != nil {
return err
}
return e.LoadPolicy()
}
// InitWithDB initializes an enforcer with a model file and a policy from database.
func (e *Enforcer) InitWithDB(modelPath string, driverName string, dataSourceName string) error {
e.modelPath = modelPath
e.adapter = persist.NewDBAdapter(driverName, dataSourceName)
e.enabled = true
err := e.LoadModel()
if err != nil {
return err
}
return e.LoadPolicy()
}
// InitWithConfig initializes an enforcer with a configuration file, by default is casbin.conf.
func (e *Enforcer) InitWithConfig(cfgPath string) error {
cfg, err := config.LoadConfig(cfgPath)
if err != nil {
return err
}
e.modelPath = cfg.ModelPath
if cfg.PolicyBackend == "file" {
e.adapter = persist.NewFileAdapter(cfg.PolicyPath)
} else if cfg.PolicyBackend == "database" {
e.adapter = persist.NewDBAdapter(cfg.DBDriver, cfg.DBDataSource)
}
e.enabled = true
err = e.LoadModel()
if err != nil {
return err
}
return e.LoadPolicy()
}
// InitWithAdapter initializes an enforcer with an adapter.
func (e *Enforcer) InitWithAdapter(modelPath string, adapter persist.Adapter) error {
e.modelPath = modelPath
e.adapter = adapter
e.enabled = true
err := e.LoadModel()
if err != nil {
return err
}
return e.LoadPolicy()
}
// LoadModel reloads the model from the model CONF file.
// Because the policy is attached to a model, so the policy is invalidated and needs to be reloaded by calling LoadPolicy().
func (e *Enforcer) LoadModel() error {
var err error
e.model, err = model.LoadModel(e.modelPath)
if err != nil {
return err
}
e.model.PrintModel()
e.fm = model.LoadFunctionMap()
return nil
}
// GetModel gets the current model.
func (e *Enforcer) GetModel() model.Model {
return e.model
}
// ClearPolicy clears all policy.
func (e *Enforcer) ClearPolicy() {
e.model.ClearPolicy()
}
// LoadPolicy reloads the policy from file/database.
func (e *Enforcer) LoadPolicy() error {
e.model.ClearPolicy()
err := e.adapter.LoadPolicy(e.model)
if err != nil {
return err
}
e.model.PrintPolicy()
e.model.BuildRoleLinks()
return nil
}
// SavePolicy saves the current policy (usually after changed with casbin API) back to file/database.
func (e *Enforcer) SavePolicy() error {
return e.adapter.SavePolicy(e.model)
}
// Enable changes the enforcing state of casbin, when casbin is disabled, all access will be allowed by the Enforce() function.
func (e *Enforcer) Enable(enable bool) {
e.enabled = enable
}
// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
// Note: this function will panic on errors, please use EnforceSafe() instead if you want to get error.
func (e *Enforcer) Enforce(rvals ...string) bool {
result, err := e.EnforceSafe(rvals...)
if err != nil {
panic(err)
}
return result
}
// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
func (e *Enforcer) EnforceSafe(rvals ...string) (bool, error) {
if !e.enabled {
return true, nil
}
expString := e.model["m"]["m"].Value
var expression *govaluate.EvaluableExpression
functions := make(map[string]govaluate.ExpressionFunction)
for key, function := range e.fm {
functions[key] = function
}
_, ok := e.model["g"]
if ok {
for key, ast := range e.model["g"] {
rm := ast.RM
functions[key] = func(args ...interface{}) (interface{}, error) {
if len(args) == 2 {
name1 := args[0].(string)
name2 := args[1].(string)
return (bool)(rm.HasLink(name1, name2)), nil
} else {
name1 := args[0].(string)
name2 := args[1].(string)
domain := args[2].(string)
return (bool)(rm.HasLink(name1, name2, domain)), nil
}
}
}
}
expression, _ = govaluate.NewEvaluableExpressionWithFunctions(expString, functions)
var policyResults []Effect
if len(e.model["p"]["p"].Policy) != 0 {
policyResults = make([]Effect, len(e.model["p"]["p"].Policy))
for i, pvals := range e.model["p"]["p"].Policy {
//log.Print("Policy Rule: ", pvals)
parameters := make(map[string]interface{}, 8)
for j, token := range e.model["r"]["r"].Tokens {
parameters[token] = rvals[j]
}
for j, token := range e.model["p"]["p"].Tokens {
parameters[token] = pvals[j]
}
result, err := expression.Evaluate(parameters)
//log.Print("Result: ", result)
if err != nil {
policyResults[i] = EFFECT_INDETERMINATE
} else {
if !result.(bool) {
policyResults[i] = EFFECT_INDETERMINATE
} else {
if effect, ok := parameters["p_eft"]; ok {
if effect == "allow" {
policyResults[i] = EFFECT_ALLOW
} else if effect == "deny" {
policyResults[i] = EFFECT_DENY
} else {
policyResults[i] = EFFECT_INDETERMINATE
}
} else {
policyResults[i] = EFFECT_ALLOW
}
}
}
}
} else {
policyResults = make([]Effect, 1)
parameters := make(map[string]interface{}, 8)
for j, token := range e.model["r"]["r"].Tokens {
parameters[token] = rvals[j]
}
for _, token := range e.model["p"]["p"].Tokens {
parameters[token] = ""
}
result, err := expression.Evaluate(parameters)
//log.Print("Result: ", result)
if err != nil {
policyResults[0] = EFFECT_INDETERMINATE
} else {
if result.(bool) {
policyResults[0] = EFFECT_ALLOW
} else {
policyResults[0] = EFFECT_INDETERMINATE
}
}
}
//log.Print("Rule Results: ", policyResults)
result := false
if e.model["e"]["e"].Value == "some(where (p_eft == allow))" {
result = false
for _, eft := range policyResults {
if eft == EFFECT_ALLOW {
result = true
break
}
}
} else if e.model["e"]["e"].Value == "!some(where (p_eft == deny))" {
result = true
for _, eft := range policyResults {
if eft == EFFECT_DENY {
result = false
break
}
}
} else if e.model["e"]["e"].Value == "some(where (p_eft == allow)) && !some(where (p_eft == deny))" {
result = false
for _, eft := range policyResults {
if eft == EFFECT_ALLOW {
result = true
} else if eft == EFFECT_DENY {
result = false
break
}
}
}
log.Print("Request ", rvals, ": ", result)
return result, nil
}