diff --git a/enforcer_interface.go b/enforcer_interface.go index 94baf84e..8c1c4655 100644 --- a/enforcer_interface.go +++ b/enforcer_interface.go @@ -108,10 +108,14 @@ type IEnforcer interface { GetFilteredPolicy(fieldIndex int, fieldValues ...string) ([][]string, error) GetNamedPolicy(ptype string) ([][]string, error) GetFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) ([][]string, error) + GetStrictFilteredPolicy(fieldIndex int, fieldValues ...string) ([][]string, error) + GetStrictFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) ([][]string, error) GetGroupingPolicy() ([][]string, error) GetFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) ([][]string, error) GetNamedGroupingPolicy(ptype string) ([][]string, error) GetFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) ([][]string, error) + GetStrictFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) ([][]string, error) + GetStrictFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) ([][]string, error) HasPolicy(params ...interface{}) (bool, error) HasNamedPolicy(ptype string, params ...interface{}) (bool, error) AddPolicy(params ...interface{}) (bool, error) diff --git a/enforcer_synced.go b/enforcer_synced.go index 89bbe5da..33ed388c 100644 --- a/enforcer_synced.go +++ b/enforcer_synced.go @@ -320,6 +320,24 @@ func (e *SyncedEnforcer) GetFilteredNamedPolicy(ptype string, fieldIndex int, fi return e.Enforcer.GetFilteredNamedPolicy(ptype, fieldIndex, fieldValues...) } +// GetStrictFilteredPolicy gets all the authorization rules in the policy, field filters can be specified. +// In contrast to GetFilteredPolicy, "" (empty string) is treated as a literal empty string match, +// and "*" is treated as a wildcard that matches any value. +func (e *SyncedEnforcer) GetStrictFilteredPolicy(fieldIndex int, fieldValues ...string) ([][]string, error) { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetStrictFilteredPolicy(fieldIndex, fieldValues...) +} + +// GetStrictFilteredNamedPolicy gets all the authorization rules in the named policy, field filters can be specified. +// In contrast to GetFilteredNamedPolicy, "" (empty string) is treated as a literal empty string match, +// and "*" is treated as a wildcard that matches any value. +func (e *SyncedEnforcer) GetStrictFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) ([][]string, error) { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetStrictFilteredNamedPolicy(ptype, fieldIndex, fieldValues...) +} + // GetGroupingPolicy gets all the role inheritance rules in the policy. func (e *SyncedEnforcer) GetGroupingPolicy() ([][]string, error) { e.m.RLock() @@ -348,6 +366,24 @@ func (e *SyncedEnforcer) GetFilteredNamedGroupingPolicy(ptype string, fieldIndex return e.Enforcer.GetFilteredNamedGroupingPolicy(ptype, fieldIndex, fieldValues...) } +// GetStrictFilteredGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. +// In contrast to GetFilteredGroupingPolicy, "" (empty string) is treated as a literal empty string match, +// and "*" is treated as a wildcard that matches any value. +func (e *SyncedEnforcer) GetStrictFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) ([][]string, error) { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetStrictFilteredGroupingPolicy(fieldIndex, fieldValues...) +} + +// GetStrictFilteredNamedGroupingPolicy gets all the role inheritance rules in the named policy, field filters can be specified. +// In contrast to GetFilteredNamedGroupingPolicy, "" (empty string) is treated as a literal empty string match, +// and "*" is treated as a wildcard that matches any value. +func (e *SyncedEnforcer) GetStrictFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) ([][]string, error) { + e.m.RLock() + defer e.m.RUnlock() + return e.Enforcer.GetStrictFilteredNamedGroupingPolicy(ptype, fieldIndex, fieldValues...) +} + // HasPolicy determines whether an authorization rule exists. func (e *SyncedEnforcer) HasPolicy(params ...interface{}) (bool, error) { e.m.RLock() diff --git a/management_api.go b/management_api.go index 7a8f768e..08bebd90 100644 --- a/management_api.go +++ b/management_api.go @@ -113,6 +113,20 @@ func (e *Enforcer) GetFilteredNamedPolicy(ptype string, fieldIndex int, fieldVal return e.model.GetFilteredPolicy("p", ptype, fieldIndex, fieldValues...) } +// GetStrictFilteredPolicy gets all the authorization rules in the policy, field filters can be specified. +// In contrast to GetFilteredPolicy, "" (empty string) is treated as a literal empty string match, +// and "*" is treated as a wildcard that matches any value. +func (e *Enforcer) GetStrictFilteredPolicy(fieldIndex int, fieldValues ...string) ([][]string, error) { + return e.GetStrictFilteredNamedPolicy("p", fieldIndex, fieldValues...) +} + +// GetStrictFilteredNamedPolicy gets all the authorization rules in the named policy, field filters can be specified. +// In contrast to GetFilteredNamedPolicy, "" (empty string) is treated as a literal empty string match, +// and "*" is treated as a wildcard that matches any value. +func (e *Enforcer) GetStrictFilteredNamedPolicy(ptype string, fieldIndex int, fieldValues ...string) ([][]string, error) { + return e.model.GetStrictFilteredPolicy("p", ptype, fieldIndex, fieldValues...) +} + // GetGroupingPolicy gets all the role inheritance rules in the policy. func (e *Enforcer) GetGroupingPolicy() ([][]string, error) { return e.GetNamedGroupingPolicy("g") @@ -133,6 +147,20 @@ func (e *Enforcer) GetFilteredNamedGroupingPolicy(ptype string, fieldIndex int, return e.model.GetFilteredPolicy("g", ptype, fieldIndex, fieldValues...) } +// GetStrictFilteredGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. +// In contrast to GetFilteredGroupingPolicy, "" (empty string) is treated as a literal empty string match, +// and "*" is treated as a wildcard that matches any value. +func (e *Enforcer) GetStrictFilteredGroupingPolicy(fieldIndex int, fieldValues ...string) ([][]string, error) { + return e.GetStrictFilteredNamedGroupingPolicy("g", fieldIndex, fieldValues...) +} + +// GetStrictFilteredNamedGroupingPolicy gets all the role inheritance rules in the named policy, field filters can be specified. +// In contrast to GetFilteredNamedGroupingPolicy, "" (empty string) is treated as a literal empty string match, +// and "*" is treated as a wildcard that matches any value. +func (e *Enforcer) GetStrictFilteredNamedGroupingPolicy(ptype string, fieldIndex int, fieldValues ...string) ([][]string, error) { + return e.model.GetStrictFilteredPolicy("g", ptype, fieldIndex, fieldValues...) +} + // GetFilteredNamedPolicyWithMatcher gets rules based on matcher from the policy. func (e *Enforcer) GetFilteredNamedPolicyWithMatcher(ptype string, matcher string) ([][]string, error) { var res [][]string diff --git a/management_api_test.go b/management_api_test.go index 620b3944..0ebc3620 100644 --- a/management_api_test.go +++ b/management_api_test.go @@ -199,6 +199,51 @@ func TestGetPolicyAPI(t *testing.T) { testHasGroupingPolicy(t, e, []string{"bob", "data2_admin"}, false) } +func TestGetStrictFilteredPolicyAPI(t *testing.T) { + e, _ := NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv") + + // Add a policy with an empty string field to test strict matching. + _, _ = e.AddPolicy("", "data1", "read") + + // "*" acts as wildcard - matches any value in the field. + myRes, err := e.GetStrictFilteredPolicy(0, "*") + if err != nil { + t.Error(err) + } + // All rules (alice and the empty-subject rule) should be returned. + if len(myRes) != 3 { + t.Errorf("Expected 3 rules with wildcard, got %d: %v", len(myRes), myRes) + } + + // "" matches only rules where the subject is literally an empty string. + myRes, err = e.GetStrictFilteredPolicy(0, "") + if err != nil { + t.Error(err) + } + if len(myRes) != 1 || myRes[0][0] != "" { + t.Errorf("Expected 1 rule with empty subject, got %d: %v", len(myRes), myRes) + } + + // "alice" matches only the alice rule. + myRes, err = e.GetStrictFilteredPolicy(0, "alice") + if err != nil { + t.Error(err) + } + if len(myRes) != 1 || myRes[0][0] != "alice" { + t.Errorf("Expected 1 rule for alice, got %d: %v", len(myRes), myRes) + } + + // Mix: "*" wildcard for subject, specific action. + myRes, err = e.GetStrictFilteredPolicy(0, "*", "data1", "read") + if err != nil { + t.Error(err) + } + // Both alice->data1->read and ""->data1->read should match. + if len(myRes) != 2 { + t.Errorf("Expected 2 rules, got %d: %v", len(myRes), myRes) + } +} + func TestModifyPolicyAPI(t *testing.T) { e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") diff --git a/model/policy.go b/model/policy.go index e55bf410..695ba0af 100644 --- a/model/policy.go +++ b/model/policy.go @@ -141,6 +141,37 @@ func (model Model) GetFilteredPolicy(sec string, ptype string, fieldIndex int, f return res, nil } +// GetStrictFilteredPolicy gets rules based on field filters from a policy. +// In contrast to GetFilteredPolicy, "" (empty string) is treated as a literal empty string match, +// and "*" is treated as a wildcard that matches any value. +func (model Model) GetStrictFilteredPolicy(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 fieldIndex+i >= len(rule) { + matched = false + break + } + 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)