fix(authorization): subject wildcard domain rule not matching (#4187)

This fixes an issue where the subject wildcard domain rules (those containing {user} and {group}) are not considered matches even though they may be once a user authenticates.

Fixes #4186
pull/4167/head^2
James Elliott 2022-10-18 19:14:34 +11:00 committed by GitHub
parent bcdbedee11
commit a4edf21320
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 18 deletions

View File

@ -9,7 +9,7 @@ import (
)
// NewAccessControlDomain creates a new SubjectObjectMatcher that matches the domain as a basic string.
func NewAccessControlDomain(domain string) AccessControlDomain {
func NewAccessControlDomain(domain string) (subjcets bool, rule AccessControlDomain) {
m := &AccessControlDomainMatcher{}
domain = strings.ToLower(domain)
@ -19,15 +19,15 @@ func NewAccessControlDomain(domain string) AccessControlDomain {
m.Name = domain[1:]
case strings.HasPrefix(domain, "{user}"):
m.UserWildcard = true
m.Name = domain[7:]
m.Name = domain[6:]
case strings.HasPrefix(domain, "{group}"):
m.GroupWildcard = true
m.Name = domain[8:]
m.Name = domain[7:]
default:
m.Name = domain
}
return AccessControlDomain{m}
return m.UserWildcard || m.GroupWildcard, AccessControlDomain{m}
}
// NewAccessControlDomainRegex creates a new SubjectObjectMatcher that matches the domain either in a basic way or
@ -65,11 +65,19 @@ func (m AccessControlDomainMatcher) IsMatch(domain string, subject Subject) (mat
case m.Wildcard:
return strings.HasSuffix(domain, m.Name)
case m.UserWildcard:
return domain == fmt.Sprintf("%s.%s", subject.Username, m.Name)
case m.GroupWildcard:
prefix, suffix := domainToPrefixSuffix(domain)
if subject.IsAnonymous() && strings.HasSuffix(domain, m.Name) {
return true
}
return suffix == m.Name && utils.IsStringInSliceFold(prefix, subject.Groups)
return domain == fmt.Sprintf("%s%s", subject.Username, m.Name)
case m.GroupWildcard:
if subject.IsAnonymous() && strings.HasSuffix(domain, m.Name) {
return true
}
i := strings.Index(domain, ".")
return domain[i:] == m.Name && utils.IsStringInSliceFold(domain[:i], subject.Groups)
default:
return strings.EqualFold(domain, m.Name)
}

View File

@ -22,7 +22,6 @@ func NewAccessControlRules(config schema.AccessControlConfiguration) (rules []*A
func NewAccessControlRule(pos int, rule schema.ACLRule, networksMap map[string][]*net.IPNet, networksCacheMap map[string]*net.IPNet) *AccessControlRule {
r := &AccessControlRule{
Position: pos,
Domains: schemaDomainsToACL(rule.Domains),
Methods: schemaMethodsToACL(rule.Methods),
Networks: schemaNetworksToACL(rule.Networks, networksMap, networksCacheMap),
Subjects: schemaSubjectsToACL(rule.Subjects),
@ -33,6 +32,7 @@ func NewAccessControlRule(pos int, rule schema.ACLRule, networksMap map[string][
r.HasSubjects = true
}
ruleAddDomain(rule.Domains, r)
ruleAddDomainRegex(rule.DomainsRegex, r)
ruleAddResources(rule.Resources, r)

View File

@ -151,17 +151,17 @@ func (s *AuthorizerSuite) TestShouldCheckDynamicDomainRules() {
WithDefaultPolicy(deny).
WithRule(schema.ACLRule{
Domains: []string{"{user}.example.com"},
Policy: bypass,
Policy: oneFactor,
}).
WithRule(schema.ACLRule{
Domains: []string{"{group}.example.com"},
Policy: bypass,
Policy: oneFactor,
}).
Build()
tester.CheckAuthorizations(s.T(), UserWithGroups, "https://john.example.com/", "GET", Bypass)
tester.CheckAuthorizations(s.T(), UserWithGroups, "https://dev.example.com/", "GET", Bypass)
tester.CheckAuthorizations(s.T(), UserWithGroups, "https://admins.example.com/", "GET", Bypass)
tester.CheckAuthorizations(s.T(), UserWithGroups, "https://john.example.com/", "GET", OneFactor)
tester.CheckAuthorizations(s.T(), UserWithGroups, "https://dev.example.com/", "GET", OneFactor)
tester.CheckAuthorizations(s.T(), UserWithGroups, "https://admins.example.com/", "GET", OneFactor)
tester.CheckAuthorizations(s.T(), UserWithGroups, "https://othergroup.example.com/", "GET", Denied)
}

View File

@ -70,12 +70,16 @@ func schemaSubjectToACLSubject(subjectRule string) (subject SubjectMatcher) {
return nil
}
func schemaDomainsToACL(domainRules []string) (domains []AccessControlDomain) {
func ruleAddDomain(domainRules []string, rule *AccessControlRule) {
for _, domainRule := range domainRules {
domains = append(domains, NewAccessControlDomain(domainRule))
}
subjects, r := NewAccessControlDomain(domainRule)
return domains
rule.Domains = append(rule.Domains, r)
if !rule.HasSubjects && subjects {
rule.HasSubjects = true
}
}
}
func ruleAddDomainRegex(exps []regexp.Regexp, rule *AccessControlRule) {