authelia/internal/authorization/util.go
James Elliott ddea31193b
feature(oidc): add support for OpenID Connect
OpenID connect has become a standard when it comes to authentication and
in order to fix a security concern around forwarding authentication and authorization information
it has been decided to add support for it.

This feature is in beta version and only enabled when there is a configuration for it.
Before enabling it in production, please consider that it's in beta with potential bugs and that there
are several production critical features still missing such as all OIDC related data is stored in
configuration or memory. This means you are potentially going to experience issues with HA
deployments, or when restarting a single instance specifically related to OIDC.

We are still working on adding the remaining set of features before making it GA as soon as possible.

Related to #189

Co-authored-by: Clement Michaud <clement.michaud34@gmail.com>
2021-05-05 00:15:36 +02:00

193 lines
4.9 KiB
Go

package authorization
import (
"net"
"regexp"
"strings"
"github.com/authelia/authelia/internal/authentication"
"github.com/authelia/authelia/internal/configuration/schema"
)
// PolicyToLevel converts a string policy to int authorization level.
func PolicyToLevel(policy string) Level {
switch policy {
case "bypass":
return Bypass
case "one_factor":
return OneFactor
case "two_factor":
return TwoFactor
case "deny":
return Denied
}
// By default the deny policy applies.
return Denied
}
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
}