2021-01-04 17:55:23 +07:00
package validator
import (
"fmt"
"net"
2021-01-16 17:05:41 +07:00
"regexp"
2021-01-04 17:55:23 +07:00
"strings"
"github.com/authelia/authelia/internal/configuration/schema"
"github.com/authelia/authelia/internal/utils"
)
// IsPolicyValid check if policy is valid.
2021-03-05 11:18:31 +07:00
func IsPolicyValid ( policy string ) ( isValid bool ) {
2021-04-14 17:53:23 +07:00
return policy == denyPolicy || policy == oneFactorPolicy || policy == twoFactorPolicy || policy == bypassPolicy
2021-01-04 17:55:23 +07:00
}
2021-01-16 17:05:41 +07:00
// IsResourceValid check if a resource is valid.
2021-03-05 11:18:31 +07:00
func IsResourceValid ( resource string ) ( err error ) {
_ , err = regexp . Compile ( resource )
2021-01-16 17:05:41 +07:00
return err
}
2021-01-04 17:55:23 +07:00
// IsSubjectValid check if a subject is valid.
2021-03-05 11:18:31 +07:00
func IsSubjectValid ( subject string ) ( isValid bool ) {
2021-01-04 17:55:23 +07:00
return subject == "" || strings . HasPrefix ( subject , "user:" ) || strings . HasPrefix ( subject , "group:" )
}
// IsNetworkGroupValid check if a network group is valid.
func IsNetworkGroupValid ( configuration schema . AccessControlConfiguration , network string ) bool {
for _ , networks := range configuration . Networks {
2021-03-05 11:18:31 +07:00
if network != networks . Name {
2021-01-04 17:55:23 +07:00
continue
} else {
return true
}
}
return false
}
// IsNetworkValid check if a network is valid.
2021-03-05 11:18:31 +07:00
func IsNetworkValid ( network string ) ( isValid bool ) {
2021-01-04 17:55:23 +07:00
if net . ParseIP ( network ) == nil {
_ , _ , err := net . ParseCIDR ( network )
return err == nil
}
return true
}
// ValidateAccessControl validates access control configuration.
func ValidateAccessControl ( configuration schema . AccessControlConfiguration , validator * schema . StructValidator ) {
if ! IsPolicyValid ( configuration . DefaultPolicy ) {
validator . Push ( fmt . Errorf ( "'default_policy' must either be 'deny', 'two_factor', 'one_factor' or 'bypass'" ) )
}
if configuration . Networks != nil {
for _ , n := range configuration . Networks {
for _ , networks := range n . Networks {
if ! IsNetworkValid ( networks ) {
2021-01-16 17:05:41 +07:00
validator . Push ( fmt . Errorf ( "Network %s from network group: %s must be a valid IP or CIDR" , n . Networks , n . Name ) )
2021-01-04 17:55:23 +07:00
}
}
}
}
}
// ValidateRules validates an ACL Rule configuration.
func ValidateRules ( configuration schema . AccessControlConfiguration , validator * schema . StructValidator ) {
2021-04-14 17:53:23 +07:00
if configuration . Rules == nil || len ( configuration . Rules ) == 0 {
if configuration . DefaultPolicy != oneFactorPolicy && configuration . DefaultPolicy != twoFactorPolicy {
validator . Push ( fmt . Errorf ( "Default Policy [%s] is invalid, access control rules must be provided or a policy must either be 'one_factor' or 'two_factor'" , configuration . DefaultPolicy ) )
return
}
validator . PushWarning ( fmt . Errorf ( "No access control rules have been defined so the default policy %s will be applied to all requests" , configuration . DefaultPolicy ) )
return
}
for i , rule := range configuration . Rules {
rulePosition := i + 1
if len ( rule . Domains ) == 0 {
validator . Push ( fmt . Errorf ( "Rule #%d is invalid, a policy must have one or more domains" , rulePosition ) )
2021-01-04 17:55:23 +07:00
}
2021-04-14 17:53:23 +07:00
if ! IsPolicyValid ( rule . Policy ) {
validator . Push ( fmt . Errorf ( "Policy [%s] for rule #%d domain: %s is invalid, a policy must either be 'deny', 'two_factor', 'one_factor' or 'bypass'" , rule . Policy , rulePosition , rule . Domains ) )
2021-01-04 17:55:23 +07:00
}
2021-04-14 17:53:23 +07:00
validateNetworks ( rulePosition , rule , configuration , validator )
2021-03-05 11:18:31 +07:00
2021-04-14 17:53:23 +07:00
validateResources ( rulePosition , rule , validator )
2021-03-05 11:18:31 +07:00
2021-04-14 17:53:23 +07:00
validateSubjects ( rulePosition , rule , validator )
2021-03-05 11:18:31 +07:00
2021-04-14 17:53:23 +07:00
validateMethods ( rulePosition , rule , validator )
2021-03-05 11:18:31 +07:00
2021-04-14 17:53:23 +07:00
if rule . Policy == bypassPolicy && len ( rule . Subjects ) != 0 {
validator . Push ( fmt . Errorf ( errAccessControlInvalidPolicyWithSubjects , rulePosition , rule . Domains , rule . Subjects ) )
2021-01-04 17:55:23 +07:00
}
2021-03-05 11:18:31 +07:00
}
}
2021-01-04 17:55:23 +07:00
2021-04-14 17:53:23 +07:00
func validateNetworks ( rulePosition int , rule schema . ACLRule , configuration schema . AccessControlConfiguration , validator * schema . StructValidator ) {
for _ , network := range rule . Networks {
2021-03-05 11:18:31 +07:00
if ! IsNetworkValid ( network ) {
if ! IsNetworkGroupValid ( configuration , network ) {
2021-04-14 17:53:23 +07:00
validator . Push ( fmt . Errorf ( "Network %s for rule #%d domain: %s is not a valid network or network group" , rule . Networks , rulePosition , rule . Domains ) )
2021-01-16 17:05:41 +07:00
}
}
2021-03-05 11:18:31 +07:00
}
}
2021-01-16 17:05:41 +07:00
2021-04-14 17:53:23 +07:00
func validateResources ( rulePosition int , rule schema . ACLRule , validator * schema . StructValidator ) {
for _ , resource := range rule . Resources {
2021-03-05 11:18:31 +07:00
if err := IsResourceValid ( resource ) ; err != nil {
2021-04-14 17:53:23 +07:00
validator . Push ( fmt . Errorf ( "Resource %s for rule #%d domain: %s is invalid, %s" , rule . Resources , rulePosition , rule . Domains , err ) )
2021-03-05 11:18:31 +07:00
}
}
}
2021-04-14 17:53:23 +07:00
func validateSubjects ( rulePosition int , rule schema . ACLRule , validator * schema . StructValidator ) {
for _ , subjectRule := range rule . Subjects {
2021-03-05 11:18:31 +07:00
for _ , subject := range subjectRule {
if ! IsSubjectValid ( subject ) {
2021-04-14 17:53:23 +07:00
validator . Push ( fmt . Errorf ( "Subject %s for rule #%d domain: %s is invalid, must start with 'user:' or 'group:'" , subjectRule , rulePosition , rule . Domains ) )
2021-01-04 17:55:23 +07:00
}
}
}
}
2021-03-05 11:18:31 +07:00
2021-04-14 17:53:23 +07:00
func validateMethods ( rulePosition int , rule schema . ACLRule , validator * schema . StructValidator ) {
for _ , method := range rule . Methods {
2021-03-05 11:18:31 +07:00
if ! utils . IsStringInSliceFold ( method , validRequestMethods ) {
2021-04-14 17:53:23 +07:00
validator . Push ( fmt . Errorf ( "Method %s for rule #%d domain: %s is invalid, must be one of the following methods: %s" , method , rulePosition , rule . Domains , strings . Join ( validRequestMethods , ", " ) ) )
2021-03-05 11:18:31 +07:00
}
}
}