github.com/greenpau/go-authcrunch@v1.1.4/pkg/authz/validator/validator_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 validator
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"net/http"
    21  	"net/http/httptest"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/greenpau/go-authcrunch/internal/tests"
    26  	"github.com/greenpau/go-authcrunch/internal/testutils"
    27  	"github.com/greenpau/go-authcrunch/pkg/acl"
    28  	"github.com/greenpau/go-authcrunch/pkg/authz/options"
    29  	"github.com/greenpau/go-authcrunch/pkg/errors"
    30  	"github.com/greenpau/go-authcrunch/pkg/kms"
    31  	"github.com/greenpau/go-authcrunch/pkg/requests"
    32  	"github.com/greenpau/go-authcrunch/pkg/user"
    33  	logutil "github.com/greenpau/go-authcrunch/pkg/util/log"
    34  
    35  	"github.com/google/go-cmp/cmp"
    36  )
    37  
    38  var (
    39  
    40  	// Create access list with default deny that allows read:books only
    41  	defaultDenyACL = []*acl.RuleConfiguration{
    42  		{
    43  			Comment: "allow read:books scope",
    44  			Conditions: []string{
    45  				"match scopes read:books",
    46  			},
    47  			Action: `allow log`,
    48  		},
    49  	}
    50  
    51  	// Create access list with default allow that denies write:books
    52  	defaultAllowACL = []*acl.RuleConfiguration{
    53  		{
    54  			Comment: "deny write:books scope",
    55  			Conditions: []string{
    56  				"match scopes write:books",
    57  			},
    58  			Action: `deny`,
    59  		},
    60  		{
    61  			Comment: "allow all scopes",
    62  			Conditions: []string{
    63  				"field scopes exists",
    64  			},
    65  			Action: `allow`,
    66  		},
    67  	}
    68  
    69  	// Create access list with default deny that allows 127.0.0.1 only
    70  	audienceDefaultDenyACL = []*acl.RuleConfiguration{
    71  		{
    72  			Conditions: []string{
    73  				"match aud https://127.0.0.1:2019/",
    74  			},
    75  			Action: `allow`,
    76  		},
    77  	}
    78  
    79  	// Create access list with default allow that denies localhost
    80  	audienceDefaultAllowACL = []*acl.RuleConfiguration{
    81  		{
    82  			Conditions: []string{
    83  				"match aud https://localhost/",
    84  			},
    85  			Action: `deny`,
    86  		},
    87  		{
    88  			Comment: "allow all audiences",
    89  			Conditions: []string{
    90  				"field audience exists",
    91  			},
    92  			Action: `allow`,
    93  		},
    94  	}
    95  
    96  	// Create access list with default deny and HTTP Method and Path rules
    97  	customACL = []*acl.RuleConfiguration{
    98  		{
    99  			Conditions: []string{
   100  				"match scope write:books",
   101  				"match method GET",
   102  				"match path /app/page1/blocked",
   103  			},
   104  			Action: `deny`,
   105  		},
   106  		{
   107  			Conditions: []string{
   108  				"match scope write:books",
   109  				"match method GET",
   110  				"match path /app/page2/blocked",
   111  			},
   112  			Action: `deny`,
   113  		},
   114  		{
   115  			Conditions: []string{
   116  				"match scope write:books",
   117  				"match method GET",
   118  				"match path /app/page3/allowed",
   119  			},
   120  			Action: `allow`,
   121  		},
   122  		{
   123  			Conditions: []string{
   124  				"match scope read:books",
   125  			},
   126  			Action: `allow`,
   127  		},
   128  	}
   129  
   130  	// Create access list with default deny and mixed claims
   131  	mixedACL = []*acl.RuleConfiguration{
   132  		{
   133  			Conditions: []string{
   134  				"match scope write:books",
   135  			},
   136  			Action: `allow`,
   137  		},
   138  		{
   139  			Conditions: []string{
   140  				"match audience https://127.0.0.1:2019/",
   141  			},
   142  			Action: `allow`,
   143  		},
   144  	}
   145  
   146  	// Create viewer persona
   147  	viewer = `{
   148          "exp": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute).Unix()) + `,
   149          "iat": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute*-1).Unix()) + `,
   150          "nbf": ` + fmt.Sprintf("%d", time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix()) + `,
   151          "aud": ["https://127.0.0.1:2019/", "https://google.com/"],
   152          "sub": "smithj@outlook.com",
   153          "scope": ["read:books"]
   154      }`
   155  
   156  	editor = `{
   157          "exp": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute).Unix()) + `,
   158          "iat": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute*-1).Unix()) + `,
   159          "nbf": ` + fmt.Sprintf("%d", time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix()) + `,
   160          "aud": "https://localhost/",
   161          "sub": "jane.smith@outlook.com",
   162          "scope": ["write:books"]
   163      }`
   164  
   165  	// Create access list with default deny that allows viewer only
   166  	defaultRolesDenyACL = []*acl.RuleConfiguration{
   167  		{
   168  			Conditions: []string{
   169  				"match role viewer",
   170  			},
   171  			Action: `allow`,
   172  		},
   173  	}
   174  
   175  	denyViewerAllowOthersACL = []*acl.RuleConfiguration{
   176  		{
   177  			Conditions: []string{
   178  				"match role viewer",
   179  			},
   180  			Action: `deny`,
   181  		},
   182  		{
   183  			Conditions: []string{
   184  				"field roles exists",
   185  			},
   186  			Action: `allow`,
   187  		},
   188  	}
   189  
   190  	// Create access list with default allow that denies editor
   191  	defaultRolesAllowACL = []*acl.RuleConfiguration{
   192  		{
   193  			Conditions: []string{
   194  				"match role editor",
   195  			},
   196  			Action: `deny`,
   197  		},
   198  		{
   199  			Conditions: []string{
   200  				"field roles exists",
   201  			},
   202  			Action: `allow`,
   203  		},
   204  	}
   205  
   206  	// Create access list with default deny and HTTP Method and Path rules
   207  	customRolesACL = []*acl.RuleConfiguration{
   208  		{
   209  			Conditions: []string{
   210  				"match role editor",
   211  				"match method GET",
   212  				"match path /app/page1/blocked",
   213  			},
   214  			Action: `deny log`,
   215  		},
   216  		{
   217  			Conditions: []string{
   218  				"match role editor",
   219  				"match method GET",
   220  				"match path /app/page2/blocked",
   221  			},
   222  			Action: `deny log`,
   223  		},
   224  		{
   225  			Conditions: []string{
   226  				"match role editor",
   227  				"match method GET",
   228  				"match path /app/page3/allowed",
   229  			},
   230  			Action: `allow log`,
   231  		},
   232  		{
   233  			Conditions: []string{
   234  				"match role viewer",
   235  			},
   236  			Action: `allow log`,
   237  		},
   238  	}
   239  
   240  	// Create viewer persona
   241  	viewer2 = `{
   242          "exp": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute).Unix()) + `,
   243          "iat": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute*-1).Unix()) + `,
   244          "nbf": ` + fmt.Sprintf("%d", time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix()) + `,
   245          "name":   "Smith, John",
   246          "email":  "smithj@outlook.com",
   247          "origin": "localhost",
   248          "sub":    "smithj@outlook.com",
   249          "roles": ["viewer"],
   250          "addr": "10.10.10.10"
   251      }`
   252  
   253  	editor2 = `{
   254          "exp": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute).Unix()) + `,
   255          "iat": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute*-1).Unix()) + `,
   256          "nbf": ` + fmt.Sprintf("%d", time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix()) + `,
   257          "name":   "Smith, Jane",
   258          "email":  "jane.smith@outlook.com",
   259          "origin": "localhost",
   260          "sub":    "jane.smith@outlook.com",
   261          "roles": ["editor"]
   262      }`
   263  
   264  	viewer3 = `{
   265          "exp": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute).Unix()) + `,
   266          "iat": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute*-1).Unix()) + `,
   267          "nbf": ` + fmt.Sprintf("%d", time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix()) + `,
   268          "name":   "Smith, John",
   269          "email":  "smithj@outlook.com",
   270          "origin": "localhost",
   271          "sub":    "smithj@outlook.com",
   272          "roles": ["viewer"],
   273  		"acl":{
   274  			"paths": {
   275  				"/**/allowed": {}
   276  			}
   277  		}
   278      }`
   279  
   280  	viewer4 = `{
   281          "exp": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute).Unix()) + `,
   282          "iat": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute*-1).Unix()) + `,
   283          "nbf": ` + fmt.Sprintf("%d", time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix()) + `,
   284          "name":   "Smith, John",
   285          "email":  "smithj@outlook.com",
   286          "origin": "localhost",
   287          "sub":    "smithj@outlook.com",
   288          "roles": ["viewer"],
   289          "addr": "10.10.10.10",
   290          "acl":{
   291              "paths": {
   292                  "/**/allowed": {}
   293              }
   294          }
   295      }`
   296  
   297  	viewer5 = `{
   298          "exp": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute).Unix()) + `,
   299          "iat": ` + fmt.Sprintf("%d", time.Now().Add(10*time.Minute*-1).Unix()) + `,
   300          "nbf": ` + fmt.Sprintf("%d", time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix()) + `,
   301          "name":   "Smith, John",
   302          "email":  "smithj@outlook.com",
   303          "origin": "localhost",
   304          "sub":    "smithj@outlook.com",
   305          "roles": ["viewer"],
   306          "addr": "2001:DB8::21f:5bff:febf:ce22:8a2e"
   307      }`
   308  )
   309  
   310  func TestAuthorize(t *testing.T) {
   311  	testcases := []struct {
   312  		name string
   313  		// disabled                    bool
   314  		claims                      string
   315  		config                      []*acl.RuleConfiguration
   316  		method                      string
   317  		path                        string
   318  		sourceAddress               string
   319  		enableBearer                bool
   320  		cacheUser                   bool
   321  		validateAccessListPathClaim bool
   322  		validateSourceAddress       bool
   323  		validateMethodPath          bool
   324  		optionsDisabled             bool
   325  		want                        map[string]interface{}
   326  		shouldErr                   bool
   327  		err                         error
   328  	}{
   329  		// Access list with default deny that allows viewer only
   330  		{
   331  			name:   "user with viewer scope claim and default deny acl",
   332  			claims: viewer, config: defaultDenyACL, method: "GET", path: "/app/viewer", shouldErr: false,
   333  			validateMethodPath: true,
   334  		},
   335  		{
   336  			name:   "user with editor scope claim and default deny acl",
   337  			claims: editor, config: defaultDenyACL, method: "GET", path: "/app/viewer", shouldErr: true, err: errors.ErrAccessNotAllowed,
   338  			validateMethodPath: true,
   339  		},
   340  		// Access list with default allow that denies editor
   341  		{
   342  			name:   "user with viewer scope claim and default allow acl",
   343  			claims: viewer, config: defaultAllowACL, method: "GET", path: "/app/viewer", shouldErr: false,
   344  			validateMethodPath: true,
   345  		},
   346  		{
   347  			name:   "user with editor scope claim and default allow acl",
   348  			claims: editor, config: defaultAllowACL, method: "GET", path: "/app/viewer", shouldErr: true, err: errors.ErrAccessNotAllowed,
   349  			validateMethodPath: true,
   350  		},
   351  		// Access list with default deny that allows 127.0.0.1 only
   352  		{
   353  			name:   "user with viewer scope claim and audience deny acl",
   354  			claims: viewer, config: audienceDefaultDenyACL, method: "GET", path: "/app/viewer", shouldErr: false,
   355  			validateMethodPath: true,
   356  		},
   357  		{
   358  			name:   "user with editor scope claim and audience deny acl",
   359  			claims: editor, config: audienceDefaultDenyACL, method: "GET", path: "/app/viewer", shouldErr: true, err: errors.ErrAccessNotAllowed,
   360  			validateMethodPath: true,
   361  		},
   362  		// Access list with default allow that denies localhost
   363  		{
   364  			name:   "user with viewer scope claim and audience allow acl",
   365  			claims: viewer, config: audienceDefaultAllowACL, method: "GET", path: "/app/viewer", shouldErr: false,
   366  			validateMethodPath: true,
   367  		},
   368  		{
   369  			name:   "user with editor scope claim and audience allow acl",
   370  			claims: editor, config: audienceDefaultAllowACL, method: "GET", path: "/app/viewer", shouldErr: true, err: errors.ErrAccessNotAllowed,
   371  			validateMethodPath: true,
   372  		},
   373  		// Custom ACL
   374  		{
   375  			name:   "user with viewer scope claim and custom acl going to /app/page1/blocked via get",
   376  			claims: viewer, config: customACL, method: "GET", path: "/app/page1/blocked", shouldErr: false,
   377  			validateMethodPath: true,
   378  		},
   379  		{
   380  			name:   "user with viewer scope claim and custom acl going to /app/page2/blocked via get",
   381  			claims: viewer, config: customACL, method: "GET", path: "/app/page2/blocked", shouldErr: false,
   382  			validateMethodPath: true,
   383  		},
   384  		{
   385  			name:   "user with viewer scope claim and custom acl going to /app/page3/allowed via get",
   386  			claims: viewer, config: customACL, method: "GET", path: "/app/page3/allowed", shouldErr: false,
   387  			validateMethodPath: true,
   388  		},
   389  		{
   390  			name:   "user with editor scope claim and custom acl going to /app/page1/blocked via get",
   391  			claims: editor, config: customACL, method: "GET", path: "/app/page1/blocked", shouldErr: true, err: errors.ErrAccessNotAllowed,
   392  			validateMethodPath: true,
   393  		},
   394  		{
   395  			name:   "user with editor scope claim and custom acl going to /app/page2/blocked via get",
   396  			claims: editor, config: customACL, method: "GET", path: "/app/page2/blocked", shouldErr: true, err: errors.ErrAccessNotAllowed,
   397  			validateMethodPath: true,
   398  		},
   399  		{
   400  			name:   "user with editor scope claim and custom acl going to /app/page3/allowed via get",
   401  			claims: editor, config: customACL, method: "GET", path: "/app/page3/allowed", shouldErr: false,
   402  			validateMethodPath: true,
   403  		},
   404  		// Mixed ACL
   405  		{
   406  			name:   "user with viewer scope and audience claims and custom acl",
   407  			claims: viewer, config: mixedACL, method: "GET", path: "/app/page1/blocked", shouldErr: false,
   408  			validateMethodPath: true,
   409  		},
   410  		{
   411  			name:   "user with editor scope and localhost audience claims and mixed acl",
   412  			claims: editor, config: mixedACL, method: "GET", path: "/app/editor", shouldErr: false,
   413  			validateMethodPath: true,
   414  		},
   415  		// Role-based ACLs.
   416  		{
   417  			name:   "user with viewer role claim and default deny acl going to app/viewer via get",
   418  			claims: viewer2, config: defaultRolesDenyACL, method: "GET", path: "/app/viewer", shouldErr: false,
   419  			enableBearer:       true,
   420  			validateMethodPath: true,
   421  		},
   422  		{
   423  			name:   "user with viewer role claim and default deny acl going to app/editor via get",
   424  			claims: viewer2, config: defaultRolesDenyACL, method: "GET", path: "/app/editor", shouldErr: false,
   425  			enableBearer:       true,
   426  			validateMethodPath: true,
   427  		},
   428  		{
   429  			name:   "user with viewer role claim and default deny acl going to app/admin via get",
   430  			claims: viewer2, config: defaultRolesDenyACL, method: "GET", path: "/app/admin", shouldErr: false,
   431  			enableBearer:       true,
   432  			validateMethodPath: true,
   433  		},
   434  		{
   435  			name:   "user with editor role claim and default deny acl going to app/viewer via get",
   436  			claims: editor2, config: defaultRolesDenyACL, method: "GET", path: "/app/viewer", shouldErr: true, err: errors.ErrAccessNotAllowed,
   437  			enableBearer:       true,
   438  			validateMethodPath: true,
   439  		},
   440  		{
   441  			name:   "user with editor role claim and default deny acl going to app/editor via get",
   442  			claims: editor2, config: defaultRolesDenyACL, method: "GET", path: "/app/editor", shouldErr: true, err: errors.ErrAccessNotAllowed,
   443  			enableBearer:       true,
   444  			validateMethodPath: true,
   445  		},
   446  		{
   447  			name:   "user with editor role claim and default deny acl going to app/admin via get",
   448  			claims: editor2, config: defaultRolesDenyACL, method: "GET", path: "/app/admin", shouldErr: true, err: errors.ErrAccessNotAllowed,
   449  			enableBearer:       true,
   450  			validateMethodPath: true,
   451  		},
   452  		// Access list with default allow that denies editor
   453  		{
   454  			name:   "user with viewer role claim and default allow acl going to app/viewer via get",
   455  			claims: viewer2, config: defaultRolesAllowACL, method: "GET", path: "/app/viewer", shouldErr: false,
   456  			enableBearer:       true,
   457  			validateMethodPath: true,
   458  		},
   459  		{
   460  			name:   "user with viewer role claim and default allow acl going to app/editor via get",
   461  			claims: viewer2, config: defaultRolesAllowACL, method: "GET", path: "/app/editor", shouldErr: false,
   462  			enableBearer:       true,
   463  			validateMethodPath: true,
   464  		},
   465  		{
   466  			name:   "user with viewer role claim and default allow acl going to app/admin via get",
   467  			claims: viewer2, config: defaultRolesAllowACL, method: "GET", path: "/app/admin", shouldErr: false,
   468  			enableBearer:       true,
   469  			validateMethodPath: true,
   470  		},
   471  		{
   472  			name:   "user with editor role claim and default allow acl going to app/viewer via get",
   473  			claims: editor2, config: defaultRolesAllowACL, method: "GET", path: "/app/viewer", shouldErr: true, err: errors.ErrAccessNotAllowed,
   474  			enableBearer:       true,
   475  			validateMethodPath: true,
   476  		},
   477  		{
   478  			name:   "user with editor role claim and default allow acl going to app/editor via get",
   479  			claims: editor2, config: defaultRolesAllowACL, method: "GET", path: "/app/editor", shouldErr: true, err: errors.ErrAccessNotAllowed,
   480  			enableBearer:       true,
   481  			validateMethodPath: true,
   482  		},
   483  		{
   484  			name:   "user with editor role claim and default allow acl going to app/admin via get",
   485  			claims: editor2, config: defaultRolesAllowACL, method: "GET", path: "/app/admin", shouldErr: true, err: errors.ErrAccessNotAllowed,
   486  			enableBearer:       true,
   487  			validateMethodPath: true,
   488  		},
   489  		// Custom ACL
   490  		{
   491  			name:   "user with editor role claim and custom acl going to /app/page1/blocked via get",
   492  			claims: editor2, config: customRolesACL, method: "GET", path: "/app/page1/blocked", shouldErr: true, err: errors.ErrAccessNotAllowed,
   493  			enableBearer:       true,
   494  			validateMethodPath: true,
   495  		},
   496  		{
   497  			name:   "user with editor role claim and custom acl going to /app/page2/blocked via get",
   498  			claims: editor2, config: customRolesACL, method: "GET", path: "/app/page2/blocked", shouldErr: true, err: errors.ErrAccessNotAllowed,
   499  			enableBearer:       true,
   500  			validateMethodPath: true,
   501  		},
   502  		{
   503  			name:   "user with editor role claim and custom acl going to /app/page3/allowed via get",
   504  			claims: editor2, config: customRolesACL, method: "GET", path: "/app/page3/allowed", shouldErr: false,
   505  			enableBearer:       true,
   506  			validateMethodPath: true,
   507  		},
   508  		{
   509  			name:   "user with viewer role claim and custom acl going to /app/page1/blocked via get",
   510  			claims: viewer2, config: customRolesACL, method: "GET", path: "/app/page1/blocked", shouldErr: false,
   511  			enableBearer:       true,
   512  			validateMethodPath: true,
   513  		},
   514  		{
   515  			name:   "user with viewer role claim and custom acl going to /app/page2/blocked via get",
   516  			claims: viewer2, config: customRolesACL, method: "GET", path: "/app/page2/blocked", shouldErr: false,
   517  			enableBearer:       true,
   518  			validateMethodPath: true,
   519  		},
   520  		{
   521  			name:   "user with viewer role claim and custom acl going to /app/page3/allowed via get",
   522  			claims: viewer2, config: customRolesACL, method: "GET", path: "/app/page3/allowed", shouldErr: false,
   523  			enableBearer:       true,
   524  			validateMethodPath: true,
   525  		},
   526  		// Token based ACL
   527  		{
   528  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with src addr",
   529  			claims:                      viewer4,
   530  			config:                      defaultRolesDenyACL,
   531  			method:                      "GET",
   532  			path:                        "/app/page3/allowed",
   533  			shouldErr:                   false,
   534  			validateAccessListPathClaim: true,
   535  			validateMethodPath:          true,
   536  			validateSourceAddress:       true,
   537  			sourceAddress:               "10.10.10.10",
   538  		},
   539  		{
   540  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with src addr and block path acl",
   541  			claims:                      viewer4,
   542  			config:                      defaultRolesDenyACL,
   543  			method:                      "GET",
   544  			path:                        "/app/page3/blocked",
   545  			validateAccessListPathClaim: true,
   546  			validateMethodPath:          true,
   547  			validateSourceAddress:       true,
   548  			sourceAddress:               "10.10.10.10",
   549  			shouldErr:                   true,
   550  			err:                         errors.ErrAccessNotAllowedByPathACL,
   551  		},
   552  		{
   553  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with acl block with src addr",
   554  			claims:                      viewer3,
   555  			config:                      denyViewerAllowOthersACL,
   556  			method:                      "GET",
   557  			path:                        "/app/page3/denied",
   558  			validateAccessListPathClaim: true,
   559  			validateMethodPath:          true,
   560  			validateSourceAddress:       true,
   561  			sourceAddress:               "10.10.10.10",
   562  			shouldErr:                   true,
   563  			err:                         errors.ErrAccessNotAllowed,
   564  		},
   565  		{
   566  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with acl block with src addr",
   567  			claims:                      viewer2,
   568  			config:                      defaultRolesAllowACL,
   569  			method:                      "GET",
   570  			path:                        "/app/page3/denied",
   571  			validateAccessListPathClaim: true,
   572  			validateMethodPath:          true,
   573  			validateSourceAddress:       true,
   574  			sourceAddress:               "10.10.10.10",
   575  			shouldErr:                   true,
   576  			err:                         errors.ErrAccessNotAllowedByPathACL,
   577  		},
   578  		{
   579  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with acl block with src addr mismatch",
   580  			claims:                      viewer2,
   581  			config:                      defaultRolesAllowACL,
   582  			method:                      "GET",
   583  			path:                        "/app/page3/denied",
   584  			validateAccessListPathClaim: true,
   585  			validateMethodPath:          true,
   586  			validateSourceAddress:       true,
   587  			sourceAddress:               "20.20.20.20",
   588  			shouldErr:                   true,
   589  			err:                         errors.ErrSourceAddressMismatch.WithArgs("10.10.10.10", "20.20.20.20"),
   590  		},
   591  		{
   592  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get",
   593  			claims:                      viewer3,
   594  			config:                      defaultRolesDenyACL,
   595  			method:                      "GET",
   596  			path:                        "/app/page3/allowed",
   597  			shouldErr:                   false,
   598  			validateAccessListPathClaim: true,
   599  			validateMethodPath:          true,
   600  		},
   601  		{
   602  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with acl block",
   603  			claims:                      viewer3,
   604  			config:                      denyViewerAllowOthersACL,
   605  			method:                      "GET",
   606  			path:                        "/app/page3/denied",
   607  			validateAccessListPathClaim: true,
   608  			validateMethodPath:          true,
   609  			shouldErr:                   true,
   610  			err:                         errors.ErrAccessNotAllowed,
   611  		},
   612  		{
   613  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with acl block",
   614  			claims:                      viewer2,
   615  			config:                      defaultRolesAllowACL,
   616  			method:                      "GET",
   617  			path:                        "/app/page3/denied",
   618  			validateAccessListPathClaim: true,
   619  			validateMethodPath:          true,
   620  			shouldErr:                   true,
   621  			err:                         errors.ErrAccessNotAllowedByPathACL,
   622  		},
   623  		{
   624  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with deny acl",
   625  			claims:                      viewer3,
   626  			config:                      defaultRolesDenyACL,
   627  			method:                      "GET",
   628  			path:                        "/app/page3/denied",
   629  			validateAccessListPathClaim: true,
   630  			shouldErr:                   true,
   631  			err:                         errors.ErrAccessNotAllowedByPathACL,
   632  		},
   633  		{
   634  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get without method and path",
   635  			claims:                      viewer3,
   636  			config:                      denyViewerAllowOthersACL,
   637  			validateAccessListPathClaim: true,
   638  			method:                      "GET",
   639  			path:                        "/app/page3/allowed",
   640  			shouldErr:                   true,
   641  			err:                         errors.ErrAccessNotAllowed,
   642  		},
   643  		{
   644  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with src addr",
   645  			claims:                      viewer3,
   646  			config:                      defaultRolesDenyACL,
   647  			method:                      "GET",
   648  			path:                        "/app/page3/allowed",
   649  			validateAccessListPathClaim: true,
   650  			validateSourceAddress:       true,
   651  			shouldErr:                   true,
   652  			err:                         errors.ErrSourceAddressNotFound,
   653  		},
   654  		{
   655  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with deny acl and with src addr and no ip match",
   656  			claims:                      viewer4,
   657  			config:                      defaultRolesDenyACL,
   658  			method:                      "GET",
   659  			path:                        "/app/page3/denied",
   660  			validateAccessListPathClaim: true,
   661  			validateSourceAddress:       true,
   662  			sourceAddress:               "20.20.20.20",
   663  			shouldErr:                   true,
   664  			err:                         errors.ErrSourceAddressMismatch.WithArgs("10.10.10.10", "20.20.20.20"),
   665  		},
   666  		{
   667  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with deny acl and with src addr and ip match",
   668  			claims:                      viewer4,
   669  			config:                      defaultRolesDenyACL,
   670  			method:                      "GET",
   671  			path:                        "/app/page3/allowed",
   672  			validateAccessListPathClaim: true,
   673  			validateSourceAddress:       true,
   674  			sourceAddress:               "10.10.10.10",
   675  		},
   676  		{
   677  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with deny acl and with src addr and no ip block",
   678  			claims:                      viewer4,
   679  			config:                      defaultRolesDenyACL,
   680  			method:                      "GET",
   681  			path:                        "/app/page3/denied",
   682  			validateAccessListPathClaim: true,
   683  			validateSourceAddress:       true,
   684  			sourceAddress:               "10.10.10.10",
   685  			shouldErr:                   true,
   686  			err:                         errors.ErrAccessNotAllowedByPathACL,
   687  		},
   688  		{
   689  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with deny acl and with src addr and no acl",
   690  			claims:                      viewer2,
   691  			config:                      defaultRolesDenyACL,
   692  			method:                      "GET",
   693  			path:                        "/app/page3/denied",
   694  			validateAccessListPathClaim: true,
   695  			validateSourceAddress:       true,
   696  			sourceAddress:               "10.10.10.10",
   697  			shouldErr:                   true,
   698  			err:                         errors.ErrAccessNotAllowedByPathACL,
   699  		},
   700  		{
   701  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get without method and path and with src addr",
   702  			claims:                      viewer3,
   703  			config:                      denyViewerAllowOthersACL,
   704  			validateAccessListPathClaim: true,
   705  			validateSourceAddress:       true,
   706  			method:                      "GET",
   707  			path:                        "/app/page3/allowed",
   708  			shouldErr:                   true,
   709  			err:                         errors.ErrAccessNotAllowed,
   710  		},
   711  
   712  		{
   713  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get without acl",
   714  			claims:                      viewer,
   715  			config:                      defaultRolesAllowACL,
   716  			validateAccessListPathClaim: true,
   717  			method:                      "GET",
   718  			path:                        "/app/page3/allowed",
   719  			shouldErr:                   true,
   720  			err:                         errors.ErrAccessNotAllowedByPathACL,
   721  		},
   722  		{
   723  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get without method and path",
   724  			claims:                      viewer3,
   725  			config:                      defaultRolesDenyACL,
   726  			method:                      "GET",
   727  			path:                        "/app/page3/allowed",
   728  			shouldErr:                   false,
   729  			validateAccessListPathClaim: true,
   730  		},
   731  		{
   732  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with source address",
   733  			claims:                      viewer3,
   734  			config:                      defaultRolesDenyACL,
   735  			method:                      "GET",
   736  			path:                        "/app/page3/allowed",
   737  			shouldErr:                   true,
   738  			err:                         errors.ErrSourceAddressNotFound,
   739  			validateAccessListPathClaim: true,
   740  			validateSourceAddress:       true,
   741  			validateMethodPath:          true,
   742  		},
   743  		{
   744  			name:                        "user with viewer role claim and token-based acl going to /app/page3/allowed via get with source address and without method and path",
   745  			claims:                      viewer3,
   746  			config:                      defaultRolesDenyACL,
   747  			method:                      "GET",
   748  			path:                        "/app/page3/allowed",
   749  			shouldErr:                   true,
   750  			err:                         errors.ErrSourceAddressNotFound,
   751  			validateAccessListPathClaim: true,
   752  			validateSourceAddress:       true,
   753  		},
   754  		{
   755  			name:                        "user with viewer role claim and token-based acl going to /app/page2/blocked via get",
   756  			claims:                      viewer3,
   757  			config:                      defaultRolesDenyACL,
   758  			method:                      "GET",
   759  			path:                        "/app/page2/blocked",
   760  			validateAccessListPathClaim: true,
   761  			validateMethodPath:          true,
   762  			shouldErr:                   true,
   763  			err:                         errors.ErrAccessNotAllowedByPathACL,
   764  		},
   765  		{
   766  			name:      "user with viewer role claim going to /app/page2/blocked via get",
   767  			claims:    viewer3,
   768  			config:    denyViewerAllowOthersACL,
   769  			method:    "GET",
   770  			path:      "/app/page2/blocked",
   771  			shouldErr: true,
   772  			err:       errors.ErrAccessNotAllowed,
   773  		},
   774  		{
   775  			name:               "access list not set",
   776  			claims:             viewer,
   777  			method:             "GET",
   778  			path:               "/app/page3/allowed",
   779  			validateMethodPath: true,
   780  			shouldErr:          true,
   781  			err:                errors.ErrNoAccessList,
   782  		},
   783  		{
   784  			name:               "empty token",
   785  			config:             defaultAllowACL,
   786  			method:             "GET",
   787  			path:               "/app/page3/allowed",
   788  			validateMethodPath: true,
   789  			shouldErr:          true,
   790  			err:                errors.ErrCryptoKeyStoreParseTokenFailed,
   791  		},
   792  		{
   793  			name:               "bad token",
   794  			config:             defaultAllowACL,
   795  			method:             "GET",
   796  			path:               "/app/page3/allowed",
   797  			validateMethodPath: true,
   798  			shouldErr:          true,
   799  			err:                errors.ErrCryptoKeyStoreParseTokenFailed,
   800  		},
   801  		{
   802  			name:               "no acl rules",
   803  			claims:             viewer,
   804  			config:             defaultAllowACL,
   805  			method:             "GET",
   806  			path:               "/app/page3/allowed",
   807  			validateMethodPath: true,
   808  			shouldErr:          true,
   809  			err:                errors.ErrAccessListNoRules,
   810  		},
   811  		{
   812  			name:               "no verify keys",
   813  			claims:             viewer,
   814  			config:             defaultAllowACL,
   815  			method:             "GET",
   816  			path:               "/app/page3/allowed",
   817  			validateMethodPath: true,
   818  			shouldErr:          true,
   819  			err:                errors.ErrValidatorCryptoKeyStoreNoKeys,
   820  		},
   821  		{
   822  			name:                  "token without ip address",
   823  			claims:                viewer,
   824  			config:                defaultAllowACL,
   825  			method:                "GET",
   826  			path:                  "/app/page3/allowed",
   827  			validateSourceAddress: true,
   828  			shouldErr:             true,
   829  			err:                   errors.ErrSourceAddressNotFound,
   830  		},
   831  		{
   832  			name:                  "token ip address and client ip address not match",
   833  			claims:                viewer2,
   834  			config:                defaultRolesAllowACL,
   835  			method:                "GET",
   836  			path:                  "/app/page3/allowed",
   837  			validateSourceAddress: true,
   838  			sourceAddress:         "20.20.20.20",
   839  			shouldErr:             true,
   840  			err:                   errors.ErrSourceAddressMismatch.WithArgs("10.10.10.10", "20.20.20.20"),
   841  		},
   842  		{
   843  			name:                  "token ip address and client ip address match",
   844  			claims:                viewer2,
   845  			config:                defaultRolesAllowACL,
   846  			method:                "GET",
   847  			path:                  "/app/page3/allowed",
   848  			validateSourceAddress: true,
   849  			sourceAddress:         "10.10.10.10",
   850  		},
   851  		{
   852  			name:      "cached user",
   853  			claims:    viewer2,
   854  			config:    defaultRolesAllowACL,
   855  			method:    "GET",
   856  			path:      "/app/page3/allowed",
   857  			cacheUser: true,
   858  		},
   859  		{
   860  			name:                  "token ip address and client ip address match but not roles",
   861  			claims:                viewer2,
   862  			config:                denyViewerAllowOthersACL,
   863  			method:                "GET",
   864  			path:                  "/app/page3/allowed",
   865  			validateSourceAddress: true,
   866  			sourceAddress:         "10.10.10.10",
   867  			shouldErr:             true,
   868  			err:                   errors.ErrAccessNotAllowed,
   869  		},
   870  		{
   871  			name:                  "token without ip address with method and path",
   872  			claims:                viewer,
   873  			config:                defaultAllowACL,
   874  			method:                "GET",
   875  			path:                  "/app/page3/allowed",
   876  			validateSourceAddress: true,
   877  			validateMethodPath:    true,
   878  			shouldErr:             true,
   879  			err:                   errors.ErrSourceAddressNotFound,
   880  		},
   881  		{
   882  			name:                  "token without ip address with method and path and with acl block",
   883  			claims:                viewer,
   884  			config:                defaultRolesDenyACL,
   885  			method:                "GET",
   886  			path:                  "/app/page3/allowed",
   887  			validateSourceAddress: true,
   888  			validateMethodPath:    true,
   889  			shouldErr:             true,
   890  			err:                   errors.ErrAccessNotAllowed,
   891  		},
   892  		{
   893  			name:                  "token without ip address with method and path and without acl block",
   894  			claims:                viewer2,
   895  			config:                defaultRolesAllowACL,
   896  			method:                "GET",
   897  			path:                  "/app/page3/allowed",
   898  			validateSourceAddress: true,
   899  			validateMethodPath:    true,
   900  			sourceAddress:         "10.10.10.10",
   901  		},
   902  		{
   903  			name:                  "token ip address and client ip address not match with method and path",
   904  			claims:                viewer2,
   905  			config:                defaultRolesAllowACL,
   906  			method:                "GET",
   907  			path:                  "/app/page3/allowed",
   908  			validateSourceAddress: true,
   909  			validateMethodPath:    true,
   910  			sourceAddress:         "20.20.20.20",
   911  			shouldErr:             true,
   912  			err:                   errors.ErrSourceAddressMismatch.WithArgs("10.10.10.10", "20.20.20.20"),
   913  		},
   914  		{
   915  			name:            "validator options disabled",
   916  			claims:          viewer,
   917  			config:          defaultAllowACL,
   918  			method:          "GET",
   919  			path:            "/app/page3/allowed",
   920  			optionsDisabled: true,
   921  			shouldErr:       true,
   922  			err:             errors.ErrTokenValidatorOptionsNotFound,
   923  		},
   924  		// IPv6 source address.
   925  		{
   926  			name:                  "token ip6 address and client ip6 address match",
   927  			claims:                viewer5,
   928  			config:                defaultRolesAllowACL,
   929  			method:                "GET",
   930  			path:                  "/app/page3/allowed",
   931  			validateSourceAddress: true,
   932  			sourceAddress:         "2001:DB8::21f:5bff:febf:ce22:8a2e",
   933  		},
   934  		{
   935  			name:                  "token ip6 address and client ip6 address do not match",
   936  			claims:                viewer5,
   937  			config:                defaultRolesAllowACL,
   938  			method:                "GET",
   939  			path:                  "/app/page3/allowed",
   940  			validateSourceAddress: true,
   941  			sourceAddress:         "2001:DB8::21f:5bff:febf:ce22:8a21",
   942  			shouldErr:             true,
   943  			err:                   errors.ErrSourceAddressMismatch.WithArgs("2001:DB8::21f:5bff:febf:ce22:8a2e", "2001:DB8::21f:5bff:febf:ce22:8a21"),
   944  		},
   945  		{
   946  			name:                  "token ip6 address with port and client ip6 address match",
   947  			claims:                viewer5,
   948  			config:                defaultRolesAllowACL,
   949  			method:                "GET",
   950  			path:                  "/app/page3/allowed",
   951  			validateSourceAddress: true,
   952  			sourceAddress:         "[2001:DB8::21f:5bff:febf:ce22:8a2e]:80",
   953  		},
   954  	}
   955  
   956  	for _, tc := range testcases {
   957  		t.Run(tc.name, func(t *testing.T) {
   958  			//if tc.disabled {
   959  			//	return
   960  			// }
   961  			var accessList *acl.AccessList
   962  			var opts *options.TokenValidatorOptions
   963  			var token string
   964  			ctx := context.Background()
   965  			logger := logutil.NewLogger()
   966  
   967  			ks := testutils.NewTestCryptoKeyStore()
   968  			keys := ks.GetKeys()
   969  			signingKey := keys[0]
   970  
   971  			validator := NewTokenValidator()
   972  
   973  			if !tc.optionsDisabled {
   974  				opts = options.NewTokenValidatorOptions()
   975  				if tc.enableBearer {
   976  					opts.ValidateBearerHeader = true
   977  				}
   978  				if tc.validateAccessListPathClaim {
   979  					opts.ValidateAccessListPathClaim = true
   980  				}
   981  				if tc.validateSourceAddress {
   982  					opts.ValidateSourceAddress = true
   983  				}
   984  				if tc.validateMethodPath {
   985  					opts.ValidateMethodPath = true
   986  				}
   987  			}
   988  
   989  			if len(tc.config) > 0 {
   990  				accessList = acl.NewAccessList()
   991  				accessList.SetLogger(logger)
   992  				if tc.name != "no acl rules" {
   993  					if err := accessList.AddRules(ctx, tc.config); err != nil {
   994  						t.Fatal(err)
   995  					}
   996  				}
   997  			}
   998  
   999  			if tc.name == "no verify keys" {
  1000  				keys = []*kms.CryptoKey{}
  1001  			}
  1002  
  1003  			if err := validator.Configure(ctx, keys, accessList, opts); err != nil {
  1004  				if tests.EvalErr(t, err, tc.config, tc.shouldErr, tc.err) {
  1005  					return
  1006  				}
  1007  			}
  1008  
  1009  			if tc.want == nil {
  1010  				tc.want = make(map[string]interface{})
  1011  			}
  1012  
  1013  			if tc.claims != "" {
  1014  				usr, err := user.NewUser(tc.claims)
  1015  				if err != nil {
  1016  					t.Fatal(err)
  1017  				}
  1018  				tc.want["claims"] = usr.Claims
  1019  				if err := signingKey.SignToken("HS512", usr); err != nil {
  1020  					t.Fatal(err)
  1021  				}
  1022  				token = usr.Token
  1023  			}
  1024  
  1025  			if tc.name == "bad token" {
  1026  				token = `{"foobar", "barfoo"}`
  1027  			}
  1028  
  1029  			if tc.enableBearer {
  1030  				tc.want["token_name"] = "bearer"
  1031  			} else {
  1032  				tc.want["token_name"] = "access_token"
  1033  			}
  1034  
  1035  			handler := func(w http.ResponseWriter, r *http.Request) {
  1036  				ctx := context.Background()
  1037  				var msgs []string
  1038  				msgs = append(msgs, fmt.Sprintf("test name: %s", tc.name))
  1039  				for _, entry := range tc.config {
  1040  					msgs = append(msgs, fmt.Sprintf("ACL: %+v", entry))
  1041  				}
  1042  				msgs = append(msgs, fmt.Sprintf("claims: %+v", tc.claims))
  1043  				msgs = append(msgs, fmt.Sprintf("path: %s", r.URL.Path))
  1044  				msgs = append(msgs, fmt.Sprintf("method: %s", r.Method))
  1045  				msgs = append(msgs, fmt.Sprintf("key\n%s", cmp.Diff(nil, keys[0])))
  1046  
  1047  				ar := requests.NewAuthorizationRequest()
  1048  				ar.ID = "TEST_REQUEST_ID"
  1049  				ar.SessionID = "TEST_SESSION_ID"
  1050  
  1051  				usr, err := validator.Authorize(ctx, r, ar)
  1052  				if tests.EvalErrWithLog(t, err, tc.config, tc.shouldErr, tc.err, msgs) {
  1053  					return
  1054  				}
  1055  				got := make(map[string]interface{})
  1056  				got["token_name"] = usr.TokenName
  1057  				got["claims"] = usr.Claims
  1058  				tests.CustomEvalObjectsWithLog(t, "eval", tc.want, got, msgs, user.Claims{})
  1059  
  1060  				if tc.shouldErr {
  1061  					return
  1062  				}
  1063  
  1064  				if tc.cacheUser {
  1065  					if err := validator.CacheUser(usr); err != nil {
  1066  						if tests.EvalErrWithLog(t, err, "cache user", tc.shouldErr, tc.err, msgs) {
  1067  							return
  1068  						}
  1069  					}
  1070  					usr, err = validator.Authorize(ctx, r, ar)
  1071  					if tests.EvalErrWithLog(t, err, "cached auth", tc.shouldErr, tc.err, msgs) {
  1072  						return
  1073  					}
  1074  				}
  1075  			}
  1076  
  1077  			req, err := http.NewRequest(tc.method, tc.path, nil)
  1078  			if err != nil {
  1079  				t.Fatal(err)
  1080  			}
  1081  
  1082  			if tc.enableBearer {
  1083  				req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
  1084  			} else {
  1085  				req.Header.Set("Authorization", fmt.Sprintf("access_token=%s", token))
  1086  			}
  1087  
  1088  			if tc.sourceAddress != "" {
  1089  				req.Header.Set("X-Real-Ip", tc.sourceAddress)
  1090  			}
  1091  
  1092  			w := httptest.NewRecorder()
  1093  			handler(w, req)
  1094  			w.Result()
  1095  		})
  1096  	}
  1097  }
  1098  
  1099  func TestAddKeys(t *testing.T) {
  1100  	testcases := []struct {
  1101  		name                 string
  1102  		keys                 []*kms.CryptoKey
  1103  		verifyFound          bool
  1104  		verifyNotCapable     bool
  1105  		verifyNoTokenName    bool
  1106  		verifyNoMaxLifetime  bool
  1107  		verifyEmptyTokenName bool
  1108  		shouldErr            bool
  1109  		err                  error
  1110  	}{
  1111  		{
  1112  			name:      "no keys",
  1113  			shouldErr: true,
  1114  			err:       errors.ErrValidatorCryptoKeyStoreNoKeys,
  1115  		},
  1116  		{
  1117  			name: "add keys",
  1118  			keys: []*kms.CryptoKey{
  1119  				&kms.CryptoKey{},
  1120  			},
  1121  			verifyFound: true,
  1122  		},
  1123  		{
  1124  			name: "add non verify key",
  1125  			keys: []*kms.CryptoKey{
  1126  				&kms.CryptoKey{},
  1127  			},
  1128  			verifyFound:      true,
  1129  			verifyNotCapable: true,
  1130  			shouldErr:        true,
  1131  			err:              errors.ErrValidatorCryptoKeyStoreNoVerifyKeys,
  1132  		},
  1133  		{
  1134  			name: "add key without token name",
  1135  			keys: []*kms.CryptoKey{
  1136  				&kms.CryptoKey{},
  1137  			},
  1138  			verifyFound:       true,
  1139  			verifyNoTokenName: true,
  1140  			shouldErr:         true,
  1141  			err:               errors.ErrValidatorCryptoKeyStoreNoVerifyKeys,
  1142  		},
  1143  		{
  1144  			name: "add key without token lifetime",
  1145  			keys: []*kms.CryptoKey{
  1146  				&kms.CryptoKey{},
  1147  			},
  1148  			verifyFound:         true,
  1149  			verifyNoMaxLifetime: true,
  1150  			shouldErr:           true,
  1151  			err:                 errors.ErrValidatorCryptoKeyStoreNoVerifyKeys,
  1152  		},
  1153  		{
  1154  			name: "add key with empty token name with spaces",
  1155  			keys: []*kms.CryptoKey{
  1156  				&kms.CryptoKey{},
  1157  			},
  1158  			verifyFound:          true,
  1159  			verifyEmptyTokenName: true,
  1160  			shouldErr:            true,
  1161  			err:                  errors.ErrEmptyTokenName,
  1162  		},
  1163  	}
  1164  
  1165  	for _, tc := range testcases {
  1166  		t.Run(tc.name, func(t *testing.T) {
  1167  			var err error
  1168  			ctx := context.Background()
  1169  			validator := NewTokenValidator()
  1170  			for _, k := range tc.keys {
  1171  				if tc.verifyFound {
  1172  					k.Verify = kms.NewCryptoKeyOperator()
  1173  					k.Verify.Token.Capable = true
  1174  					k.Verify.Token.Name = "access_token"
  1175  					k.Verify.Token.MaxLifetime = 900
  1176  				}
  1177  				if tc.verifyNotCapable {
  1178  					k.Verify.Token.Capable = false
  1179  				}
  1180  				if tc.verifyNoTokenName {
  1181  					k.Verify.Token.Name = ""
  1182  				}
  1183  				if tc.verifyNoMaxLifetime {
  1184  					k.Verify.Token.MaxLifetime = 0
  1185  				}
  1186  				if tc.verifyEmptyTokenName {
  1187  					k.Verify.Token.Name = "    "
  1188  				}
  1189  			}
  1190  			err = validator.addKeys(ctx, tc.keys)
  1191  			if tests.EvalErr(t, err, "keys", tc.shouldErr, tc.err) {
  1192  				return
  1193  			}
  1194  		})
  1195  	}
  1196  }
  1197  
  1198  func TestSetAllowedTokenNames(t *testing.T) {
  1199  	testcases := []struct {
  1200  		name       string
  1201  		tokenNames []string
  1202  		want       map[string]interface{}
  1203  		shouldErr  bool
  1204  		err        error
  1205  	}{
  1206  		{
  1207  			name:       "token names slice with duplicate values",
  1208  			tokenNames: []string{"foo", "foo"},
  1209  			shouldErr:  true,
  1210  			err:        errors.ErrDuplicateTokenName.WithArgs("foo"),
  1211  		},
  1212  		{
  1213  			name:       "token names slice with empty values",
  1214  			tokenNames: []string{"foo", ""},
  1215  			shouldErr:  true,
  1216  			err:        errors.ErrEmptyTokenName,
  1217  		},
  1218  		{
  1219  			name:       "valid token names",
  1220  			tokenNames: []string{"foo", "bar"},
  1221  			want: map[string]interface{}{
  1222  				"header": map[string]interface{}{
  1223  					"foo": true,
  1224  					"bar": true,
  1225  				},
  1226  				"cookie": map[string]interface{}{
  1227  					"foo": true,
  1228  					"bar": true,
  1229  				},
  1230  				"query": map[string]interface{}{
  1231  					"foo": true,
  1232  					"bar": true,
  1233  				},
  1234  			},
  1235  		},
  1236  	}
  1237  
  1238  	for _, tc := range testcases {
  1239  		t.Run(tc.name, func(t *testing.T) {
  1240  			validator := NewTokenValidator()
  1241  			err := validator.setAllowedTokenNames(tc.tokenNames)
  1242  			if tests.EvalErr(t, err, "token names", tc.shouldErr, tc.err) {
  1243  				return
  1244  			}
  1245  			got := make(map[string]interface{})
  1246  			got["header"] = validator.authHeaders
  1247  			got["cookie"] = validator.GetAuthCookies()
  1248  			got["query"] = validator.authHeaders
  1249  			tests.EvalObjects(t, "token names", tc.want, got)
  1250  		})
  1251  	}
  1252  }
  1253  
  1254  func TestSetSourcePriority(t *testing.T) {
  1255  	testcases := []struct {
  1256  		name      string
  1257  		sources   []string
  1258  		want      map[string]interface{}
  1259  		shouldErr bool
  1260  		err       error
  1261  	}{
  1262  		{
  1263  			name:      "empty allowed token sources slice",
  1264  			shouldErr: true,
  1265  			err:       errors.ErrInvalidSourcePriority,
  1266  		},
  1267  		{
  1268  			name:      "allowed token sources slice exceeds three values",
  1269  			shouldErr: true,
  1270  			sources:   []string{"foo", "foo", "foo", "foo"},
  1271  			err:       errors.ErrInvalidSourcePriority,
  1272  		},
  1273  		{
  1274  			name:      "allowed token sources slice has invalid source",
  1275  			sources:   []string{"header", "cookie", "foo"},
  1276  			shouldErr: true,
  1277  			err:       errors.ErrInvalidSourceName.WithArgs("foo"),
  1278  		},
  1279  		{
  1280  			name:      "allowed token sources slice has duplicate source",
  1281  			sources:   []string{"header", "query", "query"},
  1282  			shouldErr: true,
  1283  			err:       errors.ErrDuplicateSourceName.WithArgs("query"),
  1284  		},
  1285  		{
  1286  			name:    "reorder token source priority",
  1287  			sources: []string{"header", "cookie", "query"},
  1288  			want: map[string]interface{}{
  1289  				"sources": []string{"header", "cookie", "query"},
  1290  			},
  1291  		},
  1292  	}
  1293  	for _, tc := range testcases {
  1294  		t.Run(tc.name, func(t *testing.T) {
  1295  			validator := NewTokenValidator()
  1296  			err := validator.SetSourcePriority(tc.sources)
  1297  			if tests.EvalErr(t, err, "token sources", tc.shouldErr, tc.err) {
  1298  				return
  1299  			}
  1300  			got := make(map[string]interface{})
  1301  			got["sources"] = validator.GetSourcePriority()
  1302  			tests.EvalObjects(t, "token sources", tc.want, got)
  1303  		})
  1304  	}
  1305  }