2021-03-05 04:18:31 +00:00
|
|
|
package authorization
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
|
2021-08-11 01:04:35 +00:00
|
|
|
"github.com/authelia/authelia/v4/internal/authentication"
|
|
|
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
2021-03-05 04:18:31 +00:00
|
|
|
)
|
|
|
|
|
2022-12-17 12:39:24 +00:00
|
|
|
// NewLevel converts a string policy to int authorization level.
|
|
|
|
func NewLevel(policy string) Level {
|
2021-03-05 04:18:31 +00:00
|
|
|
switch policy {
|
2021-06-18 01:38:01 +00:00
|
|
|
case bypass:
|
2021-03-05 04:18:31 +00:00
|
|
|
return Bypass
|
2021-06-18 01:38:01 +00:00
|
|
|
case oneFactor:
|
2021-03-05 04:18:31 +00:00
|
|
|
return OneFactor
|
2021-06-18 01:38:01 +00:00
|
|
|
case twoFactor:
|
2021-03-05 04:18:31 +00:00
|
|
|
return TwoFactor
|
2021-06-18 01:38:01 +00:00
|
|
|
case deny:
|
2021-03-05 04:18:31 +00:00
|
|
|
return Denied
|
|
|
|
}
|
|
|
|
// By default the deny policy applies.
|
|
|
|
return Denied
|
|
|
|
}
|
|
|
|
|
2022-12-17 12:39:24 +00:00
|
|
|
// String returns a policy string representation of an authorization.Level.
|
|
|
|
func (l Level) String() string {
|
|
|
|
switch l {
|
2022-02-28 03:15:01 +00:00
|
|
|
case Bypass:
|
|
|
|
return bypass
|
|
|
|
case OneFactor:
|
|
|
|
return oneFactor
|
|
|
|
case TwoFactor:
|
|
|
|
return twoFactor
|
|
|
|
case Denied:
|
|
|
|
return deny
|
2022-12-17 12:39:24 +00:00
|
|
|
default:
|
|
|
|
return deny
|
2022-02-28 03:15:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-01 11:38:49 +00:00
|
|
|
func stringSliceToRegexpSlice(strings []string) (regexps []regexp.Regexp, err error) {
|
|
|
|
for _, str := range strings {
|
|
|
|
pattern, err := regexp.Compile(str)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
regexps = append(regexps, *pattern)
|
|
|
|
}
|
|
|
|
|
|
|
|
return regexps, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func schemaSubjectToACLSubject(subjectRule string) (subject SubjectMatcher) {
|
|
|
|
if strings.HasPrefix(subjectRule, prefixUser) {
|
|
|
|
user := strings.Trim(subjectRule[len(prefixUser):], " ")
|
2021-03-05 04:18:31 +00:00
|
|
|
|
|
|
|
return AccessControlUser{Name: user}
|
|
|
|
}
|
|
|
|
|
2022-04-01 11:38:49 +00:00
|
|
|
if strings.HasPrefix(subjectRule, prefixGroup) {
|
|
|
|
group := strings.Trim(subjectRule[len(prefixGroup):], " ")
|
2021-03-05 04:18:31 +00:00
|
|
|
|
|
|
|
return AccessControlGroup{Name: group}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-18 08:14:34 +00:00
|
|
|
func ruleAddDomain(domainRules []string, rule *AccessControlRule) {
|
2021-03-05 04:18:31 +00:00
|
|
|
for _, domainRule := range domainRules {
|
2022-10-18 08:14:34 +00:00
|
|
|
subjects, r := NewAccessControlDomain(domainRule)
|
|
|
|
|
|
|
|
rule.Domains = append(rule.Domains, r)
|
2021-03-05 04:18:31 +00:00
|
|
|
|
2022-10-18 08:14:34 +00:00
|
|
|
if !rule.HasSubjects && subjects {
|
|
|
|
rule.HasSubjects = true
|
|
|
|
}
|
|
|
|
}
|
2021-03-05 04:18:31 +00:00
|
|
|
}
|
|
|
|
|
2022-09-26 04:33:08 +00:00
|
|
|
func ruleAddDomainRegex(exps []regexp.Regexp, rule *AccessControlRule) {
|
|
|
|
for _, exp := range exps {
|
|
|
|
subjects, r := NewAccessControlDomainRegex(exp)
|
|
|
|
|
|
|
|
rule.Domains = append(rule.Domains, r)
|
|
|
|
|
|
|
|
if !rule.HasSubjects && subjects {
|
|
|
|
rule.HasSubjects = true
|
|
|
|
}
|
2021-03-05 04:18:31 +00:00
|
|
|
}
|
2022-09-26 04:33:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func ruleAddResources(exps []regexp.Regexp, rule *AccessControlRule) {
|
|
|
|
for _, exp := range exps {
|
|
|
|
subjects, r := NewAccessControlResource(exp)
|
|
|
|
|
|
|
|
rule.Resources = append(rule.Resources, r)
|
2021-03-05 04:18:31 +00:00
|
|
|
|
2022-09-26 04:33:08 +00:00
|
|
|
if !rule.HasSubjects && subjects {
|
|
|
|
rule.HasSubjects = true
|
|
|
|
}
|
|
|
|
}
|
2021-03-05 04:18:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func schemaMethodsToACL(methodRules []string) (methods []string) {
|
|
|
|
for _, method := range methodRules {
|
|
|
|
methods = append(methods, strings.ToUpper(method))
|
|
|
|
}
|
|
|
|
|
|
|
|
return methods
|
|
|
|
}
|
|
|
|
|
|
|
|
func schemaNetworksToACL(networkRules []string, networksMap map[string][]*net.IPNet, networksCacheMap map[string]*net.IPNet) (networks []*net.IPNet) {
|
|
|
|
for _, network := range networkRules {
|
|
|
|
if _, ok := networksMap[network]; !ok {
|
|
|
|
if _, ok := networksCacheMap[network]; ok {
|
|
|
|
networks = append(networks, networksCacheMap[network])
|
|
|
|
} else {
|
|
|
|
cidr, err := parseNetwork(network)
|
|
|
|
if err == nil {
|
|
|
|
networks = append(networks, cidr)
|
|
|
|
networksCacheMap[cidr.String()] = cidr
|
|
|
|
|
|
|
|
if cidr.String() != network {
|
|
|
|
networksCacheMap[network] = cidr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
networks = append(networks, networksMap[network]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return networks
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseSchemaNetworks(schemaNetworks []schema.ACLNetwork) (networksMap map[string][]*net.IPNet, networksCacheMap map[string]*net.IPNet) {
|
|
|
|
// These maps store pointers to the net.IPNet values so we can reuse them efficiently.
|
|
|
|
// The networksMap contains the named networks as keys, the networksCacheMap contains the CIDR notations as keys.
|
|
|
|
networksMap = map[string][]*net.IPNet{}
|
|
|
|
networksCacheMap = map[string]*net.IPNet{}
|
|
|
|
|
|
|
|
for _, aclNetwork := range schemaNetworks {
|
|
|
|
var networks []*net.IPNet
|
|
|
|
|
|
|
|
for _, networkRule := range aclNetwork.Networks {
|
|
|
|
cidr, err := parseNetwork(networkRule)
|
|
|
|
if err == nil {
|
|
|
|
networks = append(networks, cidr)
|
|
|
|
networksCacheMap[cidr.String()] = cidr
|
|
|
|
|
|
|
|
if cidr.String() != networkRule {
|
|
|
|
networksCacheMap[networkRule] = cidr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := networksMap[aclNetwork.Name]; len(networks) != 0 && !ok {
|
|
|
|
networksMap[aclNetwork.Name] = networks
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return networksMap, networksCacheMap
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseNetwork(networkRule string) (cidr *net.IPNet, err error) {
|
|
|
|
if !strings.Contains(networkRule, "/") {
|
|
|
|
ip := net.ParseIP(networkRule)
|
|
|
|
if ip.To4() != nil {
|
|
|
|
_, cidr, err = net.ParseCIDR(networkRule + "/32")
|
|
|
|
} else {
|
|
|
|
_, cidr, err = net.ParseCIDR(networkRule + "/128")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_, cidr, err = net.ParseCIDR(networkRule)
|
|
|
|
}
|
|
|
|
|
|
|
|
return cidr, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func schemaSubjectsToACL(subjectRules [][]string) (subjects []AccessControlSubjects) {
|
|
|
|
for _, subjectRule := range subjectRules {
|
|
|
|
subject := AccessControlSubjects{}
|
|
|
|
|
|
|
|
for _, subjectRuleItem := range subjectRule {
|
|
|
|
subject.AddSubject(subjectRuleItem)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(subject.Subjects) != 0 {
|
|
|
|
subjects = append(subjects, subject)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return subjects
|
|
|
|
}
|
|
|
|
|
|
|
|
func domainToPrefixSuffix(domain string) (prefix, suffix string) {
|
|
|
|
parts := strings.Split(domain, ".")
|
|
|
|
|
|
|
|
if len(parts) == 1 {
|
|
|
|
return "", parts[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
return parts[0], strings.Join(parts[1:], ".")
|
|
|
|
}
|
2021-05-04 22:06:05 +00:00
|
|
|
|
|
|
|
// IsAuthLevelSufficient returns true if the current authenticationLevel is above the authorizationLevel.
|
|
|
|
func IsAuthLevelSufficient(authenticationLevel authentication.Level, authorizationLevel Level) bool {
|
|
|
|
switch authorizationLevel {
|
|
|
|
case Denied:
|
|
|
|
return false
|
|
|
|
case OneFactor:
|
|
|
|
return authenticationLevel >= authentication.OneFactor
|
|
|
|
case TwoFactor:
|
|
|
|
return authenticationLevel >= authentication.TwoFactor
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|