Files
upstream-casbin/model/policy.go
upstream-mirror d8971e16fb
Some checks failed
Build / test (1.21) (push) Successful in 2m21s
Build / benchmark (1.21) (push) Failing after 2m28s
golangci-lint / golangci (push) Successful in 19m40s
snapshot of https://github.com/casbin/casbin.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:46:19 +05:00

483 lines
14 KiB
Go

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 model
import (
"fmt"
"strconv"
"strings"
"github.com/casbin/casbin/v3/constant"
"github.com/casbin/casbin/v3/rbac"
"github.com/casbin/casbin/v3/util"
)
type (
PolicyOp int
)
const (
PolicyAdd PolicyOp = iota
PolicyRemove
)
const DefaultSep = ","
// BuildIncrementalRoleLinks provides incremental build the role inheritance relations.
func (model Model) BuildIncrementalRoleLinks(rmMap map[string]rbac.RoleManager, op PolicyOp, sec string, ptype string, rules [][]string) error {
if sec == "g" && rmMap[ptype] != nil {
_, err := model.GetAssertion(sec, ptype)
if err != nil {
return err
}
return model[sec][ptype].buildIncrementalRoleLinks(rmMap[ptype], op, rules)
}
return nil
}
// BuildRoleLinks initializes the roles in RBAC.
func (model Model) BuildRoleLinks(rmMap map[string]rbac.RoleManager) error {
model.PrintPolicy()
for ptype, ast := range model["g"] {
if rm := rmMap[ptype]; rm != nil {
err := ast.buildRoleLinks(rm)
if err != nil {
return err
}
}
}
return nil
}
// BuildIncrementalConditionalRoleLinks provides incremental build the role inheritance relations.
func (model Model) BuildIncrementalConditionalRoleLinks(condRmMap map[string]rbac.ConditionalRoleManager, op PolicyOp, sec string, ptype string, rules [][]string) error {
if sec == "g" && condRmMap[ptype] != nil {
_, err := model.GetAssertion(sec, ptype)
if err != nil {
return err
}
return model[sec][ptype].buildIncrementalConditionalRoleLinks(condRmMap[ptype], op, rules)
}
return nil
}
// BuildConditionalRoleLinks initializes the roles in RBAC.
func (model Model) BuildConditionalRoleLinks(condRmMap map[string]rbac.ConditionalRoleManager) error {
model.PrintPolicy()
for ptype, ast := range model["g"] {
if condRm := condRmMap[ptype]; condRm != nil {
err := ast.buildConditionalRoleLinks(condRm)
if err != nil {
return err
}
}
}
return nil
}
// PrintPolicy prints the policy to log.
func (model Model) PrintPolicy() {
// Logger has been removed - this is now a no-op
}
// ClearPolicy clears all current policy.
func (model Model) ClearPolicy() {
for _, ast := range model["p"] {
ast.Policy = nil
ast.PolicyMap = map[string]int{}
}
for _, ast := range model["g"] {
ast.Policy = nil
ast.PolicyMap = map[string]int{}
}
}
// GetPolicy gets all rules in a policy.
func (model Model) GetPolicy(sec string, ptype string) ([][]string, error) {
_, err := model.GetAssertion(sec, ptype)
if err != nil {
return nil, err
}
return model[sec][ptype].Policy, nil
}
// GetFilteredPolicy gets rules based on field filters from a policy.
func (model Model) GetFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) ([][]string, error) {
_, err := model.GetAssertion(sec, ptype)
if err != nil {
return nil, err
}
res := [][]string{}
for _, rule := range model[sec][ptype].Policy {
matched := true
for i, fieldValue := range fieldValues {
if fieldValue != "" && rule[fieldIndex+i] != fieldValue {
matched = false
break
}
}
if matched {
res = append(res, rule)
}
}
return res, nil
}
// HasPolicyEx determines whether a model has the specified policy rule with error.
func (model Model) HasPolicyEx(sec string, ptype string, rule []string) (bool, error) {
assertion, err := model.GetAssertion(sec, ptype)
if err != nil {
return false, err
}
switch sec {
case "p":
if len(rule) != len(assertion.Tokens) {
return false, fmt.Errorf(
"invalid policy rule size: expected %d, got %d, rule: %v",
len(model["p"][ptype].Tokens),
len(rule),
rule)
}
case "g":
if len(rule) < len(assertion.Tokens) {
return false, fmt.Errorf(
"invalid policy rule size: expected %d, got %d, rule: %v",
len(model["g"][ptype].Tokens),
len(rule),
rule)
}
}
return model.HasPolicy(sec, ptype, rule)
}
// HasPolicy determines whether a model has the specified policy rule.
func (model Model) HasPolicy(sec string, ptype string, rule []string) (bool, error) {
_, err := model.GetAssertion(sec, ptype)
if err != nil {
return false, err
}
_, ok := model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)]
return ok, nil
}
// HasPolicies determines whether a model has any of the specified policies. If one is found we return true.
func (model Model) HasPolicies(sec string, ptype string, rules [][]string) (bool, error) {
for i := 0; i < len(rules); i++ {
ok, err := model.HasPolicy(sec, ptype, rules[i])
if err != nil {
return false, err
}
if ok {
return true, nil
}
}
return false, nil
}
// AddPolicy adds a policy rule to the model.
func (model Model) AddPolicy(sec string, ptype string, rule []string) error {
assertion, err := model.GetAssertion(sec, ptype)
if err != nil {
return err
}
assertion.Policy = append(assertion.Policy, rule)
assertion.PolicyMap[strings.Join(rule, DefaultSep)] = len(model[sec][ptype].Policy) - 1
hasPriority := false
if _, ok := assertion.FieldIndexMap[constant.PriorityIndex]; ok {
hasPriority = true
}
if sec == "p" && hasPriority {
if idxInsert, err := strconv.Atoi(rule[assertion.FieldIndexMap[constant.PriorityIndex]]); err == nil {
i := len(assertion.Policy) - 1
for ; i > 0; i-- {
idx, err := strconv.Atoi(assertion.Policy[i-1][assertion.FieldIndexMap[constant.PriorityIndex]])
if err != nil || idx <= idxInsert {
break
}
assertion.Policy[i] = assertion.Policy[i-1]
assertion.PolicyMap[strings.Join(assertion.Policy[i-1], DefaultSep)]++
}
assertion.Policy[i] = rule
assertion.PolicyMap[strings.Join(rule, DefaultSep)] = i
}
}
return nil
}
// AddPolicies adds policy rules to the model.
func (model Model) AddPolicies(sec string, ptype string, rules [][]string) error {
_, err := model.AddPoliciesWithAffected(sec, ptype, rules)
return err
}
// AddPoliciesWithAffected adds policy rules to the model, and returns affected rules.
func (model Model) AddPoliciesWithAffected(sec string, ptype string, rules [][]string) ([][]string, error) {
_, err := model.GetAssertion(sec, ptype)
if err != nil {
return nil, err
}
var affected [][]string
for _, rule := range rules {
hashKey := strings.Join(rule, DefaultSep)
_, ok := model[sec][ptype].PolicyMap[hashKey]
if ok {
continue
}
affected = append(affected, rule)
err = model.AddPolicy(sec, ptype, rule)
if err != nil {
return affected, err
}
}
return affected, err
}
// RemovePolicy removes a policy rule from the model.
func (model Model) RemovePolicy(sec string, ptype string, rule []string) (bool, error) {
ast, err := model.GetAssertion(sec, ptype)
if err != nil {
return false, err
}
key := strings.Join(rule, DefaultSep)
index, ok := ast.PolicyMap[key]
if !ok {
return false, nil
}
// The order of Policy is significant: it carries the priority semantics of
// the "priority(p_eft)" effector, both for explicit priority models and for
// implicit ones where the priority is the policy order itself. So the rule
// has to be shifted out instead of being swapped with the last one.
ast.Policy = append(ast.Policy[:index], ast.Policy[index+1:]...)
delete(ast.PolicyMap, key)
for i := index; i < len(ast.Policy); i++ {
ast.PolicyMap[strings.Join(ast.Policy[i], DefaultSep)] = i
}
return true, nil
}
// UpdatePolicy updates a policy rule from the model.
func (model Model) UpdatePolicy(sec string, ptype string, oldRule []string, newRule []string) (bool, error) {
_, err := model.GetAssertion(sec, ptype)
if err != nil {
return false, err
}
oldPolicy := strings.Join(oldRule, DefaultSep)
index, ok := model[sec][ptype].PolicyMap[oldPolicy]
if !ok {
return false, nil
}
model[sec][ptype].Policy[index] = newRule
delete(model[sec][ptype].PolicyMap, oldPolicy)
model[sec][ptype].PolicyMap[strings.Join(newRule, DefaultSep)] = index
return true, nil
}
// UpdatePolicies updates a policy rule from the model.
func (model Model) UpdatePolicies(sec string, ptype string, oldRules, newRules [][]string) (bool, error) {
_, err := model.GetAssertion(sec, ptype)
if err != nil {
return false, err
}
rollbackFlag := false
// index -> []{oldIndex, newIndex}
modifiedRuleIndex := make(map[int][]int)
// rollback
defer func() {
if rollbackFlag {
for index, oldNewIndex := range modifiedRuleIndex {
model[sec][ptype].Policy[index] = oldRules[oldNewIndex[0]]
oldPolicy := strings.Join(oldRules[oldNewIndex[0]], DefaultSep)
newPolicy := strings.Join(newRules[oldNewIndex[1]], DefaultSep)
delete(model[sec][ptype].PolicyMap, newPolicy)
model[sec][ptype].PolicyMap[oldPolicy] = index
}
}
}()
newIndex := 0
for oldIndex, oldRule := range oldRules {
oldPolicy := strings.Join(oldRule, DefaultSep)
index, ok := model[sec][ptype].PolicyMap[oldPolicy]
if !ok {
rollbackFlag = true
return false, nil
}
model[sec][ptype].Policy[index] = newRules[newIndex]
delete(model[sec][ptype].PolicyMap, oldPolicy)
model[sec][ptype].PolicyMap[strings.Join(newRules[newIndex], DefaultSep)] = index
modifiedRuleIndex[index] = []int{oldIndex, newIndex}
newIndex++
}
return true, nil
}
// RemovePolicies removes policy rules from the model.
func (model Model) RemovePolicies(sec string, ptype string, rules [][]string) (bool, error) {
affected, err := model.RemovePoliciesWithAffected(sec, ptype, rules)
return len(affected) != 0, err
}
// RemovePoliciesWithAffected removes policy rules from the model, and returns affected rules.
func (model Model) RemovePoliciesWithAffected(sec string, ptype string, rules [][]string) ([][]string, error) {
assertion, err := model.GetAssertion(sec, ptype)
if err != nil {
return nil, err
}
var affected [][]string
removeSet := make(map[string]struct{}, len(rules))
for _, rule := range rules {
key := strings.Join(rule, DefaultSep)
if _, ok := assertion.PolicyMap[key]; !ok {
continue
}
if _, exists := removeSet[key]; exists {
continue
}
affected = append(affected, rule)
removeSet[key] = struct{}{}
}
if len(removeSet) == 0 {
return affected, nil
}
compactPolicy := assertion.Policy[:0]
compactPolicyMap := make(map[string]int, len(assertion.Policy)-len(removeSet))
for _, policyRule := range assertion.Policy {
key := strings.Join(policyRule, DefaultSep)
if _, ok := removeSet[key]; ok {
continue
}
compactPolicyMap[key] = len(compactPolicy)
compactPolicy = append(compactPolicy, policyRule)
}
assertion.Policy = compactPolicy
assertion.PolicyMap = compactPolicyMap
return affected, nil
}
// RemoveFilteredPolicy removes policy rules based on field filters from the model.
func (model Model) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) (bool, [][]string, error) {
_, err := model.GetAssertion(sec, ptype)
if err != nil {
return false, nil, err
}
var tmp [][]string
var effects [][]string
res := false
model[sec][ptype].PolicyMap = map[string]int{}
for _, rule := range model[sec][ptype].Policy {
matched := true
for i, fieldValue := range fieldValues {
if fieldValue != "" && rule[fieldIndex+i] != fieldValue {
matched = false
break
}
}
if matched {
effects = append(effects, rule)
} else {
tmp = append(tmp, rule)
model[sec][ptype].PolicyMap[strings.Join(rule, DefaultSep)] = len(tmp) - 1
}
}
if len(tmp) != len(model[sec][ptype].Policy) {
model[sec][ptype].Policy = tmp
res = true
}
return res, effects, nil
}
// GetValuesForFieldInPolicy gets all values for a field for all rules in a policy, duplicated values are removed.
func (model Model) GetValuesForFieldInPolicy(sec string, ptype string, fieldIndex int) ([]string, error) {
values := []string{}
_, err := model.GetAssertion(sec, ptype)
if err != nil {
return nil, err
}
for _, rule := range model[sec][ptype].Policy {
values = append(values, rule[fieldIndex])
}
util.ArrayRemoveDuplicates(&values)
return values, nil
}
// GetValuesForFieldInPolicyAllTypes gets all values for a field for all rules in a policy of all ptypes, duplicated values are removed.
func (model Model) GetValuesForFieldInPolicyAllTypes(sec string, fieldIndex int) ([]string, error) {
values := []string{}
for ptype := range model[sec] {
v, err := model.GetValuesForFieldInPolicy(sec, ptype, fieldIndex)
if err != nil {
return nil, err
}
values = append(values, v...)
}
util.ArrayRemoveDuplicates(&values)
return values, nil
}
// GetValuesForFieldInPolicyAllTypesByName gets all values for a field for all rules in a policy of all ptypes, duplicated values are removed.
func (model Model) GetValuesForFieldInPolicyAllTypesByName(sec string, field string) ([]string, error) {
values := []string{}
for ptype := range model[sec] {
// GetFieldIndex will return (-1, err) if field is not found, ignore it
index, err := model.GetFieldIndex(ptype, field)
if err != nil {
continue
}
v, err := model.GetValuesForFieldInPolicy(sec, ptype, index)
if err != nil {
return nil, err
}
values = append(values, v...)
}
util.ArrayRemoveDuplicates(&values)
return values, nil
}