authelia/internal/configuration/schema/access_control.go
Dustin Sweigart 951dc71325
[FEATURE] Support multiple domains and multiple subjects in ACLs (#869)
* added support for listing multiple domains and multiple subjects

* updated documentation to show use of multiple domains and subjects

* updated config.template.yml to display multiple domains as a list

* updated config.template.yml to display multiple subjects as a list

* updated docs/configuration/access-control.md to display multiple domains as a list

* updated docs/configuration/access-control.md to display multiple subjects as a list

* removed redundant check that always returned true

* Commentary definition for `weak`
2020-04-16 10:18:11 +10:00

75 lines
2.1 KiB
Go

package schema
import (
"fmt"
"net"
"strings"
)
// ACLRule represent one ACL rule
// "weak" coerces a single value into string slice
type ACLRule struct {
Domains []string `mapstructure:"domain,weak"`
Policy string `mapstructure:"policy"`
Subjects []string `mapstructure:"subject,weak"`
Networks []string `mapstructure:"networks"`
Resources []string `mapstructure:"resources"`
}
// IsPolicyValid check if policy is valid
func IsPolicyValid(policy string) bool {
return policy == "deny" || policy == "one_factor" || policy == "two_factor" || policy == "bypass"
}
// IsSubjectValid check if a subject is valid
func IsSubjectValid(subject string) bool {
return subject == "" || strings.HasPrefix(subject, "user:") || strings.HasPrefix(subject, "group:")
}
// IsNetworkValid check if a network is valid
func IsNetworkValid(network string) bool {
_, _, err := net.ParseCIDR(network)
return err == nil
}
// Validate validate an ACL Rule
func (r *ACLRule) Validate(validator *StructValidator) {
if len(r.Domains) == 0 {
validator.Push(fmt.Errorf("Domain must be provided"))
}
if !IsPolicyValid(r.Policy) {
validator.Push(fmt.Errorf("A policy must either be 'deny', 'two_factor', 'one_factor' or 'bypass'"))
}
for i, subject := range r.Subjects {
if !IsSubjectValid(subject) {
validator.Push(fmt.Errorf("Subject %d must start with 'user:' or 'group:'", i))
}
}
for i, network := range r.Networks {
if !IsNetworkValid(network) {
validator.Push(fmt.Errorf("Network %d must be a valid CIDR", i))
}
}
}
// AccessControlConfiguration represents the configuration related to ACLs.
type AccessControlConfiguration struct {
DefaultPolicy string `mapstructure:"default_policy"`
Rules []ACLRule `mapstructure:"rules"`
}
// Validate validate the access control configuration
func (acc *AccessControlConfiguration) Validate(validator *StructValidator) {
if acc.DefaultPolicy == "" {
acc.DefaultPolicy = "deny"
}
if !IsPolicyValid(acc.DefaultPolicy) {
validator.Push(fmt.Errorf("'default_policy' must either be 'deny', 'two_factor', 'one_factor' or 'bypass'"))
}
}