github.com/argoproj/argo-cd/v3@v3.2.1/util/rbac/rbac_test.go (about)

     1  package rbac
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/argoproj/argo-cd/v3/util/test"
    10  
    11  	"github.com/golang-jwt/jwt/v5"
    12  	log "github.com/sirupsen/logrus"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	corev1 "k8s.io/api/core/v1"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	"k8s.io/client-go/kubernetes/fake"
    18  
    19  	"github.com/argoproj/argo-cd/v3/util/assets"
    20  )
    21  
    22  const (
    23  	fakeConfigMapName = "fake-cm"
    24  	fakeNamespace     = "fake-ns"
    25  )
    26  
    27  var noOpUpdate = func(_ *corev1.ConfigMap) error {
    28  	return nil
    29  }
    30  
    31  func fakeConfigMap() *corev1.ConfigMap {
    32  	cm := corev1.ConfigMap{
    33  		TypeMeta: metav1.TypeMeta{
    34  			Kind:       "ConfigMap",
    35  			APIVersion: "v1",
    36  		},
    37  		ObjectMeta: metav1.ObjectMeta{
    38  			Name:      fakeConfigMapName,
    39  			Namespace: fakeNamespace,
    40  		},
    41  		Data: make(map[string]string),
    42  	}
    43  	return &cm
    44  }
    45  
    46  func TestPolicyCSV(t *testing.T) {
    47  	t.Run("will return empty string if data has no csv entries", func(t *testing.T) {
    48  		// given
    49  		data := make(map[string]string)
    50  
    51  		// when
    52  		policy := PolicyCSV(data)
    53  
    54  		// then
    55  		assert.Empty(t, policy)
    56  	})
    57  	t.Run("will return just policy defined with default key", func(t *testing.T) {
    58  		// given
    59  		data := make(map[string]string)
    60  		expectedPolicy := "policy1\npolicy2"
    61  		data[ConfigMapPolicyCSVKey] = expectedPolicy
    62  		data["UnrelatedKey"] = "unrelated value"
    63  
    64  		// when
    65  		policy := PolicyCSV(data)
    66  
    67  		// then
    68  		assert.Equal(t, expectedPolicy, policy)
    69  	})
    70  	t.Run("will return composed policy provided by multiple policy keys", func(t *testing.T) {
    71  		// given
    72  		data := make(map[string]string)
    73  		data[ConfigMapPolicyCSVKey] = "policy1"
    74  		data["UnrelatedKey"] = "unrelated value"
    75  		data["policy.overlay1.csv"] = "policy2"
    76  		data["policy.overlay2.csv"] = "policy3"
    77  
    78  		// when
    79  		policy := PolicyCSV(data)
    80  
    81  		// then
    82  		assert.Regexp(t, "^policy1", policy)
    83  		assert.Contains(t, policy, "policy2")
    84  		assert.Contains(t, policy, "policy3")
    85  	})
    86  	t.Run("will return composed policy in a deterministic order", func(t *testing.T) {
    87  		// given
    88  		data := make(map[string]string)
    89  		data["UnrelatedKey"] = "unrelated value"
    90  		data["policy.B.csv"] = "policyb"
    91  		data["policy.A.csv"] = "policya"
    92  		data["policy.C.csv"] = "policyc"
    93  		data[ConfigMapPolicyCSVKey] = "policy1"
    94  
    95  		// when
    96  		policy := PolicyCSV(data)
    97  
    98  		// then
    99  		result := strings.Split(policy, "\n")
   100  		assert.Len(t, result, 4)
   101  		assert.Equal(t, "policy1", result[0])
   102  		assert.Equal(t, "policya", result[1])
   103  		assert.Equal(t, "policyb", result[2])
   104  		assert.Equal(t, "policyc", result[3])
   105  	})
   106  }
   107  
   108  // TestBuiltinPolicyEnforcer tests the builtin policy rules
   109  func TestBuiltinPolicyEnforcer(t *testing.T) {
   110  	kubeclientset := fake.NewClientset()
   111  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   112  	require.NoError(t, enf.syncUpdate(fakeConfigMap(), noOpUpdate))
   113  
   114  	// Without setting builtin policy, this should fail
   115  	assert.False(t, enf.Enforce("admin", "applications", "get", "foo/bar"))
   116  
   117  	// now set builtin policy
   118  	_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
   119  
   120  	allowed := [][]any{
   121  		{"admin", "applications", "get", "foo/bar"},
   122  		{"admin", "applications", "delete", "foo/bar"},
   123  		{"role:readonly", "applications", "get", "foo/bar"},
   124  		{"role:admin", "applications", "get", "foo/bar"},
   125  		{"role:admin", "applications", "delete", "foo/bar"},
   126  	}
   127  	for _, a := range allowed {
   128  		if !assert.True(t, enf.Enforce(a...)) {
   129  			log.Errorf("%s: expected true, got false", a)
   130  		}
   131  	}
   132  
   133  	disallowed := [][]any{
   134  		{"role:readonly", "applications", "create", "foo/bar"},
   135  		{"role:readonly", "applications", "delete", "foo/bar"},
   136  	}
   137  	for _, a := range disallowed {
   138  		if !assert.False(t, enf.Enforce(a...)) {
   139  			log.Errorf("%s: expected false, got true", a)
   140  		}
   141  	}
   142  }
   143  
   144  // TestProjectIsolationEnforcement verifies the ability to create Project specific policies
   145  func TestProjectIsolationEnforcement(t *testing.T) {
   146  	kubeclientset := fake.NewClientset(fakeConfigMap())
   147  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   148  	policy := `
   149  p, role:foo-admin, *, *, foo/*, allow
   150  p, role:bar-admin, *, *, bar/*, allow
   151  g, alice, role:foo-admin
   152  g, bob, role:bar-admin
   153  `
   154  	_ = enf.SetBuiltinPolicy(policy)
   155  
   156  	// verify alice can only affect objects in foo and not bar,
   157  	// and that bob can only affect objects in bar and not foo
   158  	assert.True(t, enf.Enforce("bob", "applications", "delete", "bar/obj"))
   159  	assert.False(t, enf.Enforce("bob", "applications", "delete", "foo/obj"))
   160  	assert.True(t, enf.Enforce("alice", "applications", "delete", "foo/obj"))
   161  	assert.False(t, enf.Enforce("alice", "applications", "delete", "bar/obj"))
   162  }
   163  
   164  // TestProjectReadOnly verifies the ability to have a read only role in a Project
   165  func TestProjectReadOnly(t *testing.T) {
   166  	kubeclientset := fake.NewClientset(fakeConfigMap())
   167  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   168  	policy := `
   169  p, role:foo-readonly, *, get, foo/*, allow
   170  g, alice, role:foo-readonly
   171  `
   172  	_ = enf.SetBuiltinPolicy(policy)
   173  
   174  	assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   175  	assert.False(t, enf.Enforce("alice", "applications", "delete", "bar/obj"))
   176  	assert.False(t, enf.Enforce("alice", "applications", "get", "bar/obj"))
   177  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   178  }
   179  
   180  // TestDefaultRole tests the ability to set a default role
   181  func TestDefaultRole(t *testing.T) {
   182  	kubeclientset := fake.NewClientset()
   183  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   184  	require.NoError(t, enf.syncUpdate(fakeConfigMap(), noOpUpdate))
   185  	_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
   186  
   187  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/bar"))
   188  	// after setting the default role to be the read-only role, this should now pass
   189  	enf.SetDefaultRole("role:readonly")
   190  	assert.True(t, enf.Enforce("bob", "applications", "get", "foo/bar"))
   191  }
   192  
   193  // TestURLAsObjectName tests the ability to have a URL as an object name
   194  func TestURLAsObjectName(t *testing.T) {
   195  	kubeclientset := fake.NewClientset()
   196  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   197  	require.NoError(t, enf.syncUpdate(fakeConfigMap(), noOpUpdate))
   198  	policy := `
   199  p, alice, repositories, *, foo/*, allow
   200  p, bob, repositories, *, foo/https://github.com/argoproj/argo-cd.git, allow
   201  p, cathy, repositories, *, foo/*, allow
   202  `
   203  	_ = enf.SetUserPolicy(policy)
   204  
   205  	assert.True(t, enf.Enforce("alice", "repositories", "delete", "foo/https://github.com/argoproj/argo-cd.git"))
   206  	assert.True(t, enf.Enforce("alice", "repositories", "delete", "foo/https://github.com/golang/go.git"))
   207  
   208  	assert.True(t, enf.Enforce("bob", "repositories", "delete", "foo/https://github.com/argoproj/argo-cd.git"))
   209  	assert.False(t, enf.Enforce("bob", "repositories", "delete", "foo/https://github.com/golang/go.git"))
   210  }
   211  
   212  func TestEnableDisableEnforce(t *testing.T) {
   213  	kubeclientset := fake.NewClientset(fakeConfigMap())
   214  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   215  	policy := `
   216  p, alice, *, get, foo/obj, allow
   217  p, mike, *, get, foo/obj, deny
   218  `
   219  	_ = enf.SetUserPolicy(policy)
   220  	enf.SetClaimsEnforcerFunc(func(_ jwt.Claims, _ ...any) bool {
   221  		return false
   222  	})
   223  
   224  	assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   225  	assert.False(t, enf.Enforce("alice", "applications/resources", "delete", "foo/obj"))
   226  	assert.False(t, enf.Enforce("mike", "applications", "get", "foo/obj"))
   227  	assert.False(t, enf.Enforce("mike", "applications/resources", "delete", "foo/obj"))
   228  	assert.False(t, enf.Enforce(nil, "applications/resources", "delete", "foo/obj"))
   229  	assert.False(t, enf.Enforce(&jwt.RegisteredClaims{}, "applications/resources", "delete", "foo/obj"))
   230  
   231  	enf.EnableEnforce(false)
   232  	assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   233  	assert.True(t, enf.Enforce("alice", "applications/resources", "delete", "foo/obj"))
   234  	assert.True(t, enf.Enforce("mike", "applications", "get", "foo/obj"))
   235  	assert.True(t, enf.Enforce("mike", "applications/resources", "delete", "foo/obj"))
   236  	assert.True(t, enf.Enforce(nil, "applications/resources", "delete", "foo/obj"))
   237  	assert.True(t, enf.Enforce(&jwt.RegisteredClaims{}, "applications/resources", "delete", "foo/obj"))
   238  }
   239  
   240  func TestUpdatePolicy(t *testing.T) {
   241  	kubeclientset := fake.NewClientset(fakeConfigMap())
   242  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   243  
   244  	_ = enf.SetUserPolicy("p, alice, *, get, foo/obj, allow")
   245  	assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   246  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   247  
   248  	_ = enf.SetUserPolicy("p, bob, *, get, foo/obj, allow")
   249  	assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   250  	assert.True(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   251  
   252  	_ = enf.SetUserPolicy("")
   253  	assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   254  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   255  
   256  	_ = enf.SetBuiltinPolicy("p, alice, *, get, foo/obj, allow")
   257  	assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   258  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   259  
   260  	_ = enf.SetBuiltinPolicy("p, bob, *, get, foo/obj, allow")
   261  	assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   262  	assert.True(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   263  
   264  	_ = enf.SetBuiltinPolicy("")
   265  	assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   266  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   267  }
   268  
   269  func TestNoPolicy(t *testing.T) {
   270  	cm := fakeConfigMap()
   271  	kubeclientset := fake.NewClientset(cm)
   272  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   273  	assert.False(t, enf.Enforce("admin", "applications", "delete", "foo/bar"))
   274  }
   275  
   276  // TestValidatePolicyCheckUserDefinedPolicyReferentialIntegrity adds a hook into logrus.StandardLogger and validates
   277  // policies with and without referential integrity issues.  Log entries are searched to verify expected outcomes.
   278  func TestValidatePolicyCheckUserDefinedPolicyReferentialIntegrity(t *testing.T) {
   279  	// Policy with referential integrity
   280  	policy := `
   281  p, role:depA, *, get, foo/obj, allow
   282  p, role:depB, *, get, foo/obj, deny
   283  g, depA, role:depA
   284  g, depB, role:depB
   285  `
   286  	hook := test.LogHook{}
   287  	log.AddHook(&hook)
   288  	t.Cleanup(func() {
   289  		log.StandardLogger().ReplaceHooks(log.LevelHooks{})
   290  	})
   291  	require.NoError(t, ValidatePolicy(policy))
   292  	assert.Empty(t, hook.GetRegexMatchesInEntries("user defined roles not found in policies"))
   293  
   294  	// Policy with a role reference which transitively associates to policies
   295  	policy = `
   296  p, role:depA, *, get, foo/obj, allow
   297  p, role:depB, *, get, foo/obj, deny
   298  g, depC, role:depC
   299  g, role:depC, role:depA
   300  `
   301  	require.NoError(t, ValidatePolicy(policy))
   302  	assert.Empty(t, hook.GetRegexMatchesInEntries("user defined roles not found in policies"))
   303  
   304  	// Policy with a role reference which has no associated policies
   305  	policy = `
   306  p, role:depA, *, get, foo/obj, allow
   307  p, role:depB, *, get, foo/obj, deny
   308  g, depC, role:depC
   309  `
   310  	require.NoError(t, ValidatePolicy(policy))
   311  	assert.Len(t, hook.GetRegexMatchesInEntries("user defined roles not found in policies: role:depC"), 1)
   312  }
   313  
   314  // TestClaimsEnforcerFunc tests
   315  func TestClaimsEnforcerFunc(t *testing.T) {
   316  	kubeclientset := fake.NewClientset()
   317  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   318  	claims := jwt.RegisteredClaims{
   319  		Subject: "foo",
   320  	}
   321  	assert.False(t, enf.Enforce(&claims, "applications", "get", "foo/bar"))
   322  	enf.SetClaimsEnforcerFunc(func(_ jwt.Claims, _ ...any) bool {
   323  		return true
   324  	})
   325  	assert.True(t, enf.Enforce(&claims, "applications", "get", "foo/bar"))
   326  }
   327  
   328  // TestDefaultRoleWithRuntimePolicy tests the ability for a default role to still take affect when
   329  // enforcing a runtime policy
   330  func TestDefaultRoleWithRuntimePolicy(t *testing.T) {
   331  	kubeclientset := fake.NewClientset()
   332  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   333  	require.NoError(t, enf.syncUpdate(fakeConfigMap(), noOpUpdate))
   334  	runtimePolicy := assets.BuiltinPolicyCSV
   335  	assert.False(t, enf.EnforceRuntimePolicy("", runtimePolicy, "bob", "applications", "get", "foo/bar"))
   336  	enf.SetDefaultRole("role:readonly")
   337  	assert.True(t, enf.EnforceRuntimePolicy("", runtimePolicy, "bob", "applications", "get", "foo/bar"))
   338  }
   339  
   340  // TestClaimsEnforcerFuncWithRuntimePolicy tests the ability for claims enforcer function to still
   341  // take effect when enforcing a runtime policy
   342  func TestClaimsEnforcerFuncWithRuntimePolicy(t *testing.T) {
   343  	kubeclientset := fake.NewClientset()
   344  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   345  	require.NoError(t, enf.syncUpdate(fakeConfigMap(), noOpUpdate))
   346  	runtimePolicy := assets.BuiltinPolicyCSV
   347  	claims := jwt.RegisteredClaims{
   348  		Subject: "foo",
   349  	}
   350  	assert.False(t, enf.EnforceRuntimePolicy("", runtimePolicy, claims, "applications", "get", "foo/bar"))
   351  	enf.SetClaimsEnforcerFunc(func(_ jwt.Claims, _ ...any) bool {
   352  		return true
   353  	})
   354  	assert.True(t, enf.EnforceRuntimePolicy("", runtimePolicy, claims, "applications", "get", "foo/bar"))
   355  }
   356  
   357  // TestInvalidRuntimePolicy tests when an invalid policy is supplied, it falls back to normal enforcement
   358  func TestInvalidRuntimePolicy(t *testing.T) {
   359  	cm := fakeConfigMap()
   360  	kubeclientset := fake.NewClientset(cm)
   361  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   362  	require.NoError(t, enf.syncUpdate(fakeConfigMap(), noOpUpdate))
   363  	_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
   364  	assert.True(t, enf.EnforceRuntimePolicy("", "", "admin", "applications", "update", "foo/bar"))
   365  	assert.False(t, enf.EnforceRuntimePolicy("", "", "role:readonly", "applications", "update", "foo/bar"))
   366  	badPolicy := "this, is, not, a, good, policy"
   367  	assert.True(t, enf.EnforceRuntimePolicy("", badPolicy, "admin", "applications", "update", "foo/bar"))
   368  	assert.False(t, enf.EnforceRuntimePolicy("", badPolicy, "role:readonly", "applications", "update", "foo/bar"))
   369  }
   370  
   371  func TestValidatePolicy(t *testing.T) {
   372  	goodPolicies := []string{
   373  		"p, role:admin, projects, delete, *, allow",
   374  		"",
   375  		"#",
   376  		`p, "role,admin", projects, delete, *, allow`,
   377  		` p, role:admin, projects, delete, *, allow `,
   378  	}
   379  	for _, good := range goodPolicies {
   380  		require.NoError(t, ValidatePolicy(good))
   381  	}
   382  	badPolicies := []string{
   383  		"this, is, not, a, good, policy",
   384  		"this\ttoo",
   385  	}
   386  	for _, bad := range badPolicies {
   387  		require.Error(t, ValidatePolicy(bad))
   388  	}
   389  }
   390  
   391  // TestEnforceErrorMessage ensures we give descriptive error message
   392  func TestEnforceErrorMessage(t *testing.T) {
   393  	kubeclientset := fake.NewClientset()
   394  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   395  	err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
   396  	require.NoError(t, err)
   397  
   398  	require.EqualError(t, enf.EnforceErr("admin", "applications", "get", "foo/bar"), "rpc error: code = PermissionDenied desc = permission denied: applications, get, foo/bar")
   399  
   400  	require.EqualError(t, enf.EnforceErr(), "rpc error: code = PermissionDenied desc = permission denied")
   401  
   402  	//nolint:staticcheck
   403  	ctx := context.WithValue(t.Context(), "claims", &jwt.RegisteredClaims{Subject: "proj:default:admin"})
   404  	require.EqualError(t, enf.EnforceErr(ctx.Value("claims"), "project"), "rpc error: code = PermissionDenied desc = permission denied: project, sub: proj:default:admin")
   405  
   406  	iat := time.Unix(int64(1593035962), 0).Format(time.RFC3339)
   407  	exp := "rpc error: code = PermissionDenied desc = permission denied: project, sub: proj:default:admin, iat: " + iat
   408  	//nolint:staticcheck
   409  	ctx = context.WithValue(t.Context(), "claims", &jwt.RegisteredClaims{Subject: "proj:default:admin", IssuedAt: jwt.NewNumericDate(time.Unix(int64(1593035962), 0))})
   410  	require.EqualError(t, enf.EnforceErr(ctx.Value("claims"), "project"), exp)
   411  
   412  	//nolint:staticcheck
   413  	ctx = context.WithValue(t.Context(), "claims", &jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now())})
   414  	require.EqualError(t, enf.EnforceErr(ctx.Value("claims"), "project"), "rpc error: code = PermissionDenied desc = permission denied: project")
   415  
   416  	//nolint:staticcheck
   417  	ctx = context.WithValue(t.Context(), "claims", &jwt.RegisteredClaims{Subject: "proj:default:admin", IssuedAt: nil})
   418  	assert.EqualError(t, enf.EnforceErr(ctx.Value("claims"), "project"), "rpc error: code = PermissionDenied desc = permission denied: project, sub: proj:default:admin")
   419  }
   420  
   421  func TestDefaultGlobMatchMode(t *testing.T) {
   422  	kubeclientset := fake.NewClientset()
   423  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   424  	require.NoError(t, enf.syncUpdate(fakeConfigMap(), noOpUpdate))
   425  	policy := `
   426  p, alice, clusters, get, "https://github.com/*/*.git", allow
   427  `
   428  	_ = enf.SetUserPolicy(policy)
   429  
   430  	assert.True(t, enf.Enforce("alice", "clusters", "get", "https://github.com/argoproj/argo-cd.git"))
   431  	assert.False(t, enf.Enforce("alice", "repositories", "get", "https://github.com/argoproj/argo-cd.git"))
   432  }
   433  
   434  func TestGlobMatchMode(t *testing.T) {
   435  	cm := fakeConfigMap()
   436  	cm.Data[ConfigMapMatchModeKey] = GlobMatchMode
   437  	kubeclientset := fake.NewClientset()
   438  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   439  	require.NoError(t, enf.syncUpdate(cm, noOpUpdate))
   440  	policy := `
   441  p, alice, clusters, get, "https://github.com/*/*.git", allow
   442  `
   443  	_ = enf.SetUserPolicy(policy)
   444  
   445  	assert.True(t, enf.Enforce("alice", "clusters", "get", "https://github.com/argoproj/argo-cd.git"))
   446  	assert.False(t, enf.Enforce("alice", "clusters", "get", "https://github.com/argo-cd.git"))
   447  }
   448  
   449  func TestRegexMatchMode(t *testing.T) {
   450  	cm := fakeConfigMap()
   451  	cm.Data[ConfigMapMatchModeKey] = RegexMatchMode
   452  	kubeclientset := fake.NewClientset()
   453  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   454  	require.NoError(t, enf.syncUpdate(cm, noOpUpdate))
   455  	policy := `
   456  p, alice, clusters, get, "https://github.com/argo[a-z]{4}/argo-[a-z]+.git", allow
   457  `
   458  	_ = enf.SetUserPolicy(policy)
   459  
   460  	assert.True(t, enf.Enforce("alice", "clusters", "get", "https://github.com/argoproj/argo-cd.git"))
   461  	assert.False(t, enf.Enforce("alice", "clusters", "get", "https://github.com/argoproj/1argo-cd.git"))
   462  }
   463  
   464  func TestGlobMatchFunc(t *testing.T) {
   465  	ok, _ := globMatchFunc("arg1")
   466  	assert.False(t, ok.(bool))
   467  
   468  	ok, _ = globMatchFunc(time.Now(), "arg2")
   469  	assert.False(t, ok.(bool))
   470  
   471  	ok, _ = globMatchFunc("arg1", time.Now())
   472  	assert.False(t, ok.(bool))
   473  
   474  	ok, _ = globMatchFunc("arg/123", "arg/*")
   475  	assert.True(t, ok.(bool))
   476  }
   477  
   478  func TestLoadPolicyLine(t *testing.T) {
   479  	t.Run("Valid permission line", func(t *testing.T) {
   480  		policy := `p, role:Myrole, applications, *, myproj/*, allow`
   481  		model := newBuiltInModel()
   482  		require.NoError(t, loadPolicyLine(policy, model))
   483  	})
   484  	t.Run("Valid grant line", func(t *testing.T) {
   485  		policy := `g, your-github-org:your-team, role:org-admin`
   486  		model := newBuiltInModel()
   487  		require.NoError(t, loadPolicyLine(policy, model))
   488  	})
   489  	t.Run("Empty policy line", func(t *testing.T) {
   490  		policy := ""
   491  		model := newBuiltInModel()
   492  		require.NoError(t, loadPolicyLine(policy, model))
   493  	})
   494  	t.Run("Comment policy line", func(t *testing.T) {
   495  		policy := "# Some comment"
   496  		model := newBuiltInModel()
   497  		require.NoError(t, loadPolicyLine(policy, model))
   498  	})
   499  	t.Run("Invalid policy line: single token", func(t *testing.T) {
   500  		policy := "p"
   501  		model := newBuiltInModel()
   502  		require.Error(t, loadPolicyLine(policy, model))
   503  	})
   504  	t.Run("Invalid policy line: plain text", func(t *testing.T) {
   505  		policy := "Some comment"
   506  		model := newBuiltInModel()
   507  		require.Error(t, loadPolicyLine(policy, model))
   508  	})
   509  	t.Run("Invalid policy line", func(t *testing.T) {
   510  		policy := "agh, foo, bar"
   511  		model := newBuiltInModel()
   512  		require.Error(t, loadPolicyLine(policy, model))
   513  	})
   514  	t.Run("Invalid policy line missing comma", func(t *testing.T) {
   515  		policy := "p, role:Myrole, applications, *, myproj/* allow"
   516  		model := newBuiltInModel()
   517  		require.Error(t, loadPolicyLine(policy, model))
   518  	})
   519  	t.Run("Invalid policy line missing policy type", func(t *testing.T) {
   520  		policy := ", role:Myrole, applications, *, myproj/*, allow"
   521  		model := newBuiltInModel()
   522  		require.Error(t, loadPolicyLine(policy, model))
   523  	})
   524  }