summaryrefslogtreecommitdiff
path: root/internal/authorization/authorizer.go
blob: d08ec3a5b95a87611eeda02126b378a7bfd7eb75 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package authorization

import (
	"github.com/authelia/authelia/v4/internal/configuration/schema"
	"github.com/authelia/authelia/v4/internal/logging"
)

// Authorizer the component in charge of checking whether a user can access a given resource.
type Authorizer struct {
	defaultPolicy Level
	rules         []*AccessControlRule
	mfa           bool
	configuration *schema.Configuration
}

// NewAuthorizer create an instance of authorizer with a given access control configuration.
func NewAuthorizer(configuration *schema.Configuration) (authorizer *Authorizer) {
	authorizer = &Authorizer{
		defaultPolicy: StringToLevel(configuration.AccessControl.DefaultPolicy),
		rules:         NewAccessControlRules(configuration.AccessControl),
		configuration: configuration,
	}

	if authorizer.defaultPolicy == TwoFactor {
		authorizer.mfa = true

		return authorizer
	}

	for _, rule := range authorizer.rules {
		if rule.Policy == TwoFactor {
			authorizer.mfa = true

			return authorizer
		}
	}

	if authorizer.configuration.IdentityProviders.OIDC != nil {
		for _, client := range authorizer.configuration.IdentityProviders.OIDC.Clients {
			if client.Policy == twoFactor {
				authorizer.mfa = true

				return authorizer
			}
		}
	}

	return authorizer
}

// IsSecondFactorEnabled return true if at least one policy is set to second factor.
func (p Authorizer) IsSecondFactorEnabled() bool {
	return p.mfa
}

// GetRequiredLevel retrieve the required level of authorization to access the object.
func (p Authorizer) GetRequiredLevel(subject Subject, object Object) (bool, Level) {
	logger := logging.Logger()

	logger.Debugf("Check authorization of subject %s and object %s (method %s).",
		subject.String(), object.String(), object.Method)

	for _, rule := range p.rules {
		if rule.IsMatch(subject, object) {
			logger.Tracef(traceFmtACLHitMiss, "HIT", rule.Position, subject.String(), object.String(), object.Method)

			return len(rule.Subjects) > 0, rule.Policy
		}

		logger.Tracef(traceFmtACLHitMiss, "MISS", rule.Position, subject.String(), object.String(), object.Method)
	}

	logger.Debugf("No matching rule for subject %s and url %s... Applying default policy.",
		subject.String(), object.String())

	return false, p.defaultPolicy
}

// GetRuleMatchResults iterates through the rules and produces a list of RuleMatchResult provided a subject and object.
func (p Authorizer) GetRuleMatchResults(subject Subject, object Object) (results []RuleMatchResult) {
	skipped := false

	results = make([]RuleMatchResult, len(p.rules))

	for i, rule := range p.rules {
		results[i] = RuleMatchResult{
			Rule:    rule,
			Skipped: skipped,

			MatchDomain:        isMatchForDomains(subject, object, rule),
			MatchResources:     isMatchForResources(subject, object, rule),
			MatchMethods:       isMatchForMethods(object, rule),
			MatchNetworks:      isMatchForNetworks(subject, rule),
			MatchSubjects:      isMatchForSubjects(subject, rule),
			MatchSubjectsExact: isExactMatchForSubjects(subject, rule),
		}

		skipped = skipped || results[i].IsMatch()
	}

	return results
}