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 ) {
2023-04-13 10:58:18 +00:00
validator . Push ( fmt . Errorf ( errFmtAccessControlDefaultPolicyValue , strJoinOr ( 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-11-23 23:16:23 +00:00
validateDomains ( rulePosition , rule , validator )
2021-01-04 10:55:23 +00:00
2023-04-13 10:58:18 +00:00
switch rule . Policy {
case "" :
validator . Push ( fmt . Errorf ( errFmtAccessControlRuleNoPolicy , ruleDescriptor ( rulePosition , rule ) ) )
default :
if ! IsPolicyValid ( rule . Policy ) {
validator . Push ( fmt . Errorf ( errFmtAccessControlRuleInvalidPolicy , ruleDescriptor ( rulePosition , rule ) , strJoinOr ( validACLRulePolicies ) , 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-11-23 23:16:23 +00:00
func validateDomains ( rulePosition int , rule schema . ACLRule , validator * schema . StructValidator ) {
if len ( rule . Domains ) + len ( rule . DomainsRegex ) == 0 {
validator . Push ( fmt . Errorf ( errFmtAccessControlRuleNoDomains , ruleDescriptor ( rulePosition , rule ) ) )
}
for i , domain := range rule . Domains {
if len ( domain ) > 1 && domain [ 0 ] == '*' && domain [ 1 ] != '.' {
validator . PushWarning ( fmt . Errorf ( "access control: rule #%d: domain #%d: domain '%s' is ineffective and should probably be '%s' instead" , rulePosition , i + 1 , domain , fmt . Sprintf ( "*.%s" , domain [ 1 : ] ) ) )
}
}
}
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 ) {
2023-04-13 10:58:18 +00:00
invalid , duplicates := validateList ( rule . Methods , validACLHTTPMethodVerbs , true )
if len ( invalid ) != 0 {
validator . Push ( fmt . Errorf ( errFmtAccessControlRuleInvalidEntries , ruleDescriptor ( rulePosition , rule ) , "methods" , strJoinOr ( validACLHTTPMethodVerbs ) , strJoinAnd ( invalid ) ) )
}
if len ( duplicates ) != 0 {
validator . Push ( fmt . Errorf ( errFmtAccessControlRuleInvalidDuplicates , ruleDescriptor ( rulePosition , rule ) , "methods" , strJoinAnd ( duplicates ) ) )
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 ) {
2023-04-13 10:58:18 +00:00
validator . Push ( fmt . Errorf ( errFmtAccessControlRuleQueryInvalid , ruleDescriptor ( i + 1 , rule ) , strJoinOr ( validACLRuleOperators ) , config . AccessControl . Rules [ i ] . Query [ j ] [ k ] . Operator ) )
2022-10-19 03:09:22 +00:00
}
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 ) )
}
}
}
}