2021-01-16 10:05:41 +00:00
package validator
import (
2021-03-05 04:18:31 +00:00
"fmt"
2022-04-01 11:38:49 +00:00
"regexp"
2021-01-16 10:05:41 +00:00
"testing"
2021-04-14 10:53:23 +00:00
"github.com/stretchr/testify/assert"
2021-01-16 10:05:41 +00:00
"github.com/stretchr/testify/suite"
2021-08-11 01:04:35 +00:00
"github.com/authelia/authelia/v4/internal/configuration/schema"
2021-01-16 10:05:41 +00:00
)
type AccessControl struct {
suite . Suite
2022-02-28 03:15:01 +00:00
config * schema . Configuration
validator * schema . StructValidator
2021-01-16 10:05:41 +00:00
}
func ( suite * AccessControl ) SetupTest ( ) {
suite . validator = schema . NewStructValidator ( )
2022-02-28 03:15:01 +00:00
suite . config = & schema . Configuration {
AccessControl : schema . AccessControlConfiguration {
DefaultPolicy : policyDeny ,
Networks : schema . DefaultACLNetwork ,
Rules : schema . DefaultACLRule ,
} ,
}
2021-01-16 10:05:41 +00:00
}
func ( suite * AccessControl ) TestShouldValidateCompleteConfiguration ( ) {
2022-02-28 03:15:01 +00:00
ValidateAccessControl ( suite . config , suite . validator )
2021-01-16 10:05:41 +00:00
2022-04-04 07:46:55 +00:00
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
suite . Assert ( ) . Len ( suite . validator . Errors ( ) , 0 )
2021-01-16 10:05:41 +00:00
}
2022-04-01 11:38:49 +00:00
func ( suite * AccessControl ) TestShouldValidateEitherDomainsOrDomainsRegex ( ) {
domainsRegex := regexp . MustCompile ( ` ^abc.example.com$ ` )
suite . config . AccessControl . Rules = [ ] schema . ACLRule {
{
Domains : [ ] string { "abc.example.com" } ,
Policy : "bypass" ,
} ,
{
DomainsRegex : [ ] regexp . Regexp { * domainsRegex } ,
Policy : "bypass" ,
} ,
{
Policy : "bypass" ,
} ,
}
ValidateRules ( suite . config , suite . validator )
2022-04-04 07:46:55 +00:00
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
2022-04-01 11:38:49 +00:00
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 1 )
assert . EqualError ( suite . T ( ) , suite . validator . Errors ( ) [ 0 ] , "access control: rule #3: rule is invalid: must have the option 'domain' or 'domain_regex' configured" )
}
2021-01-16 10:05:41 +00:00
func ( suite * AccessControl ) TestShouldRaiseErrorInvalidDefaultPolicy ( ) {
2022-10-17 10:51:59 +00:00
suite . config . AccessControl . DefaultPolicy = testInvalid
2021-01-16 10:05:41 +00:00
2022-02-28 03:15:01 +00:00
ValidateAccessControl ( suite . config , suite . validator )
2021-01-16 10:05:41 +00:00
2022-04-04 07:46:55 +00:00
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
2021-01-16 10:05:41 +00:00
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 1 )
2022-02-28 03:15:01 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 0 ] , "access control: option 'default_policy' must be one of 'bypass', 'one_factor', 'two_factor', 'deny' but it is configured as 'invalid'" )
2021-01-16 10:05:41 +00:00
}
func ( suite * AccessControl ) TestShouldRaiseErrorInvalidNetworkGroupNetwork ( ) {
2022-02-28 03:15:01 +00:00
suite . config . AccessControl . Networks = [ ] schema . ACLNetwork {
2021-01-16 10:05:41 +00:00
{
2021-03-05 04:18:31 +00:00
Name : "internal" ,
2021-01-16 10:05:41 +00:00
Networks : [ ] string { "abc.def.ghi.jkl" } ,
} ,
}
2022-02-28 03:15:01 +00:00
ValidateAccessControl ( suite . config , suite . validator )
2021-01-16 10:05:41 +00:00
2022-04-04 07:46:55 +00:00
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
2021-01-16 10:05:41 +00:00
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 1 )
2022-02-28 03:15:01 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 0 ] , "access control: networks: network group 'internal' is invalid: the network 'abc.def.ghi.jkl' is not a valid IP or CIDR notation" )
2021-01-16 10:05:41 +00:00
}
2022-11-23 23:16:23 +00:00
func ( suite * AccessControl ) TestShouldRaiseWarningOnBadDomain ( ) {
suite . config . AccessControl . Rules = [ ] schema . ACLRule {
{
Domains : [ ] string { "*example.com" } ,
Policy : "one_factor" ,
} ,
}
ValidateRules ( suite . config , suite . validator )
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 1 )
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 0 )
suite . Assert ( ) . EqualError ( suite . validator . Warnings ( ) [ 0 ] , "access control: rule #1: domain #1: domain '*example.com' is ineffective and should probably be '*.example.com' instead" )
}
2021-04-14 10:53:23 +00:00
func ( suite * AccessControl ) TestShouldRaiseErrorWithNoRulesDefined ( ) {
2022-02-28 03:15:01 +00:00
suite . config . AccessControl . Rules = [ ] schema . ACLRule { }
2021-01-16 10:05:41 +00:00
2022-02-28 03:15:01 +00:00
ValidateRules ( suite . config , suite . validator )
2021-01-16 10:05:41 +00:00
2022-04-04 07:46:55 +00:00
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
2021-04-14 10:53:23 +00:00
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 1 )
2022-02-28 03:15:01 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 0 ] , "access control: 'default_policy' option 'deny' is invalid: when no rules are specified it must be 'two_factor' or 'one_factor'" )
2021-04-14 10:53:23 +00:00
}
func ( suite * AccessControl ) TestShouldRaiseWarningWithNoRulesDefined ( ) {
2022-02-28 03:15:01 +00:00
suite . config . AccessControl . Rules = [ ] schema . ACLRule { }
2021-04-14 10:53:23 +00:00
2022-02-28 03:15:01 +00:00
suite . config . AccessControl . DefaultPolicy = policyTwoFactor
2021-04-14 10:53:23 +00:00
2022-02-28 03:15:01 +00:00
ValidateRules ( suite . config , suite . validator )
2021-04-14 10:53:23 +00:00
2022-04-04 07:46:55 +00:00
suite . Assert ( ) . Len ( suite . validator . Errors ( ) , 0 )
2021-04-14 10:53:23 +00:00
suite . Require ( ) . Len ( suite . validator . Warnings ( ) , 1 )
2022-02-28 03:15:01 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Warnings ( ) [ 0 ] , "access control: no rules have been specified so the 'default_policy' of 'two_factor' is going to be applied to all requests" )
2021-04-14 10:53:23 +00:00
}
func ( suite * AccessControl ) TestShouldRaiseErrorsWithEmptyRules ( ) {
2022-02-28 03:15:01 +00:00
suite . config . AccessControl . Rules = [ ] schema . ACLRule {
{ } ,
{
Policy : "wrong" ,
} ,
}
2021-04-14 10:53:23 +00:00
2022-02-28 03:15:01 +00:00
ValidateRules ( suite . config , suite . validator )
2021-04-14 10:53:23 +00:00
2022-04-04 07:46:55 +00:00
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
2021-04-14 10:53:23 +00:00
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 4 )
2021-01-16 10:05:41 +00:00
2022-04-01 11:38:49 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 0 ] , "access control: rule #1: rule is invalid: must have the option 'domain' or 'domain_regex' configured" )
2022-02-28 03:15:01 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 1 ] , "access control: rule #1: rule 'policy' option '' is invalid: must be one of 'deny', 'two_factor', 'one_factor' or 'bypass'" )
2022-04-01 11:38:49 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 2 ] , "access control: rule #2: rule is invalid: must have the option 'domain' or 'domain_regex' configured" )
2022-02-28 03:15:01 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 3 ] , "access control: rule #2: rule 'policy' option 'wrong' is invalid: must be one of 'deny', 'two_factor', 'one_factor' or 'bypass'" )
2021-01-16 10:05:41 +00:00
}
func ( suite * AccessControl ) TestShouldRaiseErrorInvalidPolicy ( ) {
2022-02-28 03:15:01 +00:00
suite . config . AccessControl . Rules = [ ] schema . ACLRule {
2021-01-16 10:05:41 +00:00
{
Domains : [ ] string { "public.example.com" } ,
2022-10-17 10:51:59 +00:00
Policy : testInvalid ,
2021-01-16 10:05:41 +00:00
} ,
}
2022-02-28 03:15:01 +00:00
ValidateRules ( suite . config , suite . validator )
2021-01-16 10:05:41 +00:00
2022-04-04 07:46:55 +00:00
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
2021-01-16 10:05:41 +00:00
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 1 )
2022-02-28 03:15:01 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 0 ] , "access control: rule #1 (domain 'public.example.com'): rule 'policy' option 'invalid' is invalid: must be one of 'deny', 'two_factor', 'one_factor' or 'bypass'" )
2021-01-16 10:05:41 +00:00
}
func ( suite * AccessControl ) TestShouldRaiseErrorInvalidNetwork ( ) {
2022-02-28 03:15:01 +00:00
suite . config . AccessControl . Rules = [ ] schema . ACLRule {
2021-01-16 10:05:41 +00:00
{
Domains : [ ] string { "public.example.com" } ,
Policy : "bypass" ,
Networks : [ ] string { "abc.def.ghi.jkl/32" } ,
} ,
}
2022-02-28 03:15:01 +00:00
ValidateRules ( suite . config , suite . validator )
2021-01-16 10:05:41 +00:00
2022-04-04 07:46:55 +00:00
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
2021-01-16 10:05:41 +00:00
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 1 )
2022-02-28 03:15:01 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 0 ] , "access control: rule #1 (domain 'public.example.com'): the network 'abc.def.ghi.jkl/32' is not a valid Group Name, IP, or CIDR notation" )
2021-01-16 10:05:41 +00:00
}
2021-03-05 04:18:31 +00:00
func ( suite * AccessControl ) TestShouldRaiseErrorInvalidMethod ( ) {
2022-02-28 03:15:01 +00:00
suite . config . AccessControl . Rules = [ ] schema . ACLRule {
2021-03-05 04:18:31 +00:00
{
Domains : [ ] string { "public.example.com" } ,
Policy : "bypass" ,
Methods : [ ] string { "GET" , "HOP" } ,
} ,
}
2022-02-28 03:15:01 +00:00
ValidateRules ( suite . config , suite . validator )
2021-03-05 04:18:31 +00:00
2022-04-04 07:46:55 +00:00
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
2021-03-05 04:18:31 +00:00
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 1 )
2022-04-01 10:53:10 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 0 ] , "access control: rule #1 (domain 'public.example.com'): 'methods' option 'HOP' is invalid: must be one of 'GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'TRACE', 'CONNECT', 'OPTIONS', 'COPY', 'LOCK', 'MKCOL', 'MOVE', 'PROPFIND', 'PROPPATCH', 'UNLOCK'" )
2021-03-05 04:18:31 +00:00
}
2021-01-16 10:05:41 +00:00
func ( suite * AccessControl ) TestShouldRaiseErrorInvalidSubject ( ) {
2021-03-05 04:18:31 +00:00
domains := [ ] string { "public.example.com" }
2022-10-17 10:51:59 +00:00
subjects := [ ] [ ] string { { testInvalid } }
2022-02-28 03:15:01 +00:00
suite . config . AccessControl . Rules = [ ] schema . ACLRule {
2021-01-16 10:05:41 +00:00
{
2021-03-05 04:18:31 +00:00
Domains : domains ,
2021-01-16 10:05:41 +00:00
Policy : "bypass" ,
2021-03-05 04:18:31 +00:00
Subjects : subjects ,
2021-01-16 10:05:41 +00:00
} ,
}
2022-02-28 03:15:01 +00:00
ValidateRules ( suite . config , suite . validator )
2021-01-16 10:05:41 +00:00
2021-03-05 04:18:31 +00:00
suite . Require ( ) . Len ( suite . validator . Warnings ( ) , 0 )
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 2 )
2021-01-16 10:05:41 +00:00
2022-02-28 03:15:01 +00:00
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 0 ] , "access control: rule #1 (domain 'public.example.com'): 'subject' option 'invalid' is invalid: must start with 'user:' or 'group:'" )
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 1 ] , fmt . Sprintf ( errAccessControlRuleBypassPolicyInvalidWithSubjects , ruleDescriptor ( 1 , suite . config . AccessControl . Rules [ 0 ] ) ) )
2021-01-16 10:05:41 +00:00
}
2022-10-21 08:41:33 +00:00
func ( suite * AccessControl ) TestShouldRaiseErrorBypassWithSubjectDomainRegexGroup ( ) {
suite . config . AccessControl . Rules = [ ] schema . ACLRule {
{
DomainsRegex : MustCompileRegexps ( [ ] string { ` ^(?P<User>\w+)\.example\.com$ ` } ) ,
Policy : "bypass" ,
} ,
}
ValidateRules ( suite . config , suite . validator )
suite . Require ( ) . Len ( suite . validator . Warnings ( ) , 0 )
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 1 )
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 0 ] , "access control: rule #1: 'policy' option 'bypass' is not supported when 'domain_regex' option contains the user or group named matches. For more information see: https://www.authelia.com/c/acl-match-concept-2" )
}
2022-10-19 03:09:22 +00:00
func ( suite * AccessControl ) TestShouldSetQueryDefaults ( ) {
domains := [ ] string { "public.example.com" }
suite . config . AccessControl . Rules = [ ] schema . ACLRule {
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ Operator : "" , Key : "example" } ,
} ,
{
{ Operator : "" , Key : "example" , Value : "test" } ,
} ,
} ,
} ,
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ Operator : "pattern" , Key : "a" , Value : "^(x|y|z)$" } ,
} ,
} ,
} ,
}
ValidateRules ( suite . config , suite . validator )
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
suite . Assert ( ) . Len ( suite . validator . Errors ( ) , 0 )
suite . Assert ( ) . Equal ( "present" , suite . config . AccessControl . Rules [ 0 ] . Query [ 0 ] [ 0 ] . Operator )
suite . Assert ( ) . Equal ( "equal" , suite . config . AccessControl . Rules [ 0 ] . Query [ 1 ] [ 0 ] . Operator )
suite . Require ( ) . Len ( suite . config . AccessControl . Rules , 2 )
suite . Require ( ) . Len ( suite . config . AccessControl . Rules [ 1 ] . Query , 1 )
suite . Require ( ) . Len ( suite . config . AccessControl . Rules [ 1 ] . Query [ 0 ] , 1 )
t := & regexp . Regexp { }
suite . Assert ( ) . IsType ( t , suite . config . AccessControl . Rules [ 1 ] . Query [ 0 ] [ 0 ] . Value )
}
func ( suite * AccessControl ) TestShouldErrorOnInvalidRulesQuery ( ) {
domains := [ ] string { "public.example.com" }
suite . config . AccessControl . Rules = [ ] schema . ACLRule {
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ Operator : "equal" , Key : "example" } ,
} ,
} ,
} ,
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ Operator : "present" } ,
} ,
} ,
} ,
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ Operator : "present" , Key : "a" } ,
} ,
} ,
} ,
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ Operator : "absent" , Key : "a" } ,
} ,
} ,
} ,
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ } ,
} ,
} ,
} ,
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ Operator : "not" , Key : "a" , Value : "a" } ,
} ,
} ,
} ,
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ Operator : "pattern" , Key : "a" , Value : "(bad pattern" } ,
} ,
} ,
} ,
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ Operator : "present" , Key : "a" , Value : "not good" } ,
} ,
} ,
} ,
{
Domains : domains ,
Policy : "bypass" ,
Query : [ ] [ ] schema . ACLQueryRule {
{
{ Operator : "present" , Key : "a" , Value : 5 } ,
} ,
} ,
} ,
}
ValidateRules ( suite . config , suite . validator )
suite . Assert ( ) . Len ( suite . validator . Warnings ( ) , 0 )
suite . Require ( ) . Len ( suite . validator . Errors ( ) , 7 )
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 0 ] , "access control: rule #1 (domain 'public.example.com'): 'query' option 'value' is invalid: must have a value when the operator is 'equal'" )
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 1 ] , "access control: rule #2 (domain 'public.example.com'): 'query' option 'key' is invalid: must have a value" )
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 2 ] , "access control: rule #5 (domain 'public.example.com'): 'query' option 'key' is invalid: must have a value" )
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 3 ] , "access control: rule #6 (domain 'public.example.com'): 'query' option 'operator' with value 'not' is invalid: must be one of 'present', 'absent', 'equal', 'not equal', 'pattern', 'not pattern'" )
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 4 ] , "access control: rule #7 (domain 'public.example.com'): 'query' option 'value' is invalid: error parsing regexp: missing closing ): `(bad pattern`" )
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 5 ] , "access control: rule #8 (domain 'public.example.com'): 'query' option 'value' is invalid: must not have a value when the operator is 'present'" )
suite . Assert ( ) . EqualError ( suite . validator . Errors ( ) [ 6 ] , "access control: rule #9 (domain 'public.example.com'): 'query' option 'value' is invalid: expected type was string but got int" )
}
2021-01-16 10:05:41 +00:00
func TestAccessControl ( t * testing . T ) {
suite . Run ( t , new ( AccessControl ) )
}
2021-04-14 10:53:23 +00:00
func TestShouldReturnCorrectResultsForValidNetworkGroups ( t * testing . T ) {
config := schema . AccessControlConfiguration {
Networks : schema . DefaultACLNetwork ,
}
validNetwork := IsNetworkGroupValid ( config , "internal" )
2021-07-15 11:02:03 +00:00
invalidNetwork := IsNetworkGroupValid ( config , loopback )
2021-04-14 10:53:23 +00:00
assert . True ( t , validNetwork )
assert . False ( t , invalidNetwork )
}
2022-10-21 08:41:33 +00:00
func MustCompileRegexps ( exps [ ] string ) ( regexps [ ] regexp . Regexp ) {
regexps = make ( [ ] regexp . Regexp , len ( exps ) )
for i , exp := range exps {
regexps [ i ] = * regexp . MustCompile ( exp )
}
return regexps
}