github.com/hernad/nomad@v1.6.112/nomad/structs/acl_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package structs
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/hashicorp/go-multierror"
    13  	"github.com/hernad/nomad/ci"
    14  	"github.com/hernad/nomad/helper/pointer"
    15  	"github.com/hernad/nomad/helper/uuid"
    16  	"github.com/shoenig/test/must"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func TestACLToken_Canonicalize(t *testing.T) {
    21  	testCases := []struct {
    22  		name   string
    23  		testFn func()
    24  	}{
    25  		{
    26  			name: "token with accessor",
    27  			testFn: func() {
    28  				mockToken := &ACLToken{
    29  					AccessorID:  uuid.Generate(),
    30  					SecretID:    uuid.Generate(),
    31  					Name:        "my cool token " + uuid.Generate(),
    32  					Type:        "client",
    33  					Policies:    []string{"foo", "bar"},
    34  					Roles:       []*ACLTokenRoleLink{},
    35  					Global:      false,
    36  					CreateTime:  time.Now().UTC(),
    37  					CreateIndex: 10,
    38  					ModifyIndex: 20,
    39  				}
    40  				mockToken.SetHash()
    41  				copiedMockToken := mockToken.Copy()
    42  
    43  				mockToken.Canonicalize()
    44  				require.Equal(t, copiedMockToken, mockToken)
    45  			},
    46  		},
    47  		{
    48  			name: "token without accessor",
    49  			testFn: func() {
    50  				mockToken := &ACLToken{
    51  					Name:     "my cool token " + uuid.Generate(),
    52  					Type:     "client",
    53  					Policies: []string{"foo", "bar"},
    54  					Global:   false,
    55  				}
    56  
    57  				mockToken.Canonicalize()
    58  				require.NotEmpty(t, mockToken.AccessorID)
    59  				require.NotEmpty(t, mockToken.SecretID)
    60  				require.NotEmpty(t, mockToken.CreateTime)
    61  			},
    62  		},
    63  		{
    64  			name: "token with ttl without accessor",
    65  			testFn: func() {
    66  				mockToken := &ACLToken{
    67  					Name:          "my cool token " + uuid.Generate(),
    68  					Type:          "client",
    69  					Policies:      []string{"foo", "bar"},
    70  					Global:        false,
    71  					ExpirationTTL: 10 * time.Hour,
    72  				}
    73  
    74  				mockToken.Canonicalize()
    75  				require.NotEmpty(t, mockToken.AccessorID)
    76  				require.NotEmpty(t, mockToken.SecretID)
    77  				require.NotEmpty(t, mockToken.CreateTime)
    78  				require.NotEmpty(t, mockToken.ExpirationTime)
    79  			},
    80  		},
    81  	}
    82  
    83  	for _, tc := range testCases {
    84  		t.Run(tc.name, func(t *testing.T) {
    85  			tc.testFn()
    86  		})
    87  	}
    88  }
    89  
    90  func TestACLTokenValidate(t *testing.T) {
    91  	ci.Parallel(t)
    92  
    93  	testCases := []struct {
    94  		name                  string
    95  		inputACLToken         *ACLToken
    96  		inputExistingACLToken *ACLToken
    97  		expectedErrorContains string
    98  	}{
    99  		{
   100  			name:                  "missing type",
   101  			inputACLToken:         &ACLToken{},
   102  			inputExistingACLToken: nil,
   103  			expectedErrorContains: "client or management",
   104  		},
   105  		{
   106  			name: "missing policies or roles",
   107  			inputACLToken: &ACLToken{
   108  				Type: ACLClientToken,
   109  			},
   110  			inputExistingACLToken: nil,
   111  			expectedErrorContains: "missing policies or roles",
   112  		},
   113  		{
   114  			name: "invalid policies",
   115  			inputACLToken: &ACLToken{
   116  				Type:     ACLManagementToken,
   117  				Policies: []string{"foo"},
   118  			},
   119  			inputExistingACLToken: nil,
   120  			expectedErrorContains: "associated with policies or roles",
   121  		},
   122  		{
   123  			name: "invalid roles",
   124  			inputACLToken: &ACLToken{
   125  				Type:  ACLManagementToken,
   126  				Roles: []*ACLTokenRoleLink{{Name: "foo"}},
   127  			},
   128  			inputExistingACLToken: nil,
   129  			expectedErrorContains: "associated with policies or roles",
   130  		},
   131  		{
   132  			name: "name too long",
   133  			inputACLToken: &ACLToken{
   134  				Type: ACLManagementToken,
   135  				Name: uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate() +
   136  					uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate(),
   137  			},
   138  			inputExistingACLToken: nil,
   139  			expectedErrorContains: "name too long",
   140  		},
   141  		{
   142  			name: "negative TTL",
   143  			inputACLToken: &ACLToken{
   144  				Type:          ACLManagementToken,
   145  				Name:          "foo",
   146  				ExpirationTTL: -1 * time.Hour,
   147  			},
   148  			inputExistingACLToken: nil,
   149  			expectedErrorContains: "should not be negative",
   150  		},
   151  		{
   152  			name: "TTL too small",
   153  			inputACLToken: &ACLToken{
   154  				Type:           ACLManagementToken,
   155  				Name:           "foo",
   156  				CreateTime:     time.Date(2022, time.July, 11, 16, 23, 0, 0, time.UTC),
   157  				ExpirationTime: pointer.Of(time.Date(2022, time.July, 11, 16, 23, 10, 0, time.UTC)),
   158  			},
   159  			inputExistingACLToken: nil,
   160  			expectedErrorContains: "expiration time cannot be less than",
   161  		},
   162  		{
   163  			name: "TTL too large",
   164  			inputACLToken: &ACLToken{
   165  				Type:           ACLManagementToken,
   166  				Name:           "foo",
   167  				CreateTime:     time.Date(2022, time.July, 11, 16, 23, 0, 0, time.UTC),
   168  				ExpirationTime: pointer.Of(time.Date(2042, time.July, 11, 16, 23, 0, 0, time.UTC)),
   169  			},
   170  			inputExistingACLToken: nil,
   171  			expectedErrorContains: "expiration time cannot be more than",
   172  		},
   173  		{
   174  			name: "valid management",
   175  			inputACLToken: &ACLToken{
   176  				Type: ACLManagementToken,
   177  				Name: "foo",
   178  			},
   179  			inputExistingACLToken: nil,
   180  			expectedErrorContains: "",
   181  		},
   182  		{
   183  			name: "valid client",
   184  			inputACLToken: &ACLToken{
   185  				Type:     ACLClientToken,
   186  				Name:     "foo",
   187  				Policies: []string{"foo"},
   188  			},
   189  			inputExistingACLToken: nil,
   190  			expectedErrorContains: "",
   191  		},
   192  	}
   193  
   194  	for _, tc := range testCases {
   195  		t.Run(tc.name, func(t *testing.T) {
   196  			actualOutputError := tc.inputACLToken.Validate(1*time.Minute, 24*time.Hour, tc.inputExistingACLToken)
   197  			if tc.expectedErrorContains != "" {
   198  				require.ErrorContains(t, actualOutputError, tc.expectedErrorContains)
   199  			} else {
   200  				require.NoError(t, actualOutputError)
   201  			}
   202  		})
   203  	}
   204  }
   205  
   206  func TestACLToken_HasExpirationTime(t *testing.T) {
   207  	testCases := []struct {
   208  		name           string
   209  		inputACLToken  *ACLToken
   210  		expectedOutput bool ``
   211  	}{
   212  		{
   213  			name:           "nil acl token",
   214  			inputACLToken:  nil,
   215  			expectedOutput: false,
   216  		},
   217  		{
   218  			name:           "default empty value",
   219  			inputACLToken:  &ACLToken{},
   220  			expectedOutput: false,
   221  		},
   222  		{
   223  			name: "expiration set to now",
   224  			inputACLToken: &ACLToken{
   225  				ExpirationTime: pointer.Of(time.Now().UTC()),
   226  			},
   227  			expectedOutput: true,
   228  		},
   229  		{
   230  			name: "expiration set to past",
   231  			inputACLToken: &ACLToken{
   232  				ExpirationTime: pointer.Of(time.Date(2022, time.February, 21, 19, 35, 0, 0, time.UTC)),
   233  			},
   234  			expectedOutput: true,
   235  		},
   236  		{
   237  			name: "expiration set to future",
   238  			inputACLToken: &ACLToken{
   239  				ExpirationTime: pointer.Of(time.Date(2087, time.April, 25, 12, 0, 0, 0, time.UTC)),
   240  			},
   241  			expectedOutput: true,
   242  		},
   243  	}
   244  
   245  	for _, tc := range testCases {
   246  		t.Run(tc.name, func(t *testing.T) {
   247  			actualOutput := tc.inputACLToken.HasExpirationTime()
   248  			require.Equal(t, tc.expectedOutput, actualOutput)
   249  		})
   250  	}
   251  }
   252  
   253  func TestACLToken_IsExpired(t *testing.T) {
   254  	testCases := []struct {
   255  		name           string
   256  		inputACLToken  *ACLToken
   257  		inputTime      time.Time
   258  		expectedOutput bool
   259  	}{
   260  		{
   261  			name:           "token without expiry",
   262  			inputACLToken:  &ACLToken{},
   263  			inputTime:      time.Now().UTC(),
   264  			expectedOutput: false,
   265  		},
   266  		{
   267  			name:           "empty input time",
   268  			inputACLToken:  &ACLToken{},
   269  			inputTime:      time.Time{},
   270  			expectedOutput: false,
   271  		},
   272  		{
   273  			name: "token not expired",
   274  			inputACLToken: &ACLToken{
   275  				ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)),
   276  			},
   277  			inputTime:      time.Date(2022, time.May, 9, 10, 26, 0, 0, time.UTC),
   278  			expectedOutput: false,
   279  		},
   280  		{
   281  			name: "token expired",
   282  			inputACLToken: &ACLToken{
   283  				ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)),
   284  			},
   285  			inputTime:      time.Date(2022, time.May, 9, 10, 28, 0, 0, time.UTC),
   286  			expectedOutput: true,
   287  		},
   288  		{
   289  			name: "empty input time",
   290  			inputACLToken: &ACLToken{
   291  				ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)),
   292  			},
   293  			inputTime:      time.Time{},
   294  			expectedOutput: true,
   295  		},
   296  	}
   297  
   298  	for _, tc := range testCases {
   299  		t.Run(tc.name, func(t *testing.T) {
   300  			actualOutput := tc.inputACLToken.IsExpired(tc.inputTime)
   301  			require.Equal(t, tc.expectedOutput, actualOutput)
   302  		})
   303  	}
   304  }
   305  
   306  func TestACLToken_HasRoles(t *testing.T) {
   307  	testCases := []struct {
   308  		name           string
   309  		inputToken     *ACLToken
   310  		inputRoleIDs   []string
   311  		expectedOutput bool
   312  	}{
   313  		{
   314  			name: "client token request all subset",
   315  			inputToken: &ACLToken{
   316  				Type: ACLClientToken,
   317  				Roles: []*ACLTokenRoleLink{
   318  					{ID: "foo"},
   319  					{ID: "bar"},
   320  					{ID: "baz"},
   321  				},
   322  			},
   323  			inputRoleIDs:   []string{"foo", "bar", "baz"},
   324  			expectedOutput: true,
   325  		},
   326  		{
   327  			name: "client token request partial subset",
   328  			inputToken: &ACLToken{
   329  				Type: ACLClientToken,
   330  				Roles: []*ACLTokenRoleLink{
   331  					{ID: "foo"},
   332  					{ID: "bar"},
   333  					{ID: "baz"},
   334  				},
   335  			},
   336  			inputRoleIDs:   []string{"foo", "baz"},
   337  			expectedOutput: true,
   338  		},
   339  		{
   340  			name: "client token request one subset",
   341  			inputToken: &ACLToken{
   342  				Type: ACLClientToken,
   343  				Roles: []*ACLTokenRoleLink{
   344  					{ID: "foo"},
   345  					{ID: "bar"},
   346  					{ID: "baz"},
   347  				},
   348  			},
   349  			inputRoleIDs:   []string{"baz"},
   350  			expectedOutput: true,
   351  		},
   352  		{
   353  			name: "client token request no subset",
   354  			inputToken: &ACLToken{
   355  				Type: ACLClientToken,
   356  				Roles: []*ACLTokenRoleLink{
   357  					{ID: "foo"},
   358  					{ID: "bar"},
   359  					{ID: "baz"},
   360  				},
   361  			},
   362  			inputRoleIDs:   []string{"new"},
   363  			expectedOutput: false,
   364  		},
   365  	}
   366  
   367  	for _, tc := range testCases {
   368  		t.Run(tc.name, func(t *testing.T) {
   369  			actualOutput := tc.inputToken.HasRoles(tc.inputRoleIDs)
   370  			require.Equal(t, tc.expectedOutput, actualOutput)
   371  		})
   372  	}
   373  }
   374  
   375  func TestACLRole_SetHash(t *testing.T) {
   376  	testCases := []struct {
   377  		name           string
   378  		inputACLRole   *ACLRole
   379  		expectedOutput []byte
   380  	}{
   381  		{
   382  			name: "no hash set",
   383  			inputACLRole: &ACLRole{
   384  				Name:        "acl-role",
   385  				Description: "mocked-test-acl-role",
   386  				Policies: []*ACLRolePolicyLink{
   387  					{Name: "mocked-test-policy-1"},
   388  					{Name: "mocked-test-policy-2"},
   389  				},
   390  				CreateIndex: 10,
   391  				ModifyIndex: 10,
   392  				Hash:        []byte{},
   393  			},
   394  			expectedOutput: []byte{
   395  				122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   396  				171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   397  			},
   398  		},
   399  		{
   400  			name: "hash set with change",
   401  			inputACLRole: &ACLRole{
   402  				Name:        "acl-role",
   403  				Description: "mocked-test-acl-role",
   404  				Policies: []*ACLRolePolicyLink{
   405  					{Name: "mocked-test-policy-1"},
   406  					{Name: "mocked-test-policy-2"},
   407  				},
   408  				CreateIndex: 10,
   409  				ModifyIndex: 10,
   410  				Hash: []byte{
   411  					137, 147, 2, 29, 53, 94, 78, 13, 45, 51, 127, 193, 21, 248, 230, 126, 34,
   412  					106, 216, 73, 248, 219, 209, 146, 204, 107, 185, 2, 89, 255, 198, 5,
   413  				},
   414  			},
   415  			expectedOutput: []byte{
   416  				122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   417  				171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   418  			},
   419  		},
   420  	}
   421  
   422  	for _, tc := range testCases {
   423  		t.Run(tc.name, func(t *testing.T) {
   424  			actualOutput := tc.inputACLRole.SetHash()
   425  			require.Equal(t, tc.expectedOutput, actualOutput)
   426  			require.Equal(t, tc.inputACLRole.Hash, actualOutput)
   427  		})
   428  	}
   429  }
   430  
   431  func TestACLRole_Validate(t *testing.T) {
   432  	testCases := []struct {
   433  		name                  string
   434  		inputACLRole          *ACLRole
   435  		expectedError         bool
   436  		expectedErrorContains string
   437  	}{
   438  		{
   439  			name: "role name too long",
   440  			inputACLRole: &ACLRole{
   441  				Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   442  			},
   443  			expectedError:         true,
   444  			expectedErrorContains: "invalid name",
   445  		},
   446  		{
   447  			name: "role name too short",
   448  			inputACLRole: &ACLRole{
   449  				Name: "",
   450  			},
   451  			expectedError:         true,
   452  			expectedErrorContains: "invalid name",
   453  		},
   454  		{
   455  			name: "role name with invalid characters",
   456  			inputACLRole: &ACLRole{
   457  				Name: "--#$%$^%_%%_?>",
   458  			},
   459  			expectedError:         true,
   460  			expectedErrorContains: "invalid name",
   461  		},
   462  		{
   463  			name: "description too long",
   464  			inputACLRole: &ACLRole{
   465  				Name:        "acl-role",
   466  				Description: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   467  			},
   468  			expectedError:         true,
   469  			expectedErrorContains: "description longer than",
   470  		},
   471  		{
   472  			name: "no policies",
   473  			inputACLRole: &ACLRole{
   474  				Name:        "acl-role",
   475  				Description: "",
   476  			},
   477  			expectedError:         true,
   478  			expectedErrorContains: "at least one policy should be specified",
   479  		},
   480  		{
   481  			name: "valid",
   482  			inputACLRole: &ACLRole{
   483  				Name:        "acl-role",
   484  				Description: "",
   485  				Policies: []*ACLRolePolicyLink{
   486  					{Name: "policy-1"},
   487  				},
   488  			},
   489  			expectedError:         false,
   490  			expectedErrorContains: "",
   491  		},
   492  	}
   493  
   494  	for _, tc := range testCases {
   495  		t.Run(tc.name, func(t *testing.T) {
   496  			actualOutput := tc.inputACLRole.Validate()
   497  			if tc.expectedError {
   498  				require.ErrorContains(t, actualOutput, tc.expectedErrorContains)
   499  			} else {
   500  				require.NoError(t, actualOutput)
   501  			}
   502  		})
   503  	}
   504  }
   505  
   506  func TestACLRole_Canonicalize(t *testing.T) {
   507  	testCases := []struct {
   508  		name         string
   509  		inputACLRole *ACLRole
   510  	}{
   511  		{
   512  			name:         "no ID set",
   513  			inputACLRole: &ACLRole{},
   514  		},
   515  		{
   516  			name:         "id set",
   517  			inputACLRole: &ACLRole{ID: "some-random-uuid"},
   518  		},
   519  	}
   520  
   521  	for _, tc := range testCases {
   522  		t.Run(tc.name, func(t *testing.T) {
   523  			existing := tc.inputACLRole.Copy()
   524  			tc.inputACLRole.Canonicalize()
   525  			if existing.ID == "" {
   526  				require.NotEmpty(t, tc.inputACLRole.ID)
   527  			} else {
   528  				require.Equal(t, existing.ID, tc.inputACLRole.ID)
   529  			}
   530  		})
   531  	}
   532  }
   533  
   534  func TestACLRole_Equals(t *testing.T) {
   535  	testCases := []struct {
   536  		name            string
   537  		composedACLRole *ACLRole
   538  		inputACLRole    *ACLRole
   539  		expectedOutput  bool
   540  	}{
   541  		{
   542  			name: "equal with hash set",
   543  			composedACLRole: &ACLRole{
   544  				Name:        "acl-role-",
   545  				Description: "mocked-test-acl-role",
   546  				Policies: []*ACLRolePolicyLink{
   547  					{Name: "mocked-test-policy-1"},
   548  					{Name: "mocked-test-policy-2"},
   549  				},
   550  				CreateIndex: 10,
   551  				ModifyIndex: 10,
   552  				Hash: []byte{
   553  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   554  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   555  				},
   556  			},
   557  			inputACLRole: &ACLRole{
   558  				Name:        "acl-role",
   559  				Description: "mocked-test-acl-role",
   560  				Policies: []*ACLRolePolicyLink{
   561  					{Name: "mocked-test-policy-1"},
   562  					{Name: "mocked-test-policy-2"},
   563  				},
   564  				CreateIndex: 10,
   565  				ModifyIndex: 10,
   566  				Hash: []byte{
   567  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   568  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   569  				},
   570  			},
   571  			expectedOutput: true,
   572  		},
   573  		{
   574  			name: "equal without hash set",
   575  			composedACLRole: &ACLRole{
   576  				Name:        "acl-role",
   577  				Description: "mocked-test-acl-role",
   578  				Policies: []*ACLRolePolicyLink{
   579  					{Name: "mocked-test-policy-1"},
   580  					{Name: "mocked-test-policy-2"},
   581  				},
   582  				CreateIndex: 10,
   583  				ModifyIndex: 10,
   584  				Hash:        []byte{},
   585  			},
   586  			inputACLRole: &ACLRole{
   587  				Name:        "acl-role",
   588  				Description: "mocked-test-acl-role",
   589  				Policies: []*ACLRolePolicyLink{
   590  					{Name: "mocked-test-policy-1"},
   591  					{Name: "mocked-test-policy-2"},
   592  				},
   593  				CreateIndex: 10,
   594  				ModifyIndex: 10,
   595  				Hash:        []byte{},
   596  			},
   597  			expectedOutput: true,
   598  		},
   599  		{
   600  			name:            "both nil",
   601  			composedACLRole: nil,
   602  			inputACLRole:    nil,
   603  			expectedOutput:  true,
   604  		},
   605  		{
   606  			name:            "not equal composed nil",
   607  			composedACLRole: nil,
   608  			inputACLRole: &ACLRole{
   609  				Name:        "acl-role",
   610  				Description: "mocked-test-acl-role",
   611  				Policies: []*ACLRolePolicyLink{
   612  					{Name: "mocked-test-policy-1"},
   613  					{Name: "mocked-test-policy-2"},
   614  				},
   615  				CreateIndex: 10,
   616  				ModifyIndex: 10,
   617  				Hash: []byte{
   618  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   619  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   620  				},
   621  			},
   622  			expectedOutput: false,
   623  		},
   624  		{
   625  			name: "not equal input nil",
   626  			composedACLRole: &ACLRole{
   627  				Name:        "acl-role",
   628  				Description: "mocked-test-acl-role",
   629  				Policies: []*ACLRolePolicyLink{
   630  					{Name: "mocked-test-policy-1"},
   631  					{Name: "mocked-test-policy-2"},
   632  				},
   633  				CreateIndex: 10,
   634  				ModifyIndex: 10,
   635  				Hash: []byte{
   636  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   637  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   638  				},
   639  			},
   640  			inputACLRole:   nil,
   641  			expectedOutput: false,
   642  		},
   643  		{
   644  			name: "not equal with hash set",
   645  			composedACLRole: &ACLRole{
   646  				Name:        "acl-role",
   647  				Description: "mocked-test-acl-role",
   648  				Policies: []*ACLRolePolicyLink{
   649  					{Name: "mocked-test-policy-1"},
   650  					{Name: "mocked-test-policy-2"},
   651  				},
   652  				CreateIndex: 10,
   653  				ModifyIndex: 10,
   654  				Hash: []byte{
   655  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   656  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   657  				},
   658  			},
   659  			inputACLRole: &ACLRole{
   660  				Name:        "acl-role",
   661  				Description: "mocked-test-acl-role",
   662  				Policies: []*ACLRolePolicyLink{
   663  					{Name: "mocked-test-policy-1"},
   664  				},
   665  				CreateIndex: 10,
   666  				ModifyIndex: 10,
   667  				Hash: []byte{
   668  					137, 147, 2, 29, 53, 94, 78, 13, 45, 51, 127, 193, 21, 248, 230, 126, 34,
   669  					106, 216, 73, 248, 219, 209, 146, 204, 107, 185, 2, 89, 255, 198, 5,
   670  				},
   671  			},
   672  			expectedOutput: false,
   673  		},
   674  		{
   675  			name: "not equal without hash set",
   676  			composedACLRole: &ACLRole{
   677  				Name:        "acl-role",
   678  				Description: "mocked-test-acl-role",
   679  				Policies: []*ACLRolePolicyLink{
   680  					{Name: "mocked-test-policy-1"},
   681  					{Name: "mocked-test-policy-2"},
   682  				},
   683  				CreateIndex: 10,
   684  				ModifyIndex: 10,
   685  				Hash:        []byte{},
   686  			},
   687  			inputACLRole: &ACLRole{
   688  				Name:        "acl-role",
   689  				Description: "mocked-test-acl-role",
   690  				Policies: []*ACLRolePolicyLink{
   691  					{Name: "mocked-test-policy-1"},
   692  				},
   693  				CreateIndex: 10,
   694  				ModifyIndex: 10,
   695  				Hash:        []byte{},
   696  			},
   697  			expectedOutput: false,
   698  		},
   699  	}
   700  
   701  	for _, tc := range testCases {
   702  		t.Run(tc.name, func(t *testing.T) {
   703  			actualOutput := tc.composedACLRole.Equal(tc.inputACLRole)
   704  			require.Equal(t, tc.expectedOutput, actualOutput)
   705  		})
   706  	}
   707  }
   708  
   709  func TestACLRole_Copy(t *testing.T) {
   710  	testCases := []struct {
   711  		name         string
   712  		inputACLRole *ACLRole
   713  	}{
   714  		{
   715  			name:         "nil input",
   716  			inputACLRole: nil,
   717  		},
   718  		{
   719  			name: "general 1",
   720  			inputACLRole: &ACLRole{
   721  				Name:        "acl-role",
   722  				Description: "mocked-test-acl-role",
   723  				Policies: []*ACLRolePolicyLink{
   724  					{Name: "mocked-test-policy-1"},
   725  					{Name: "mocked-test-policy-2"},
   726  				},
   727  				CreateIndex: 10,
   728  				ModifyIndex: 10,
   729  				Hash: []byte{
   730  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   731  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   732  				},
   733  			},
   734  		},
   735  	}
   736  
   737  	for _, tc := range testCases {
   738  		t.Run(tc.name, func(t *testing.T) {
   739  			actualOutput := tc.inputACLRole.Copy()
   740  			require.Equal(t, tc.inputACLRole, actualOutput)
   741  		})
   742  	}
   743  }
   744  
   745  func TestACLRole_Stub(t *testing.T) {
   746  	testCases := []struct {
   747  		name           string
   748  		inputACLRole   *ACLRole
   749  		expectedOutput *ACLRoleListStub
   750  	}{
   751  		{
   752  			name: "partially hydrated",
   753  			inputACLRole: &ACLRole{
   754  				ID:          "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
   755  				Name:        "my-lovely-role",
   756  				Description: "",
   757  				Policies: []*ACLRolePolicyLink{
   758  					{Name: "my-lovely-policy"},
   759  				},
   760  				Hash:        []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
   761  				CreateIndex: 24,
   762  				ModifyIndex: 24,
   763  			},
   764  			expectedOutput: &ACLRoleListStub{
   765  				ID:          "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
   766  				Name:        "my-lovely-role",
   767  				Description: "",
   768  				Policies: []*ACLRolePolicyLink{
   769  					{Name: "my-lovely-policy"},
   770  				},
   771  				Hash:        []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
   772  				CreateIndex: 24,
   773  				ModifyIndex: 24,
   774  			},
   775  		},
   776  		{
   777  			name: "hully hydrated",
   778  			inputACLRole: &ACLRole{
   779  				ID:          "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
   780  				Name:        "my-lovely-role",
   781  				Description: "this-is-my-lovely-role",
   782  				Policies: []*ACLRolePolicyLink{
   783  					{Name: "my-lovely-policy"},
   784  				},
   785  				Hash:        []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
   786  				CreateIndex: 24,
   787  				ModifyIndex: 24,
   788  			},
   789  			expectedOutput: &ACLRoleListStub{
   790  				ID:          "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
   791  				Name:        "my-lovely-role",
   792  				Description: "this-is-my-lovely-role",
   793  				Policies: []*ACLRolePolicyLink{
   794  					{Name: "my-lovely-policy"},
   795  				},
   796  				Hash:        []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
   797  				CreateIndex: 24,
   798  				ModifyIndex: 24,
   799  			},
   800  		},
   801  	}
   802  
   803  	for _, tc := range testCases {
   804  		t.Run(tc.name, func(t *testing.T) {
   805  			actualOutput := tc.inputACLRole.Stub()
   806  			require.Equal(t, tc.expectedOutput, actualOutput)
   807  		})
   808  	}
   809  }
   810  
   811  func Test_ACLRolesUpsertRequest(t *testing.T) {
   812  	req := ACLRolesUpsertRequest{}
   813  	require.False(t, req.IsRead())
   814  }
   815  
   816  func Test_ACLRolesDeleteByIDRequest(t *testing.T) {
   817  	req := ACLRolesDeleteByIDRequest{}
   818  	require.False(t, req.IsRead())
   819  }
   820  
   821  func Test_ACLRolesListRequest(t *testing.T) {
   822  	req := ACLRolesListRequest{}
   823  	require.True(t, req.IsRead())
   824  }
   825  
   826  func Test_ACLRolesByIDRequest(t *testing.T) {
   827  	req := ACLRolesByIDRequest{}
   828  	require.True(t, req.IsRead())
   829  }
   830  
   831  func Test_ACLRoleByIDRequest(t *testing.T) {
   832  	req := ACLRoleByIDRequest{}
   833  	require.True(t, req.IsRead())
   834  }
   835  
   836  func Test_ACLRoleByNameRequest(t *testing.T) {
   837  	req := ACLRoleByNameRequest{}
   838  	require.True(t, req.IsRead())
   839  }
   840  
   841  func Test_ACLAuthMethodListRequest(t *testing.T) {
   842  	req := ACLAuthMethodListRequest{}
   843  	must.True(t, req.IsRead())
   844  }
   845  
   846  func Test_ACLAuthMethodGetRequest(t *testing.T) {
   847  	req := ACLAuthMethodGetRequest{}
   848  	must.True(t, req.IsRead())
   849  }
   850  
   851  func TestACLAuthMethodSetHash(t *testing.T) {
   852  	ci.Parallel(t)
   853  
   854  	am := &ACLAuthMethod{
   855  		Name: "foo",
   856  		Type: "bad type",
   857  	}
   858  	out1 := am.SetHash()
   859  	must.NotNil(t, out1)
   860  	must.NotNil(t, am.Hash)
   861  	must.Eq(t, out1, am.Hash)
   862  
   863  	am.Type = "good type"
   864  	out2 := am.SetHash()
   865  	must.NotNil(t, out2)
   866  	must.NotNil(t, am.Hash)
   867  	must.Eq(t, out2, am.Hash)
   868  	must.NotEq(t, out1, out2)
   869  }
   870  
   871  func TestACLAuthMethod_Stub(t *testing.T) {
   872  	ci.Parallel(t)
   873  
   874  	maxTokenTTL, _ := time.ParseDuration("3600s")
   875  	am := ACLAuthMethod{
   876  		Name:          fmt.Sprintf("acl-auth-method-%s", uuid.Short()),
   877  		Type:          "acl-auth-mock-type",
   878  		TokenLocality: "locality",
   879  		MaxTokenTTL:   maxTokenTTL,
   880  		Default:       true,
   881  		Config: &ACLAuthMethodConfig{
   882  			OIDCDiscoveryURL:    "http://example.com",
   883  			OIDCClientID:        "mock",
   884  			OIDCClientSecret:    "very secret secret",
   885  			BoundAudiences:      []string{"audience1", "audience2"},
   886  			AllowedRedirectURIs: []string{"foo", "bar"},
   887  			DiscoveryCaPem:      []string{"foo"},
   888  			SigningAlgs:         []string{"bar"},
   889  			ClaimMappings:       map[string]string{"foo": "bar"},
   890  			ListClaimMappings:   map[string]string{"foo": "bar"},
   891  		},
   892  		CreateTime:  time.Now().UTC(),
   893  		CreateIndex: 10,
   894  		ModifyIndex: 10,
   895  	}
   896  	am.SetHash()
   897  
   898  	must.Eq(t, am.Stub(), &ACLAuthMethodStub{
   899  		Name:        am.Name,
   900  		Type:        am.Type,
   901  		Default:     am.Default,
   902  		Hash:        am.Hash,
   903  		CreateIndex: am.CreateIndex,
   904  		ModifyIndex: am.ModifyIndex,
   905  	})
   906  
   907  	nilAuthMethod := &ACLAuthMethod{}
   908  	must.Eq(t, nilAuthMethod.Stub(), &ACLAuthMethodStub{})
   909  }
   910  
   911  func TestACLAuthMethod_Equal(t *testing.T) {
   912  	ci.Parallel(t)
   913  
   914  	maxTokenTTL, _ := time.ParseDuration("3600s")
   915  	am1 := &ACLAuthMethod{
   916  		Name:          fmt.Sprintf("acl-auth-method-%s", uuid.Short()),
   917  		Type:          "acl-auth-mock-type",
   918  		TokenLocality: "locality",
   919  		MaxTokenTTL:   maxTokenTTL,
   920  		Default:       true,
   921  		Config: &ACLAuthMethodConfig{
   922  			OIDCDiscoveryURL:    "http://example.com",
   923  			OIDCClientID:        "mock",
   924  			OIDCClientSecret:    "very secret secret",
   925  			BoundAudiences:      []string{"audience1", "audience2"},
   926  			AllowedRedirectURIs: []string{"foo", "bar"},
   927  			DiscoveryCaPem:      []string{"foo"},
   928  			SigningAlgs:         []string{"bar"},
   929  			ClaimMappings:       map[string]string{"foo": "bar"},
   930  			ListClaimMappings:   map[string]string{"foo": "bar"},
   931  		},
   932  		CreateTime:  time.Now().UTC(),
   933  		CreateIndex: 10,
   934  		ModifyIndex: 10,
   935  	}
   936  	am1.SetHash()
   937  
   938  	// am2 differs from am1 by 1 nested conf field
   939  	am2 := am1.Copy()
   940  	am2.Config.OIDCClientID = "mock2"
   941  	am2.SetHash()
   942  
   943  	tests := []struct {
   944  		name    string
   945  		method1 *ACLAuthMethod
   946  		method2 *ACLAuthMethod
   947  		want    bool
   948  	}{
   949  		{"one nil", am1, &ACLAuthMethod{}, false},
   950  		{"both nil", &ACLAuthMethod{}, &ACLAuthMethod{}, true},
   951  		{"one is different than the other", am1, am2, false},
   952  		{"equal", am1, am1.Copy(), true},
   953  	}
   954  	for _, tt := range tests {
   955  		t.Run(tt.name, func(t *testing.T) {
   956  			got := tt.method1.Equal(tt.method2)
   957  			must.Eq(t, got, tt.want, must.Sprintf(
   958  				"ACLAuthMethod.Equal() got %v, want %v, test case: %s", got, tt.want, tt.name))
   959  		})
   960  	}
   961  }
   962  
   963  func TestACLAuthMethod_Copy(t *testing.T) {
   964  	ci.Parallel(t)
   965  
   966  	maxTokenTTL, _ := time.ParseDuration("3600s")
   967  	am1 := &ACLAuthMethod{
   968  		Name:          fmt.Sprintf("acl-auth-method-%s", uuid.Short()),
   969  		Type:          "acl-auth-mock-type",
   970  		TokenLocality: "locality",
   971  		MaxTokenTTL:   maxTokenTTL,
   972  		Default:       true,
   973  		Config: &ACLAuthMethodConfig{
   974  			OIDCDiscoveryURL:    "http://example.com",
   975  			OIDCClientID:        "mock",
   976  			OIDCClientSecret:    "very secret secret",
   977  			BoundAudiences:      []string{"audience1", "audience2"},
   978  			AllowedRedirectURIs: []string{"foo", "bar"},
   979  			DiscoveryCaPem:      []string{"foo"},
   980  			SigningAlgs:         []string{"bar"},
   981  			ClaimMappings:       map[string]string{"foo": "bar"},
   982  			ListClaimMappings:   map[string]string{"foo": "bar"},
   983  		},
   984  		CreateTime:  time.Now().UTC(),
   985  		CreateIndex: 10,
   986  		ModifyIndex: 10,
   987  	}
   988  	am1.SetHash()
   989  
   990  	am2 := am1.Copy()
   991  	am2.SetHash()
   992  	must.Eq(t, am1, am2)
   993  
   994  	am3 := am1.Copy()
   995  	am3.Config.AllowedRedirectURIs = []string{"new", "urls"}
   996  	am3.SetHash()
   997  	must.NotEq(t, am1, am3)
   998  }
   999  
  1000  func TestACLAuthMethod_Validate(t *testing.T) {
  1001  	ci.Parallel(t)
  1002  
  1003  	goodTTL, _ := time.ParseDuration("3600s")
  1004  	badTTL, _ := time.ParseDuration("3600h")
  1005  
  1006  	tests := []struct {
  1007  		name        string
  1008  		method      *ACLAuthMethod
  1009  		wantErr     bool
  1010  		errContains string
  1011  	}{
  1012  		{
  1013  			"valid method",
  1014  			&ACLAuthMethod{
  1015  				Name:          "mock-auth-method",
  1016  				Type:          "OIDC",
  1017  				TokenLocality: "local",
  1018  				MaxTokenTTL:   goodTTL,
  1019  			},
  1020  			false,
  1021  			"",
  1022  		},
  1023  		{"invalid name", &ACLAuthMethod{Name: "is this name invalid?"}, true, "invalid name"},
  1024  		{"invalid token locality", &ACLAuthMethod{TokenLocality: "regional"}, true, "invalid token locality"},
  1025  		{"invalid type", &ACLAuthMethod{Type: "groovy"}, true, "invalid token type"},
  1026  		{"invalid max ttl", &ACLAuthMethod{MaxTokenTTL: badTTL}, true, "invalid token type"},
  1027  	}
  1028  	for _, tt := range tests {
  1029  		t.Run(tt.name, func(t *testing.T) {
  1030  			minTTL, _ := time.ParseDuration("10s")
  1031  			maxTTL, _ := time.ParseDuration("10h")
  1032  			got := tt.method.Validate(minTTL, maxTTL)
  1033  			if tt.wantErr {
  1034  				must.Error(t, got, must.Sprintf(
  1035  					"ACLAuthMethod.Validate() got error, didn't expect it; test case: %s", tt.name))
  1036  				must.StrContains(t, got.Error(), tt.errContains, must.Sprintf(
  1037  					"ACLAuthMethod.Validate() got %v error message, expected %v; test case: %s",
  1038  					got, tt.errContains, tt.name))
  1039  			} else {
  1040  				must.NoError(t, got, must.Sprintf(
  1041  					"ACLAuthMethod.Validate() expected an error but didn't get one; test case: %s", tt.name))
  1042  			}
  1043  		})
  1044  	}
  1045  }
  1046  
  1047  func TestACLAuthMethod_Merge(t *testing.T) {
  1048  	ci.Parallel(t)
  1049  
  1050  	name := fmt.Sprintf("acl-auth-method-%s", uuid.Short())
  1051  
  1052  	maxTokenTTL, _ := time.ParseDuration("3600s")
  1053  	am1 := &ACLAuthMethod{
  1054  		Name:          name,
  1055  		TokenLocality: "global",
  1056  	}
  1057  	am2 := &ACLAuthMethod{
  1058  		Name:          name,
  1059  		Type:          "OIDC",
  1060  		TokenLocality: "locality",
  1061  		MaxTokenTTL:   maxTokenTTL,
  1062  		Default:       true,
  1063  		Config: &ACLAuthMethodConfig{
  1064  			OIDCDiscoveryURL:    "http://example.com",
  1065  			OIDCClientID:        "mock",
  1066  			OIDCClientSecret:    "very secret secret",
  1067  			BoundAudiences:      []string{"audience1", "audience2"},
  1068  			AllowedRedirectURIs: []string{"foo", "bar"},
  1069  			DiscoveryCaPem:      []string{"foo"},
  1070  			SigningAlgs:         []string{"bar"},
  1071  			ClaimMappings:       map[string]string{"foo": "bar"},
  1072  			ListClaimMappings:   map[string]string{"foo": "bar"},
  1073  		},
  1074  		CreateTime:  time.Now().UTC(),
  1075  		CreateIndex: 10,
  1076  		ModifyIndex: 10,
  1077  	}
  1078  
  1079  	am1.Merge(am2)
  1080  	must.Eq(t, am1.TokenLocality, "global")
  1081  	minTTL, _ := time.ParseDuration("10s")
  1082  	maxTTL, _ := time.ParseDuration("10h")
  1083  	must.NoError(t, am1.Validate(minTTL, maxTTL))
  1084  }
  1085  
  1086  func TestACLAuthMethodConfig_Copy(t *testing.T) {
  1087  	ci.Parallel(t)
  1088  
  1089  	amc1 := &ACLAuthMethodConfig{
  1090  		OIDCDiscoveryURL:    "http://example.com",
  1091  		OIDCClientID:        "mock",
  1092  		OIDCClientSecret:    "very secret secret",
  1093  		OIDCScopes:          []string{"groups"},
  1094  		BoundAudiences:      []string{"audience1", "audience2"},
  1095  		AllowedRedirectURIs: []string{"foo", "bar"},
  1096  		DiscoveryCaPem:      []string{"foo"},
  1097  		SigningAlgs:         []string{"bar"},
  1098  		ClaimMappings:       map[string]string{"foo": "bar"},
  1099  		ListClaimMappings:   map[string]string{"foo": "bar"},
  1100  	}
  1101  
  1102  	amc2 := amc1.Copy()
  1103  	must.Eq(t, amc1, amc2)
  1104  
  1105  	amc3 := amc1.Copy()
  1106  	amc3.AllowedRedirectURIs = []string{"new", "urls"}
  1107  	must.NotEq(t, amc1, amc3)
  1108  }
  1109  
  1110  func TestACLAuthMethod_Canonicalize(t *testing.T) {
  1111  	now := time.Now().UTC()
  1112  	tests := []struct {
  1113  		name        string
  1114  		inputMethod *ACLAuthMethod
  1115  	}{
  1116  		{
  1117  			"no create time or modify time set",
  1118  			&ACLAuthMethod{},
  1119  		},
  1120  		{
  1121  			"create time set to now, modify time not set",
  1122  			&ACLAuthMethod{CreateTime: now},
  1123  		},
  1124  		{
  1125  			"both create time and modify time set",
  1126  			&ACLAuthMethod{CreateTime: now, ModifyTime: now.Add(time.Hour)},
  1127  		},
  1128  	}
  1129  	for _, tt := range tests {
  1130  		t.Run(tt.name, func(t *testing.T) {
  1131  			existing := tt.inputMethod.Copy()
  1132  			tt.inputMethod.Canonicalize()
  1133  			if existing.CreateTime.IsZero() {
  1134  				must.NotEq(t, time.Time{}, tt.inputMethod.CreateTime)
  1135  			} else {
  1136  				must.Eq(t, existing.CreateTime, tt.inputMethod.CreateTime)
  1137  			}
  1138  			if existing.ModifyTime.IsZero() {
  1139  				must.NotEq(t, time.Time{}, tt.inputMethod.ModifyTime)
  1140  			}
  1141  		})
  1142  	}
  1143  }
  1144  
  1145  func TestACLAuthMethod_TokenLocalityIsGlobal(t *testing.T) {
  1146  	ci.Parallel(t)
  1147  
  1148  	globalAuthMethod := &ACLAuthMethod{TokenLocality: "global"}
  1149  	must.True(t, globalAuthMethod.TokenLocalityIsGlobal())
  1150  
  1151  	localAuthMethod := &ACLAuthMethod{TokenLocality: "local"}
  1152  	must.False(t, localAuthMethod.TokenLocalityIsGlobal())
  1153  }
  1154  
  1155  func TestACLBindingRule_Canonicalize(t *testing.T) {
  1156  	ci.Parallel(t)
  1157  
  1158  	testCases := []struct {
  1159  		name                string
  1160  		inputACLBindingRule *ACLBindingRule
  1161  	}{
  1162  		{
  1163  			name:                "new binding rule",
  1164  			inputACLBindingRule: &ACLBindingRule{},
  1165  		},
  1166  		{
  1167  			name: "existing binding rule",
  1168  			inputACLBindingRule: &ACLBindingRule{
  1169  				ID:         "some-random-uuid",
  1170  				CreateTime: time.Now().UTC(),
  1171  				ModifyTime: time.Now().UTC(),
  1172  			},
  1173  		},
  1174  	}
  1175  
  1176  	for _, tc := range testCases {
  1177  		t.Run(tc.name, func(t *testing.T) {
  1178  
  1179  			// Make a copy, so we can compare the modified object to it.
  1180  			copiedBindingRule := tc.inputACLBindingRule.Copy()
  1181  
  1182  			tc.inputACLBindingRule.Canonicalize()
  1183  
  1184  			if copiedBindingRule.ID == "" {
  1185  				must.NotEq(t, "", tc.inputACLBindingRule.ID)
  1186  				must.NotEq(t, copiedBindingRule.CreateTime, tc.inputACLBindingRule.CreateTime)
  1187  				must.NotEq(t, copiedBindingRule.ModifyTime, tc.inputACLBindingRule.ModifyTime)
  1188  			} else {
  1189  				must.Eq(t, copiedBindingRule.ID, tc.inputACLBindingRule.ID)
  1190  				must.Eq(t, copiedBindingRule.CreateTime, tc.inputACLBindingRule.CreateTime)
  1191  				must.NotEq(t, copiedBindingRule.ModifyTime, tc.inputACLBindingRule.ModifyTime)
  1192  			}
  1193  		})
  1194  	}
  1195  }
  1196  
  1197  func TestACLBindingRule_Validate(t *testing.T) {
  1198  	ci.Parallel(t)
  1199  
  1200  	testCases := []struct {
  1201  		name                string
  1202  		inputACLBindingRule *ACLBindingRule
  1203  		expectedError       error
  1204  	}{
  1205  		{
  1206  			name: "valid policy type rule",
  1207  			inputACLBindingRule: &ACLBindingRule{
  1208  				Description: "some short description",
  1209  				AuthMethod:  "auth0",
  1210  				Selector:    "group_name in list.groups",
  1211  				BindType:    ACLBindingRuleBindTypePolicy,
  1212  				BindName:    "some-policy-name",
  1213  			},
  1214  			expectedError: nil,
  1215  		},
  1216  		{
  1217  			name: "invalid policy type rule",
  1218  			inputACLBindingRule: &ACLBindingRule{
  1219  				Description: "some short description",
  1220  				AuthMethod:  "auth0",
  1221  				Selector:    "group_name in list.groups",
  1222  				BindType:    ACLBindingRuleBindTypePolicy,
  1223  				BindName:    "",
  1224  			},
  1225  			expectedError: &multierror.Error{
  1226  				Errors: []error{
  1227  					errors.New("bind name is missing"),
  1228  				},
  1229  			},
  1230  		},
  1231  		{
  1232  			name: "valid role type rule",
  1233  			inputACLBindingRule: &ACLBindingRule{
  1234  				Description: "some short description",
  1235  				AuthMethod:  "auth0",
  1236  				Selector:    "group_name in list.groups",
  1237  				BindType:    ACLBindingRuleBindTypeRole,
  1238  				BindName:    "some-role-name",
  1239  			},
  1240  			expectedError: nil,
  1241  		},
  1242  		{
  1243  			name: "invalid role type rule",
  1244  			inputACLBindingRule: &ACLBindingRule{
  1245  				Description: "some short description",
  1246  				AuthMethod:  "auth0",
  1247  				Selector:    "group_name in list.groups",
  1248  				BindType:    ACLBindingRuleBindTypeRole,
  1249  				BindName:    "",
  1250  			},
  1251  			expectedError: &multierror.Error{
  1252  				Errors: []error{
  1253  					errors.New("bind name is missing"),
  1254  				},
  1255  			},
  1256  		},
  1257  		{
  1258  			name: "valid management type rule",
  1259  			inputACLBindingRule: &ACLBindingRule{
  1260  				Description: "some short description",
  1261  				AuthMethod:  "auth0",
  1262  				Selector:    "group_name in list.groups",
  1263  				BindType:    ACLBindingRuleBindTypeManagement,
  1264  				BindName:    "",
  1265  			},
  1266  			expectedError: nil,
  1267  		},
  1268  		{
  1269  			name: "invalid management type rule",
  1270  			inputACLBindingRule: &ACLBindingRule{
  1271  				Description: "some short description",
  1272  				AuthMethod:  "auth0",
  1273  				Selector:    "group_name in list.groups",
  1274  				BindType:    ACLBindingRuleBindTypeManagement,
  1275  				BindName:    "some-name",
  1276  			},
  1277  			expectedError: &multierror.Error{
  1278  				Errors: []error{
  1279  					errors.New("bind name should be empty"),
  1280  				},
  1281  			},
  1282  		},
  1283  		{
  1284  			name: "invalid selector",
  1285  			inputACLBindingRule: &ACLBindingRule{
  1286  				Description: "some short description",
  1287  				AuthMethod:  "auth0",
  1288  				Selector:    "group-name in list.groups",
  1289  				BindType:    ACLBindingRuleBindTypePolicy,
  1290  				BindName:    "some-policy-name",
  1291  			},
  1292  			expectedError: &multierror.Error{
  1293  				Errors: []error{
  1294  					errors.New("selector is invalid: 1:6 (5): no match found, expected: \"!=\", \".\", \"==\", \"[\", [ \\t\\r\\n] or [a-zA-Z0-9_/]"),
  1295  				},
  1296  			},
  1297  		},
  1298  		{
  1299  			name: "invalid all",
  1300  			inputACLBindingRule: &ACLBindingRule{
  1301  				Description: uuid.Generate() + uuid.Generate() + uuid.Generate() +
  1302  					uuid.Generate() + uuid.Generate() + uuid.Generate() +
  1303  					uuid.Generate() + uuid.Generate(),
  1304  				AuthMethod: "",
  1305  				Selector:   "group-name in list.groups",
  1306  				BindType:   "",
  1307  				BindName:   "",
  1308  			},
  1309  			expectedError: &multierror.Error{
  1310  				Errors: []error{
  1311  					errors.New("auth method is missing"),
  1312  					errors.New("description longer than 256"),
  1313  					errors.New("bind type is missing"),
  1314  					errors.New("selector is invalid: 1:6 (5): no match found, expected: \"!=\", \".\", \"==\", \"[\", [ \\t\\r\\n] or [a-zA-Z0-9_/]"),
  1315  				},
  1316  			},
  1317  		},
  1318  	}
  1319  
  1320  	for _, tc := range testCases {
  1321  		t.Run(tc.name, func(t *testing.T) {
  1322  			must.Eq(t, tc.expectedError, tc.inputACLBindingRule.Validate())
  1323  		})
  1324  	}
  1325  }
  1326  
  1327  func TestACLBindingRule_Merge(t *testing.T) {
  1328  	ci.Parallel(t)
  1329  
  1330  	id := uuid.Short()
  1331  	br := &ACLBindingRule{
  1332  		ID:          id,
  1333  		Description: "old description",
  1334  		AuthMethod:  "example-acl-auth-method",
  1335  		BindType:    "rule",
  1336  		BindName:    "bind name",
  1337  		CreateTime:  time.Now().UTC(),
  1338  		CreateIndex: 10,
  1339  		ModifyIndex: 10,
  1340  	}
  1341  
  1342  	// make a description update
  1343  	br_description_update := &ACLBindingRule{
  1344  		ID:          id,
  1345  		Description: "new description",
  1346  	}
  1347  	br_description_update.Merge(br)
  1348  	must.Eq(t, br_description_update.Description, "new description")
  1349  	must.Eq(t, br_description_update.BindType, "rule")
  1350  }
  1351  
  1352  func TestACLBindingRule_SetHash(t *testing.T) {
  1353  	ci.Parallel(t)
  1354  
  1355  	bindingRule := &ACLBindingRule{
  1356  		ID:         uuid.Generate(),
  1357  		AuthMethod: "okta",
  1358  	}
  1359  	out1 := bindingRule.SetHash()
  1360  	must.NotNil(t, out1)
  1361  	must.NotNil(t, bindingRule.Hash)
  1362  	must.Eq(t, out1, bindingRule.Hash)
  1363  
  1364  	bindingRule.Description = "my lovely rule"
  1365  	out2 := bindingRule.SetHash()
  1366  	must.NotNil(t, out2)
  1367  	must.NotNil(t, bindingRule.Hash)
  1368  	must.Eq(t, out2, bindingRule.Hash)
  1369  	must.NotEq(t, out1, out2)
  1370  }
  1371  
  1372  func TestACLBindingRule_Equal(t *testing.T) {
  1373  	ci.Parallel(t)
  1374  
  1375  	aclBindingRule1 := &ACLBindingRule{
  1376  		ID:          uuid.Short(),
  1377  		Description: "mocked-acl-binding-rule",
  1378  		AuthMethod:  "auth0",
  1379  		Selector:    "engineering in list.roles",
  1380  		BindType:    "role-id",
  1381  		BindName:    "eng-ro",
  1382  		CreateTime:  time.Now().UTC(),
  1383  		ModifyTime:  time.Now().UTC(),
  1384  		CreateIndex: 10,
  1385  		ModifyIndex: 10,
  1386  	}
  1387  	aclBindingRule1.SetHash()
  1388  
  1389  	// Create a second binding rule, and modify this from the first.
  1390  	aclBindingRule2 := aclBindingRule1.Copy()
  1391  	aclBindingRule2.Description = ""
  1392  	aclBindingRule2.SetHash()
  1393  
  1394  	testCases := []struct {
  1395  		name    string
  1396  		method1 *ACLBindingRule
  1397  		method2 *ACLBindingRule
  1398  		want    bool
  1399  	}{
  1400  		{"one nil", aclBindingRule1, &ACLBindingRule{}, false},
  1401  		{"both nil", &ACLBindingRule{}, &ACLBindingRule{}, true},
  1402  		{"not equal", aclBindingRule1, aclBindingRule2, false},
  1403  		{"equal", aclBindingRule2, aclBindingRule2.Copy(), true},
  1404  	}
  1405  	for _, tc := range testCases {
  1406  		t.Run(tc.name, func(t *testing.T) {
  1407  			got := tc.method1.Equal(tc.method2)
  1408  			must.Eq(t, got, tc.want)
  1409  		})
  1410  	}
  1411  }
  1412  
  1413  func TestACLBindingRule_Copy(t *testing.T) {
  1414  	ci.Parallel(t)
  1415  
  1416  	aclBindingRule1 := &ACLBindingRule{
  1417  		ID:          uuid.Short(),
  1418  		Description: "mocked-acl-binding-rule",
  1419  		AuthMethod:  "auth0",
  1420  		Selector:    "engineering in list.roles",
  1421  		BindType:    "role-id",
  1422  		BindName:    "eng-ro",
  1423  		CreateTime:  time.Now().UTC(),
  1424  		ModifyTime:  time.Now().UTC(),
  1425  		CreateIndex: 10,
  1426  		ModifyIndex: 10,
  1427  	}
  1428  	aclBindingRule1.SetHash()
  1429  
  1430  	aclBindingRule2 := aclBindingRule1.Copy()
  1431  	must.Eq(t, aclBindingRule1, aclBindingRule2)
  1432  
  1433  	aclBindingRule3 := aclBindingRule1.Copy()
  1434  	aclBindingRule3.Description = ""
  1435  	aclBindingRule3.SetHash()
  1436  	must.NotEq(t, aclBindingRule1, aclBindingRule3)
  1437  }
  1438  
  1439  func TestACLBindingRule_Stub(t *testing.T) {
  1440  	ci.Parallel(t)
  1441  
  1442  	aclBindingRule := ACLBindingRule{
  1443  		ID:          "some-uuid",
  1444  		Description: "my-binding-rule",
  1445  		AuthMethod:  "auth0",
  1446  		Selector:    "some selector.pattern",
  1447  		BindType:    "role",
  1448  		BindName:    "some-role-id-or-name",
  1449  		CreateTime:  time.Now().UTC(),
  1450  		ModifyTime:  time.Now().UTC(),
  1451  		CreateIndex: 1309,
  1452  		ModifyIndex: 9031,
  1453  	}
  1454  	aclBindingRule.SetHash()
  1455  
  1456  	must.Eq(t, &ACLBindingRuleListStub{
  1457  		ID:          "some-uuid",
  1458  		Description: "my-binding-rule",
  1459  		AuthMethod:  "auth0",
  1460  		Hash:        aclBindingRule.Hash,
  1461  		CreateIndex: 1309,
  1462  		ModifyIndex: 9031,
  1463  	}, aclBindingRule.Stub())
  1464  }
  1465  
  1466  func Test_ACLBindingRulesUpsertRequest(t *testing.T) {
  1467  	ci.Parallel(t)
  1468  
  1469  	req := ACLBindingRulesUpsertRequest{}
  1470  	require.False(t, req.IsRead())
  1471  }
  1472  
  1473  func Test_ACLBindingRulesDeleteRequest(t *testing.T) {
  1474  	ci.Parallel(t)
  1475  
  1476  	req := ACLBindingRulesDeleteRequest{}
  1477  	require.False(t, req.IsRead())
  1478  }
  1479  
  1480  func Test_ACLBindingRulesListRequest(t *testing.T) {
  1481  	ci.Parallel(t)
  1482  
  1483  	req := ACLBindingRulesListRequest{}
  1484  	require.True(t, req.IsRead())
  1485  }
  1486  
  1487  func Test_ACLBindingRulesRequest(t *testing.T) {
  1488  	ci.Parallel(t)
  1489  
  1490  	req := ACLBindingRulesRequest{}
  1491  	require.True(t, req.IsRead())
  1492  }
  1493  
  1494  func Test_ACLBindingRuleRequest(t *testing.T) {
  1495  	ci.Parallel(t)
  1496  
  1497  	req := ACLBindingRuleRequest{}
  1498  	require.True(t, req.IsRead())
  1499  }
  1500  
  1501  func TestACLOIDCAuthURLRequest(t *testing.T) {
  1502  	ci.Parallel(t)
  1503  
  1504  	req := &ACLOIDCAuthURLRequest{}
  1505  	must.False(t, req.IsRead())
  1506  }
  1507  
  1508  func TestACLOIDCAuthURLRequest_Validate(t *testing.T) {
  1509  	ci.Parallel(t)
  1510  
  1511  	testRequest := &ACLOIDCAuthURLRequest{}
  1512  	err := testRequest.Validate()
  1513  	must.Error(t, err)
  1514  	must.StrContains(t, err.Error(), "missing auth method name")
  1515  	must.StrContains(t, err.Error(), "missing client nonce")
  1516  	must.StrContains(t, err.Error(), "missing redirect URI")
  1517  }
  1518  
  1519  func TestACLOIDCCompleteAuthRequest(t *testing.T) {
  1520  	ci.Parallel(t)
  1521  
  1522  	req := &ACLOIDCCompleteAuthRequest{}
  1523  	must.False(t, req.IsRead())
  1524  }
  1525  
  1526  func TestACLOIDCCompleteAuthRequest_Validate(t *testing.T) {
  1527  	ci.Parallel(t)
  1528  
  1529  	testRequest := &ACLOIDCCompleteAuthRequest{}
  1530  	err := testRequest.Validate()
  1531  	must.Error(t, err)
  1532  	must.StrContains(t, err.Error(), "missing auth method name")
  1533  	must.StrContains(t, err.Error(), "missing client nonce")
  1534  	must.StrContains(t, err.Error(), "missing state")
  1535  	must.StrContains(t, err.Error(), "missing code")
  1536  	must.StrContains(t, err.Error(), "missing redirect URI")
  1537  }