package authorization import ( "fmt" "regexp" "strings" "github.com/authelia/authelia/v4/internal/utils" ) // NewAccessControlDomain creates a new SubjectObjectMatcher that matches the domain as a basic string. func NewAccessControlDomain(domain string) SubjectObjectMatcher { d := AccessControlDomain{} domain = strings.ToLower(domain) switch { case strings.HasPrefix(domain, "*."): d.Wildcard = true d.Name = domain[1:] case strings.HasPrefix(domain, "{user}"): d.UserWildcard = true d.Name = domain[7:] case strings.HasPrefix(domain, "{group}"): d.GroupWildcard = true d.Name = domain[8:] default: d.Name = domain } return d } // AccessControlDomain represents an ACL domain. type AccessControlDomain struct { Name string Wildcard bool UserWildcard bool GroupWildcard bool } // IsMatch returns true if the ACL domain matches the object domain. func (acl AccessControlDomain) IsMatch(subject Subject, object Object) (match bool) { switch { case acl.Wildcard: return strings.HasSuffix(object.Domain, acl.Name) case acl.UserWildcard: return object.Domain == fmt.Sprintf("%s.%s", subject.Username, acl.Name) case acl.GroupWildcard: prefix, suffix := domainToPrefixSuffix(object.Domain) return suffix == acl.Name && utils.IsStringInSliceFold(prefix, subject.Groups) default: return object.Domain == acl.Name } } // String returns a string representation of the SubjectObjectMatcher rule. func (acl AccessControlDomain) String() string { return fmt.Sprintf("domain:%s", acl.Name) } // NewAccessControlDomainRegex creates a new SubjectObjectMatcher that matches the domain either in a basic way or // dynamic User/Group subexpression group way. func NewAccessControlDomainRegex(pattern regexp.Regexp) SubjectObjectMatcher { var iuser, igroup = -1, -1 for i, group := range pattern.SubexpNames() { switch group { case subexpNameUser: iuser = i case subexpNameGroup: igroup = i } } if iuser != -1 || igroup != -1 { return AccessControlDomainRegex{Pattern: pattern, SubexpNameUser: iuser, SubexpNameGroup: igroup} } return AccessControlDomainRegexBasic{Pattern: pattern} } // AccessControlDomainRegexBasic represents a basic domain regex SubjectObjectMatcher. type AccessControlDomainRegexBasic struct { Pattern regexp.Regexp } // IsMatch returns true if the ACL regex matches the object domain. func (acl AccessControlDomainRegexBasic) IsMatch(_ Subject, object Object) (match bool) { return acl.Pattern.MatchString(object.Domain) } // String returns a text representation of a AccessControlDomainRegexBasic. func (acl AccessControlDomainRegexBasic) String() string { return fmt.Sprintf("domain_regex:%s", acl.Pattern.String()) } // AccessControlDomainRegex represents an ACL domain regex. type AccessControlDomainRegex struct { Pattern regexp.Regexp SubexpNameUser int SubexpNameGroup int } // IsMatch returns true if the ACL regex matches the object domain. func (acl AccessControlDomainRegex) IsMatch(subject Subject, object Object) (match bool) { matches := acl.Pattern.FindAllStringSubmatch(object.Domain, -1) if matches == nil { return false } if acl.SubexpNameUser != -1 && !strings.EqualFold(subject.Username, matches[0][acl.SubexpNameUser]) { return false } if acl.SubexpNameGroup != -1 && !utils.IsStringInSliceFold(matches[0][acl.SubexpNameGroup], subject.Groups) { return false } return true } // String returns a text representation of a AccessControlDomainRegex. func (acl AccessControlDomainRegex) String() string { return fmt.Sprintf("domain_regex(subexp):%s", acl.Pattern.String()) }