github.com/greenpau/go-authcrunch@v1.1.4/pkg/acl/acl_test.go (about)

     1  // Copyright 2022 Paul Greenberg greenpau@outlook.com
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package acl
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"github.com/google/go-cmp/cmp"
    21  	"github.com/greenpau/go-authcrunch/internal/tests"
    22  	"github.com/greenpau/go-authcrunch/pkg/errors"
    23  	logutil "github.com/greenpau/go-authcrunch/pkg/util/log"
    24  	"testing"
    25  	"time"
    26  )
    27  
    28  func TestNewAccessList(t *testing.T) {
    29  	var testcases = []struct {
    30  		name         string
    31  		config       []*RuleConfiguration
    32  		batch        bool
    33  		defaultAllow bool
    34  		input        map[string]interface{}
    35  		want         map[string]interface{}
    36  		shouldErr    bool
    37  		err          error
    38  	}{
    39  		{
    40  			name: "new access list with logging",
    41  			config: []*RuleConfiguration{
    42  				{
    43  					Comment: "foobar barfoo",
    44  					Conditions: []string{
    45  						"exact match roles foobar",
    46  						"exact match org nyc",
    47  					},
    48  					Action: `allow any stop log`,
    49  				},
    50  			},
    51  			input: map[string]interface{}{
    52  				"name":  "John Smith",
    53  				"roles": []string{"foobar"},
    54  			},
    55  			want: map[string]interface{}{
    56  				"allow": true,
    57  			},
    58  		},
    59  		{
    60  			name: "new access list with batched conditions",
    61  			config: []*RuleConfiguration{
    62  				{
    63  					Comment: "foobar barfoo",
    64  					Conditions: []string{
    65  						"exact match roles foobar",
    66  						"exact match org nyc",
    67  					},
    68  					Action: `allow any stop log`,
    69  				},
    70  			},
    71  			batch: true,
    72  			input: map[string]interface{}{
    73  				"name":  "John Smith",
    74  				"roles": []string{"foobar"},
    75  			},
    76  			want: map[string]interface{}{
    77  				"allow": true,
    78  			},
    79  		},
    80  		{
    81  			name: "new access list with default allow",
    82  			config: []*RuleConfiguration{
    83  				{
    84  					Comment: "foobar barfoo",
    85  					Conditions: []string{
    86  						"exact match roles foobar",
    87  						"exact match org nyc",
    88  					},
    89  					Action: `allow any stop log`,
    90  				},
    91  			},
    92  			defaultAllow: true,
    93  			input: map[string]interface{}{
    94  				"name": "John Smith",
    95  			},
    96  			want: map[string]interface{}{
    97  				"allow": true,
    98  			},
    99  		},
   100  		{
   101  			name: "new access list with invalid conditions",
   102  			config: []*RuleConfiguration{
   103  				{
   104  					Comment: "foobar barfoo",
   105  					Conditions: []string{
   106  						"",
   107  						"",
   108  					},
   109  					Action: `allow any stop log`,
   110  				},
   111  			},
   112  			shouldErr: true,
   113  			err:       fmt.Errorf("invalid rule syntax, failed to extract condition tokens: EOF"),
   114  		},
   115  		{
   116  			name: "new access list with invalid batched conditions",
   117  			config: []*RuleConfiguration{
   118  				{
   119  					Comment: "foobar barfoo",
   120  					Conditions: []string{
   121  						"",
   122  						"",
   123  					},
   124  					Action: `allow any stop log`,
   125  				},
   126  			},
   127  			batch:     true,
   128  			shouldErr: true,
   129  			err:       fmt.Errorf("invalid rule syntax, failed to extract condition tokens: EOF"),
   130  		},
   131  
   132  		{
   133  			name: "new access list with allow verdict",
   134  			config: []*RuleConfiguration{
   135  				{
   136  					Comment: "foobar barfoo",
   137  					Conditions: []string{
   138  						"exact match roles foobar",
   139  						"exact match org nyc",
   140  					},
   141  					Action: `allow any log`,
   142  				},
   143  			},
   144  			input: map[string]interface{}{
   145  				"name":  "John Smith",
   146  				"roles": []string{"foobar"},
   147  			},
   148  			want: map[string]interface{}{
   149  				"allow": true,
   150  			},
   151  		},
   152  		{
   153  			name: "new access list with deny verdict",
   154  			config: []*RuleConfiguration{
   155  				{
   156  					Comment: "foobar barfoo",
   157  					Conditions: []string{
   158  						"exact match roles foobar",
   159  						"exact match org nyc",
   160  					},
   161  					Action: `deny any log`,
   162  				},
   163  			},
   164  			input: map[string]interface{}{
   165  				"name":  "John Smith",
   166  				"roles": []string{"foobar"},
   167  			},
   168  			want: map[string]interface{}{
   169  				"allow": false,
   170  			},
   171  		},
   172  		{
   173  			name: "new access list with deny and stop verdict",
   174  			config: []*RuleConfiguration{
   175  				{
   176  					Comment: "foobar barfoo",
   177  					Conditions: []string{
   178  						"exact match roles foobar",
   179  						"exact match org nyc",
   180  					},
   181  					Action: `deny any stop log`,
   182  				},
   183  			},
   184  			input: map[string]interface{}{
   185  				"name":  "John Smith",
   186  				"roles": []string{"foobar"},
   187  			},
   188  			want: map[string]interface{}{
   189  				"allow": false,
   190  			},
   191  		},
   192  		{
   193  			name: "new access list with default deny",
   194  			config: []*RuleConfiguration{
   195  				{
   196  					Comment: "foobar barfoo",
   197  					Conditions: []string{
   198  						"exact match roles foobar",
   199  						"exact match org nyc",
   200  					},
   201  					Action: `deny any stop log`,
   202  				},
   203  			},
   204  			input: map[string]interface{}{
   205  				"name": "John Smith",
   206  			},
   207  			want: map[string]interface{}{
   208  				"allow": false,
   209  			},
   210  		},
   211  	}
   212  
   213  	for _, tc := range testcases {
   214  		t.Run(tc.name, func(t *testing.T) {
   215  			var err error
   216  			ctx := context.Background()
   217  			logger := logutil.NewLogger()
   218  			accessList := NewAccessList()
   219  			accessList.SetLogger(logger)
   220  			if tc.defaultAllow {
   221  				accessList.SetDefaultAllowAction()
   222  			}
   223  			if tc.batch {
   224  				err = accessList.AddRules(ctx, tc.config)
   225  				if tests.EvalErr(t, err, tc.config, tc.shouldErr, tc.err) {
   226  					return
   227  				}
   228  			} else {
   229  				for _, rule := range tc.config {
   230  					err = accessList.AddRule(ctx, rule)
   231  					if tests.EvalErr(t, err, tc.config, tc.shouldErr, tc.err) {
   232  						return
   233  					}
   234  				}
   235  			}
   236  
   237  			tc.want["rule_count"] = len(tc.config)
   238  			got := make(map[string]interface{})
   239  			got["allow"] = accessList.Allow(ctx, tc.input)
   240  			got["rule_count"] = len(accessList.GetRules())
   241  
   242  			tests.EvalObjects(t, "eval", tc.want, got)
   243  		})
   244  	}
   245  }
   246  
   247  func TestCustomAccessList(t *testing.T) {
   248  	var testcases = []struct {
   249  		name         string
   250  		config       []*RuleConfiguration
   251  		disabled     bool
   252  		defaultAllow bool
   253  		input        map[string]interface{}
   254  		// want         map[string]interface{}
   255  		want      string
   256  		shouldErr bool
   257  		err       error
   258  	}{
   259  		{
   260  			name: "deny roles foobar",
   261  			config: []*RuleConfiguration{
   262  				{
   263  					Comment: "match roles foobar and deny",
   264  					Conditions: []string{
   265  						"match roles foobar",
   266  					},
   267  					Action: `deny stop log`,
   268  				},
   269  			},
   270  			input: map[string]interface{}{
   271  				"name":  "John Smith",
   272  				"roles": []string{"foobar"},
   273  			},
   274  			want: `{
   275                "allow": false,
   276                "config": {
   277                  "count": 1,
   278                  "rules": [
   279                    {
   280                      "action": "ruleActionDeny",
   281                      "comment": "match roles foobar and deny",
   282                      "conditions": [
   283                        {
   284                          "always_true": false,
   285                          "condition_type": "ruleStrCondExactMatchListStrInput",
   286                          "expr_data_type": "dataTypeStr",
   287                          "field": "roles",
   288                          "input_data_type": "dataTypeListStr",
   289  						"match_any": false,
   290                          "match_strategy": "fieldMatchExact",
   291                          "regex_enabled": false,
   292                          "values": [
   293                            "foobar"
   294                          ]
   295                        }
   296                      ],
   297                      "counter_enabled": false,
   298                      "fields": [
   299                        "roles"
   300                      ],
   301                      "log_level": "info",
   302                      "log_enabled": true,
   303                      "match_all": true,
   304                      "rule_type": "aclRuleDenyWithInfoLoggerStop",
   305                      "tag": "rule0"
   306                    }
   307                  ]
   308                }
   309  			}`,
   310  		},
   311  		{
   312  			name: "deny role foobar with email outside of specific email domain",
   313  			config: []*RuleConfiguration{
   314  				{
   315  					Comment: "deny role foobar with email outside of @bar.foo",
   316  					Conditions: []string{
   317  						"no suffix match email @bar.foo",
   318  						"match role foobar",
   319  					},
   320  					Action: `deny stop log`,
   321  				},
   322  				{
   323  					Comment: "default allow",
   324  					Conditions: []string{
   325  						"match any",
   326  					},
   327  					Action: `allow stop log`,
   328  				},
   329  			},
   330  			input: map[string]interface{}{
   331  				"name":  "John Smith",
   332  				"email": "jsmith@bar.foo",
   333  				"roles": []string{"foobar"},
   334  				"exp":   time.Now().Add(time.Duration(180) * time.Second).UTC().Unix(),
   335  			},
   336  			want: `{
   337                "allow": true,
   338                "config": {
   339                  "count": 2,
   340                  "rules": [
   341                    {
   342                      "action": "ruleActionDeny",
   343                      "comment": "deny role foobar with email outside of @bar.foo",
   344                      "conditions": [
   345                        {
   346                          "always_true": false,
   347                          "condition_type": "ruleStrCondSuffixNegativeMatchStrInput",
   348                          "expr_data_type": "dataTypeStr",
   349                          "field": "email",
   350                          "input_data_type": "dataTypeStr",
   351  						"match_any": false,
   352                          "match_strategy": "fieldMatchSuffix",
   353                          "regex_enabled": false,
   354                          "values": [
   355                            "@bar.foo"
   356                          ]
   357                        },
   358                        {
   359                          "always_true": false,
   360                          "condition_type": "ruleStrCondExactMatchListStrInput",
   361                          "expr_data_type": "dataTypeStr",
   362                          "field": "roles",
   363                          "input_data_type": "dataTypeListStr",
   364  						"match_any": false,
   365                          "match_strategy": "fieldMatchExact",
   366                          "regex_enabled": false,
   367                          "values": [
   368                            "foobar"
   369                          ]
   370                        }
   371                      ],
   372                      "counter_enabled": false,
   373                      "fields": [
   374                        "email",
   375                        "roles"
   376                      ],
   377                      "log_enabled": true,
   378                      "log_level": "info",
   379                      "match_all": true,
   380                      "rule_type": "aclRuleDenyWithInfoLoggerMatchAllStop",
   381                      "tag": "rule0"
   382                    },
   383                    {
   384                      "action": "ruleActionAllow",
   385                      "comment": "default allow",
   386                      "conditions": [
   387                        {
   388                          "always_true": true,
   389                          "condition_type": "ruleAnyCondAlwaysMatchAnyInput",
   390                          "expr_data_type": "dataTypeAny",
   391                          "field": "exp",
   392                          "input_data_type": "dataTypeAny",
   393  						"match_any": false,
   394                          "match_strategy": "fieldMatchAlways",
   395                          "regex_enabled": false
   396                        }
   397                      ],
   398                      "counter_enabled": false,
   399                      "fields": [
   400                        "exp"
   401                      ],
   402                      "log_enabled": true,
   403                      "log_level": "info",
   404                      "match_all": true,
   405                      "rule_type": "aclRuleAllowWithInfoLoggerStop",
   406                      "tag": "rule1"
   407                    }
   408                  ]
   409                }
   410  			}`,
   411  		},
   412  		{
   413  			name: "allow when roles field exists, mutiple conditions, match all",
   414  			config: []*RuleConfiguration{
   415  				{
   416  					Conditions: []string{
   417  						"field roles exists",
   418  						"suffix match email @bar.foo",
   419  					},
   420  					Action: `allow stop`,
   421  				},
   422  				{
   423  					Comment: "default deny",
   424  					Conditions: []string{
   425  						"match any",
   426  					},
   427  					Action: `deny stop log`,
   428  				},
   429  			},
   430  			input: map[string]interface{}{
   431  				"name":  "John Smith",
   432  				"email": "jsmith@bar.foo",
   433  				"roles": []string{"foobar"},
   434  				"exp":   time.Now().Add(time.Duration(180) * time.Second).UTC().Unix(),
   435  			},
   436  			want: `{
   437                "allow": true,
   438                "config": {
   439                  "count": 2,
   440                  "rules": [
   441                    {
   442                      "action": "ruleActionAllow",
   443                      "check_fields": {
   444                        "roles": true
   445                      },
   446                      "conditions": [
   447                        {
   448                          "always_true": false,
   449                          "condition_type": "ruleCondFieldFound",
   450                          "expr_data_type": "dataTypeAny",
   451                          "field": "roles",
   452                          "input_data_type": "dataTypeAny",
   453  						"match_any": false,
   454                          "match_strategy": "fieldFound",
   455                          "regex_enabled": false
   456                        },
   457                        {
   458                          "always_true": false,
   459                          "condition_type": "ruleStrCondSuffixMatchStrInput",
   460                          "expr_data_type": "dataTypeStr",
   461                          "field": "email",
   462                          "input_data_type": "dataTypeStr",
   463  						"match_any": false,
   464                          "match_strategy": "fieldMatchSuffix",
   465                          "regex_enabled": false,
   466                          "values": [
   467                            "@bar.foo"
   468                          ]
   469                        }
   470                      ],
   471                      "counter_enabled": false,
   472                      "fields": [
   473                        "roles",
   474                        "email"
   475                      ],
   476                      "log_enabled": false,
   477                      "match_all": true,
   478                      "rule_type": "aclRuleFieldCheckAllowMatchAllStop"
   479                    },
   480                    {
   481                      "action": "ruleActionDeny",
   482                      "comment": "default deny",
   483                      "conditions": [
   484                        {
   485                          "always_true": true,
   486                          "condition_type": "ruleAnyCondAlwaysMatchAnyInput",
   487                          "expr_data_type": "dataTypeAny",
   488                          "field": "exp",
   489                          "input_data_type": "dataTypeAny",
   490  						"match_any": false,
   491                          "match_strategy": "fieldMatchAlways",
   492                          "regex_enabled": false
   493                        }
   494                      ],
   495                      "counter_enabled": false,
   496                      "fields": [
   497                        "exp"
   498                      ],
   499                      "log_enabled": true,
   500                      "log_level": "info",
   501                      "match_all": true,
   502                      "rule_type": "aclRuleDenyWithInfoLoggerStop",
   503                      "tag": "rule1"
   504                    }
   505                  ]
   506                }
   507              }`,
   508  		},
   509  		{
   510  			name: "allow when roles field exists, mutiple conditions, match any",
   511  			config: []*RuleConfiguration{
   512  				{
   513  					Conditions: []string{
   514  						"field roles exists",
   515  						"suffix match email @bar.foo",
   516  					},
   517  					Action: `allow any stop`,
   518  				},
   519  				{
   520  					Comment: "default deny",
   521  					Conditions: []string{
   522  						"match any",
   523  					},
   524  					Action: `deny stop log`,
   525  				},
   526  			},
   527  			input: map[string]interface{}{
   528  				"name": "John Smith",
   529  				// "email": "jsmith@bar.foo",
   530  				"roles": []string{"foobar"},
   531  				"exp":   time.Now().Add(time.Duration(180) * time.Second).UTC().Unix(),
   532  			},
   533  			want: `{
   534                "allow": true,
   535                "config": {
   536                  "count": 2,
   537                  "rules": [
   538                    {
   539                      "action": "ruleActionAllow",
   540                      "check_fields": {
   541                        "roles": true
   542                      },
   543                      "conditions": [
   544                        {
   545                          "always_true": false,
   546                          "condition_type": "ruleCondFieldFound",
   547                          "expr_data_type": "dataTypeAny",
   548                          "field": "roles",
   549                          "input_data_type": "dataTypeAny",
   550  						"match_any": false,
   551                          "match_strategy": "fieldFound",
   552                          "regex_enabled": false
   553                        },
   554                        {
   555                          "always_true": false,
   556                          "condition_type": "ruleStrCondSuffixMatchStrInput",
   557                          "expr_data_type": "dataTypeStr",
   558                          "field": "email",
   559                          "input_data_type": "dataTypeStr",
   560  						"match_any": false,
   561                          "match_strategy": "fieldMatchSuffix",
   562                          "regex_enabled": false,
   563                          "values": [
   564                            "@bar.foo"
   565                          ]
   566                        }
   567                      ],
   568                      "counter_enabled": false,
   569                      "fields": [
   570                        "roles",
   571                        "email"
   572                      ],
   573                      "log_enabled": false,
   574                      "match_all": false,
   575                      "rule_type": "aclRuleFieldCheckAllowMatchAnyStop"
   576                    },
   577                    {
   578                      "action": "ruleActionDeny",
   579                      "comment": "default deny",
   580                      "conditions": [
   581                        {
   582                          "always_true": true,
   583                          "condition_type": "ruleAnyCondAlwaysMatchAnyInput",
   584                          "expr_data_type": "dataTypeAny",
   585                          "field": "exp",
   586                          "input_data_type": "dataTypeAny",
   587  						"match_any": false,
   588                          "match_strategy": "fieldMatchAlways",
   589                          "regex_enabled": false
   590                        }
   591                      ],
   592                      "counter_enabled": false,
   593                      "fields": [
   594                        "exp"
   595                      ],
   596                      "log_enabled": true,
   597                      "log_level": "info",
   598                      "match_all": true,
   599                      "rule_type": "aclRuleDenyWithInfoLoggerStop",
   600                      "tag": "rule1"
   601                    }
   602                  ]
   603                }
   604              }`,
   605  		},
   606  		{
   607  			name: "allow when roles field exists and role matches",
   608  			config: []*RuleConfiguration{
   609  				{
   610  					Conditions: []string{
   611  						"field roles exists",
   612  						"match role foobar",
   613  					},
   614  					Action: `allow stop`,
   615  				},
   616  				{
   617  					Conditions: []string{
   618  						"match any",
   619  					},
   620  					Action: `deny stop log`,
   621  				},
   622  			},
   623  			input: map[string]interface{}{
   624  				"name":  "John Smith",
   625  				"email": "jsmith@bar.foo",
   626  				"roles": []string{"foobar"},
   627  				"exp":   time.Now().Add(time.Duration(180) * time.Second).UTC().Unix(),
   628  			},
   629  			shouldErr: true,
   630  			err:       errors.ErrACLRuleSyntaxDuplicateField.WithArgs("roles"),
   631  		},
   632  		{
   633  			name: "allow when roles field exists",
   634  			config: []*RuleConfiguration{
   635  				{
   636  					Conditions: []string{
   637  						"field roles exists",
   638  					},
   639  					Action: `allow stop`,
   640  				},
   641  				{
   642  					Comment: "default deny",
   643  					Conditions: []string{
   644  						"match any",
   645  					},
   646  					Action: `deny stop log`,
   647  				},
   648  			},
   649  			input: map[string]interface{}{
   650  				"name":  "John Smith",
   651  				"email": "jsmith@bar.foo",
   652  				"roles": []string{"foobar"},
   653  				"exp":   time.Now().Add(time.Duration(180) * time.Second).UTC().Unix(),
   654  			},
   655  			want: `{
   656                "allow": true,
   657                "config": {
   658                  "count": 2,
   659                  "rules": [
   660                    {
   661                      "action": "ruleActionAllow",
   662                      "conditions": [
   663                        {
   664                          "always_true": false,
   665                          "condition_type": "ruleCondFieldFound",
   666                          "expr_data_type": "dataTypeAny",
   667                          "field": "roles",
   668                          "input_data_type": "dataTypeAny",
   669  						"match_any": false,
   670                          "match_strategy": "fieldFound",
   671                          "regex_enabled": false
   672                        }
   673                      ],
   674                      "counter_enabled": false,
   675                      "fields": [
   676                        "roles"
   677                      ],
   678  					"check_fields": {
   679  					  "roles": true
   680  					},
   681                      "log_enabled": false,
   682                      "match_all": true,
   683                      "rule_type": "aclRuleFieldCheckAllowStop"
   684                    },
   685                    {
   686                      "action": "ruleActionDeny",
   687                      "comment": "default deny",
   688                      "conditions": [
   689                        {
   690                          "always_true": true,
   691                          "condition_type": "ruleAnyCondAlwaysMatchAnyInput",
   692                          "expr_data_type": "dataTypeAny",
   693                          "field": "exp",
   694                          "input_data_type": "dataTypeAny",
   695  						"match_any": false,
   696                          "match_strategy": "fieldMatchAlways",
   697                          "regex_enabled": false
   698                        }
   699                      ],
   700                      "counter_enabled": false,
   701                      "fields": [
   702                        "exp"
   703                      ],
   704                      "log_enabled": true,
   705                      "log_level": "info",
   706                      "match_all": true,
   707                      "rule_type": "aclRuleDenyWithInfoLoggerStop",
   708                      "tag": "rule1"
   709                    }
   710                  ]
   711                }
   712              }`,
   713  		},
   714  		{
   715  			name:     "allow when roles field does not exists",
   716  			disabled: true,
   717  			config: []*RuleConfiguration{
   718  				{
   719  					Conditions: []string{
   720  						"field roles not exists",
   721  					},
   722  					Action: `allow stop log counter`,
   723  				},
   724  				{
   725  					Comment: "default deny",
   726  					Conditions: []string{
   727  						"match any",
   728  					},
   729  					Action: `deny stop log`,
   730  				},
   731  			},
   732  			input: map[string]interface{}{
   733  				"name":  "John Smith",
   734  				"email": "jsmith@bar.foo",
   735  				"exp":   time.Now().Add(time.Duration(180) * time.Second).UTC().Unix(),
   736  			},
   737  			want: `{
   738                "allow": true,
   739                "config": {
   740                  "count": 2,
   741                  "rules": [
   742                    {
   743                      "action": "ruleActionAllow",
   744                      "check_fields": {
   745                        "roles": false
   746                      },
   747                      "conditions": [
   748                        {
   749                          "always_true": false,
   750                          "condition_type": "ruleCondFieldNotFound",
   751                          "expr_data_type": "dataTypeAny",
   752                          "field": "roles",
   753                          "input_data_type": "dataTypeAny",
   754  						"match_any": false,
   755                          "match_strategy": "fieldNotFound",
   756                          "regex_enabled": false
   757                        }
   758                      ],
   759                      "counter_enabled": true,
   760                      "fields": [
   761                        "roles"
   762                      ],
   763                      "log_enabled": true,
   764                      "log_level": "info",
   765                      "match_all": true,
   766                      "rule_type": "aclRuleFieldCheckAllowWithInfoLoggerCounterStop",
   767                      "tag": "rule0"
   768                    },
   769                    {
   770                      "action": "ruleActionDeny",
   771                      "comment": "default deny",
   772                      "conditions": [
   773                        {
   774                          "always_true": true,
   775                          "condition_type": "ruleAnyCondAlwaysMatchAnyInput",
   776                          "expr_data_type": "dataTypeAny",
   777                          "field": "exp",
   778                          "input_data_type": "dataTypeAny",
   779  						"match_any": false,
   780                          "match_strategy": "fieldMatchAlways",
   781                          "regex_enabled": false
   782                        }
   783                      ],
   784                      "counter_enabled": false,
   785                      "fields": [
   786                        "exp"
   787                      ],
   788                      "log_enabled": true,
   789                      "log_level": "info",
   790                      "match_all": true,
   791                      "rule_type": "aclRuleDenyWithInfoLoggerStop",
   792                      "tag": "rule1"
   793                    }
   794                  ]
   795                }
   796              }`,
   797  		},
   798  		{
   799  			name:     "allow when metadata field exists",
   800  			disabled: true,
   801  			config: []*RuleConfiguration{
   802  				{
   803  					Conditions: []string{
   804  						"field metadata exists",
   805  					},
   806  					Action: `allow stop`,
   807  				},
   808  			},
   809  			input: map[string]interface{}{
   810  				"metadata": map[string]interface{}{
   811  					"foo": "bar",
   812  				},
   813  			},
   814  			want: `{
   815              }`,
   816  		},
   817  		{
   818  			name:     "allow when email is not in foo.bar domain",
   819  			disabled: true,
   820  			config: []*RuleConfiguration{
   821  				{
   822  					Conditions: []string{
   823  						"no suffix match email @foo.bar",
   824  					},
   825  					Action: `allow stop`,
   826  				},
   827  			},
   828  			input: map[string]interface{}{
   829  				"email": "jsmith@bar.foo",
   830  			},
   831  			want: `{
   832              }`,
   833  		},
   834  		{
   835  			name:     "allow when custom field foo equals to bar",
   836  			disabled: true,
   837  			config: []*RuleConfiguration{
   838  				{
   839  					Conditions: []string{
   840  						"match foo bar",
   841  					},
   842  					Action: `allow stop`,
   843  				},
   844  			},
   845  			input: map[string]interface{}{
   846  				"foo": "bar",
   847  			},
   848  			want: `{
   849              }`,
   850  		},
   851  		{
   852  			name:     "allow when exp field does not exceed 2 hours lifetime",
   853  			disabled: true,
   854  			config: []*RuleConfiguration{
   855  				{
   856  					Conditions: []string{
   857  						"match expires_at less than 2 hours from now",
   858  					},
   859  					Action: `allow stop`,
   860  				},
   861  				{
   862  					Conditions: []string{
   863  						"match any",
   864  					},
   865  					Action: `deny stop`,
   866  				},
   867  			},
   868  			input: map[string]interface{}{
   869  				"exp": 0,
   870  			},
   871  			want: `{
   872              }`,
   873  		},
   874  	}
   875  
   876  	for _, tc := range testcases {
   877  		t.Run(tc.name, func(t *testing.T) {
   878  			if tc.disabled {
   879  				t.SkipNow()
   880  			}
   881  			var err error
   882  			ctx := context.Background()
   883  			logger := logutil.NewLogger()
   884  			accessList := NewAccessList()
   885  			accessList.SetLogger(logger)
   886  			if tc.defaultAllow {
   887  				accessList.SetDefaultAllowAction()
   888  			}
   889  			err = accessList.AddRules(ctx, tc.config)
   890  			if tests.EvalErr(t, err, tc.config, tc.shouldErr, tc.err) {
   891  				return
   892  			}
   893  			got := make(map[string]interface{})
   894  			got["config"] = tests.Unpack(t, accessList.AsMap())
   895  			got["allow"] = accessList.Allow(ctx, tc.input)
   896  			want := tests.Unpack(t, tc.want)
   897  
   898  			if diff := cmp.Diff(want, got); diff != "" {
   899  				t.Logf("JSON: %s", tests.UnpackJSON(t, got))
   900  				t.Errorf("NewPortal() rule mismatch (-want +got):\n%s", diff)
   901  			}
   902  		})
   903  	}
   904  }