package authorization import ( "net" "regexp" "strings" "github.com/authelia/authelia/v4/internal/authentication" "github.com/authelia/authelia/v4/internal/configuration/schema" ) // PolicyToLevel converts a string policy to int authorization level. func PolicyToLevel(policy string) Level { switch policy { case bypass: return Bypass case oneFactor: return OneFactor case twoFactor: return TwoFactor case deny: return Denied } // By default the deny policy applies. return Denied } // LevelToPolicy converts a int authorization level to string policy. func LevelToPolicy(level Level) (policy string) { switch level { case Bypass: return bypass case OneFactor: return oneFactor case TwoFactor: return twoFactor case Denied: return deny } return deny } func schemaSubjectToACLSubject(subjectRule string) (subject AccessControlSubject) { if strings.HasPrefix(subjectRule, userPrefix) { user := strings.Trim(subjectRule[len(userPrefix):], " ") return AccessControlUser{Name: user} } if strings.HasPrefix(subjectRule, groupPrefix) { group := strings.Trim(subjectRule[len(groupPrefix):], " ") return AccessControlGroup{Name: group} } return nil } func schemaDomainsToACL(domainRules []string) (domains []AccessControlDomain) { for _, domainRule := range domainRules { domain := AccessControlDomain{} domainRule = strings.ToLower(domainRule) switch { case strings.HasPrefix(domainRule, "*."): domain.Wildcard = true domain.Name = domainRule[1:] case strings.HasPrefix(domainRule, "{user}"): domain.UserWildcard = true domain.Name = domainRule[7:] case strings.HasPrefix(domainRule, "{group}"): domain.GroupWildcard = true domain.Name = domainRule[8:] default: domain.Name = domainRule } domains = append(domains, domain) } return domains } func schemaResourcesToACL(resourceRules []string) (resources []AccessControlResource) { for _, resourceRule := range resourceRules { resources = append(resources, AccessControlResource{regexp.MustCompile(resourceRule)}) } return resources } 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:], ".") } // 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 }