2021-01-04 10:55:23 +00:00
|
|
|
package validator
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
2022-10-19 03:09:22 +00:00
|
|
|
"regexp"
|
2021-01-04 10:55:23 +00:00
|
|
|
"strings"
|
|
|
|
|
2022-04-01 11:38:49 +00:00
|
|
|
"github.com/authelia/authelia/v4/internal/authorization"
|
2021-08-11 01:04:35 +00:00
|
|
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
|
|
|
"github.com/authelia/authelia/v4/internal/utils"
|
2021-01-04 10:55:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// IsPolicyValid check if policy is valid.
|
2021-03-05 04:18:31 +00:00
|
|
|
func IsPolicyValid(policy string) (isValid bool) {
|
2022-02-28 03:15:01 +00:00
|
|
|
return utils.IsStringInSlice(policy, validACLRulePolicies)
|
2021-01-04 10:55:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsSubjectValid check if a subject is valid.
|
2021-03-05 04:18:31 +00:00
|
|
|
func IsSubjectValid(subject string) (isValid bool) {
|
2021-01-04 10:55:23 +00:00
|
|
|
return subject == "" || strings.HasPrefix(subject, "user:") || strings.HasPrefix(subject, "group:")
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsNetworkGroupValid check if a network group is valid.
|
2022-02-28 03:15:01 +00:00
|
|
|
func IsNetworkGroupValid(config schema.AccessControlConfiguration, network string) bool {
|
|
|
|
for _, networks := range config.Networks {
|
2021-03-05 04:18:31 +00:00
|
|
|
if network != networks.Name {
|
2021-01-04 10:55:23 +00:00
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsNetworkValid check if a network is valid.
|
2021-03-05 04:18:31 +00:00
|
|
|
func IsNetworkValid(network string) (isValid bool) {
|
2021-01-04 10:55:23 +00:00
|
|
|
if net.ParseIP(network) == nil {
|
|
|
|
_, _, err := net.ParseCIDR(network)
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
func ruleDescriptor(position int, rule schema.ACLRule) string {
|
|
|
|
if len(rule.Domains) == 0 {
|
|
|
|
return fmt.Sprintf("#%d", position)
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("#%d (domain '%s')", position, strings.Join(rule.Domains, ","))
|
|
|
|
}
|
|
|
|
|
2021-01-04 10:55:23 +00:00
|
|
|
// ValidateAccessControl validates access control configuration.
|
2022-02-28 03:15:01 +00:00
|
|
|
func ValidateAccessControl(config *schema.Configuration, validator *schema.StructValidator) {
|
|
|
|
if config.AccessControl.DefaultPolicy == "" {
|
|
|
|
config.AccessControl.DefaultPolicy = policyDeny
|
2021-06-01 04:09:50 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if !IsPolicyValid(config.AccessControl.DefaultPolicy) {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlDefaultPolicyValue, strings.Join(validACLRulePolicies, "', '"), config.AccessControl.DefaultPolicy))
|
2021-01-04 10:55:23 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
if config.AccessControl.Networks != nil {
|
|
|
|
for _, n := range config.AccessControl.Networks {
|
2021-01-04 10:55:23 +00:00
|
|
|
for _, networks := range n.Networks {
|
|
|
|
if !IsNetworkValid(networks) {
|
2022-02-28 03:15:01 +00:00
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlNetworkGroupIPCIDRInvalid, n.Name, networks))
|
2021-01-04 10:55:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValidateRules validates an ACL Rule configuration.
|
2022-02-28 03:15:01 +00:00
|
|
|
func ValidateRules(config *schema.Configuration, validator *schema.StructValidator) {
|
|
|
|
if config.AccessControl.Rules == nil || len(config.AccessControl.Rules) == 0 {
|
|
|
|
if config.AccessControl.DefaultPolicy != policyOneFactor && config.AccessControl.DefaultPolicy != policyTwoFactor {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlDefaultPolicyWithoutRules, config.AccessControl.DefaultPolicy))
|
2021-04-14 10:53:23 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
validator.PushWarning(fmt.Errorf(errFmtAccessControlWarnNoRulesDefaultPolicy, config.AccessControl.DefaultPolicy))
|
2021-04-14 10:53:23 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
for i, rule := range config.AccessControl.Rules {
|
2021-04-14 10:53:23 +00:00
|
|
|
rulePosition := i + 1
|
|
|
|
|
2022-04-01 11:38:49 +00:00
|
|
|
if len(rule.Domains)+len(rule.DomainsRegex) == 0 {
|
2022-02-28 03:15:01 +00:00
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleNoDomains, ruleDescriptor(rulePosition, rule)))
|
2021-01-04 10:55:23 +00:00
|
|
|
}
|
|
|
|
|
2021-04-14 10:53:23 +00:00
|
|
|
if !IsPolicyValid(rule.Policy) {
|
2022-02-28 03:15:01 +00:00
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleInvalidPolicy, ruleDescriptor(rulePosition, rule), rule.Policy))
|
2021-01-04 10:55:23 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
validateNetworks(rulePosition, rule, config.AccessControl, validator)
|
2021-03-05 04:18:31 +00:00
|
|
|
|
2021-04-14 10:53:23 +00:00
|
|
|
validateSubjects(rulePosition, rule, validator)
|
2021-03-05 04:18:31 +00:00
|
|
|
|
2021-04-14 10:53:23 +00:00
|
|
|
validateMethods(rulePosition, rule, validator)
|
2021-03-05 04:18:31 +00:00
|
|
|
|
2022-10-19 03:09:22 +00:00
|
|
|
validateQuery(i, rule, config, validator)
|
|
|
|
|
2022-04-01 11:38:49 +00:00
|
|
|
if rule.Policy == policyBypass {
|
|
|
|
validateBypass(rulePosition, rule, validator)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func validateBypass(rulePosition int, rule schema.ACLRule, validator *schema.StructValidator) {
|
|
|
|
if len(rule.Subjects) != 0 {
|
|
|
|
validator.Push(fmt.Errorf(errAccessControlRuleBypassPolicyInvalidWithSubjects, ruleDescriptor(rulePosition, rule)))
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, pattern := range rule.DomainsRegex {
|
|
|
|
if utils.IsStringSliceContainsAny(authorization.IdentitySubexpNames, pattern.SubexpNames()) {
|
|
|
|
validator.Push(fmt.Errorf(errAccessControlRuleBypassPolicyInvalidWithSubjectsWithGroupDomainRegex, ruleDescriptor(rulePosition, rule)))
|
|
|
|
return
|
2021-01-04 10:55:23 +00:00
|
|
|
}
|
2021-03-05 04:18:31 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-04 10:55:23 +00:00
|
|
|
|
2022-02-28 03:15:01 +00:00
|
|
|
func validateNetworks(rulePosition int, rule schema.ACLRule, config schema.AccessControlConfiguration, validator *schema.StructValidator) {
|
2021-04-14 10:53:23 +00:00
|
|
|
for _, network := range rule.Networks {
|
2021-03-05 04:18:31 +00:00
|
|
|
if !IsNetworkValid(network) {
|
2022-02-28 03:15:01 +00:00
|
|
|
if !IsNetworkGroupValid(config, network) {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleNetworksInvalid, ruleDescriptor(rulePosition, rule), network))
|
2021-01-16 10:05:41 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-05 04:18:31 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-16 10:05:41 +00:00
|
|
|
|
2021-04-14 10:53:23 +00:00
|
|
|
func validateSubjects(rulePosition int, rule schema.ACLRule, validator *schema.StructValidator) {
|
|
|
|
for _, subjectRule := range rule.Subjects {
|
2021-03-05 04:18:31 +00:00
|
|
|
for _, subject := range subjectRule {
|
|
|
|
if !IsSubjectValid(subject) {
|
2022-02-28 03:15:01 +00:00
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleSubjectInvalid, ruleDescriptor(rulePosition, rule), subject))
|
2021-01-04 10:55:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-05 04:18:31 +00:00
|
|
|
|
2021-04-14 10:53:23 +00:00
|
|
|
func validateMethods(rulePosition int, rule schema.ACLRule, validator *schema.StructValidator) {
|
|
|
|
for _, method := range rule.Methods {
|
2022-04-01 10:53:10 +00:00
|
|
|
if !utils.IsStringInSliceFold(method, validACLHTTPMethodVerbs) {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleMethodInvalid, ruleDescriptor(rulePosition, rule), method, strings.Join(validACLHTTPMethodVerbs, "', '")))
|
2021-03-05 04:18:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-10-19 03:09:22 +00:00
|
|
|
|
|
|
|
//nolint:gocyclo
|
|
|
|
func validateQuery(i int, rule schema.ACLRule, config *schema.Configuration, validator *schema.StructValidator) {
|
|
|
|
for j := 0; j < len(config.AccessControl.Rules[i].Query); j++ {
|
|
|
|
for k := 0; k < len(config.AccessControl.Rules[i].Query[j]); k++ {
|
|
|
|
if config.AccessControl.Rules[i].Query[j][k].Operator == "" {
|
|
|
|
if config.AccessControl.Rules[i].Query[j][k].Key != "" {
|
|
|
|
switch config.AccessControl.Rules[i].Query[j][k].Value {
|
|
|
|
case "", nil:
|
|
|
|
config.AccessControl.Rules[i].Query[j][k].Operator = operatorPresent
|
|
|
|
default:
|
|
|
|
config.AccessControl.Rules[i].Query[j][k].Operator = operatorEqual
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if !utils.IsStringInSliceFold(config.AccessControl.Rules[i].Query[j][k].Operator, validACLRuleOperators) {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleQueryInvalid, ruleDescriptor(i+1, rule), config.AccessControl.Rules[i].Query[j][k].Operator, strings.Join(validACLRuleOperators, "', '")))
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.AccessControl.Rules[i].Query[j][k].Key == "" {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleQueryInvalidNoValue, ruleDescriptor(i+1, rule), "key"))
|
|
|
|
}
|
|
|
|
|
|
|
|
op := config.AccessControl.Rules[i].Query[j][k].Operator
|
|
|
|
|
|
|
|
if op == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
switch v := config.AccessControl.Rules[i].Query[j][k].Value.(type) {
|
|
|
|
case nil:
|
|
|
|
if op != operatorAbsent && op != operatorPresent {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleQueryInvalidNoValueOperator, ruleDescriptor(i+1, rule), "value", op))
|
|
|
|
}
|
|
|
|
case string:
|
|
|
|
switch op {
|
|
|
|
case operatorPresent, operatorAbsent:
|
|
|
|
if v != "" {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleQueryInvalidValue, ruleDescriptor(i+1, rule), "value", op))
|
|
|
|
}
|
|
|
|
case operatorPattern, operatorNotPattern:
|
|
|
|
var (
|
|
|
|
pattern *regexp.Regexp
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
if pattern, err = regexp.Compile(v); err != nil {
|
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleQueryInvalidValueParse, ruleDescriptor(i+1, rule), "value", err))
|
|
|
|
} else {
|
|
|
|
config.AccessControl.Rules[i].Query[j][k].Value = pattern
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
validator.Push(fmt.Errorf(errFmtAccessControlRuleQueryInvalidValueType, ruleDescriptor(i+1, rule), v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|