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

     1  package rbac
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/dgrijalva/jwt-go/v4"
    10  	log "github.com/sirupsen/logrus"
    11  	"github.com/stretchr/testify/assert"
    12  	apiv1 "k8s.io/api/core/v1"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/client-go/kubernetes/fake"
    15  
    16  	"github.com/argoproj/argo-cd/util/assets"
    17  )
    18  
    19  const (
    20  	fakeConfigMapName = "fake-cm"
    21  	fakeNamespace     = "fake-ns"
    22  )
    23  
    24  var (
    25  	noOpUpdate = func(cm *apiv1.ConfigMap) error {
    26  		return nil
    27  	}
    28  )
    29  
    30  func fakeConfigMap() *apiv1.ConfigMap {
    31  	cm := apiv1.ConfigMap{
    32  		TypeMeta: metav1.TypeMeta{
    33  			Kind:       "ConfigMap",
    34  			APIVersion: "v1",
    35  		},
    36  		ObjectMeta: metav1.ObjectMeta{
    37  			Name:      fakeConfigMapName,
    38  			Namespace: fakeNamespace,
    39  		},
    40  		Data: make(map[string]string),
    41  	}
    42  	return &cm
    43  }
    44  
    45  // TestBuiltinPolicyEnforcer tests the builtin policy rules
    46  func TestBuiltinPolicyEnforcer(t *testing.T) {
    47  	kubeclientset := fake.NewSimpleClientset()
    48  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
    49  	err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
    50  	assert.Nil(t, err)
    51  
    52  	// Without setting builtin policy, this should fail
    53  	assert.False(t, enf.Enforce("admin", "applications", "get", "foo/bar"))
    54  
    55  	// now set builtin policy
    56  	_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
    57  
    58  	allowed := [][]interface{}{
    59  		{"admin", "applications", "get", "foo/bar"},
    60  		{"admin", "applications", "delete", "foo/bar"},
    61  		{"role:readonly", "applications", "get", "foo/bar"},
    62  		{"role:admin", "applications", "get", "foo/bar"},
    63  		{"role:admin", "applications", "delete", "foo/bar"},
    64  	}
    65  	for _, a := range allowed {
    66  		if !assert.True(t, enf.Enforce(a...)) {
    67  			log.Errorf("%s: expected true, got false", a)
    68  		}
    69  	}
    70  
    71  	disallowed := [][]interface{}{
    72  		{"role:readonly", "applications", "create", "foo/bar"},
    73  		{"role:readonly", "applications", "delete", "foo/bar"},
    74  	}
    75  	for _, a := range disallowed {
    76  		if !assert.False(t, enf.Enforce(a...)) {
    77  			log.Errorf("%s: expected false, got true", a)
    78  		}
    79  	}
    80  }
    81  
    82  // TestProjectIsolationEnforcement verifies the ability to create Project specific policies
    83  func TestProjectIsolationEnforcement(t *testing.T) {
    84  	kubeclientset := fake.NewSimpleClientset(fakeConfigMap())
    85  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
    86  	policy := `
    87  p, role:foo-admin, *, *, foo/*, allow
    88  p, role:bar-admin, *, *, bar/*, allow
    89  g, alice, role:foo-admin
    90  g, bob, role:bar-admin
    91  `
    92  	_ = enf.SetBuiltinPolicy(policy)
    93  
    94  	// verify alice can only affect objects in foo and not bar,
    95  	// and that bob can only affect objects in bar and not foo
    96  	assert.True(t, enf.Enforce("bob", "applications", "delete", "bar/obj"))
    97  	assert.False(t, enf.Enforce("bob", "applications", "delete", "foo/obj"))
    98  	assert.True(t, enf.Enforce("alice", "applications", "delete", "foo/obj"))
    99  	assert.False(t, enf.Enforce("alice", "applications", "delete", "bar/obj"))
   100  }
   101  
   102  // TestProjectReadOnly verifies the ability to have a read only role in a Project
   103  func TestProjectReadOnly(t *testing.T) {
   104  	kubeclientset := fake.NewSimpleClientset(fakeConfigMap())
   105  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   106  	policy := `
   107  p, role:foo-readonly, *, get, foo/*, allow
   108  g, alice, role:foo-readonly
   109  `
   110  	_ = enf.SetBuiltinPolicy(policy)
   111  
   112  	assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   113  	assert.False(t, enf.Enforce("alice", "applications", "delete", "bar/obj"))
   114  	assert.False(t, enf.Enforce("alice", "applications", "get", "bar/obj"))
   115  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   116  }
   117  
   118  // TestDefaultRole tests the ability to set a default role
   119  func TestDefaultRole(t *testing.T) {
   120  	kubeclientset := fake.NewSimpleClientset()
   121  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   122  	err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
   123  	assert.Nil(t, err)
   124  	_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
   125  
   126  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/bar"))
   127  	// after setting the default role to be the read-only role, this should now pass
   128  	enf.SetDefaultRole("role:readonly")
   129  	assert.True(t, enf.Enforce("bob", "applications", "get", "foo/bar"))
   130  }
   131  
   132  // TestURLAsObjectName tests the ability to have a URL as an object name
   133  func TestURLAsObjectName(t *testing.T) {
   134  	kubeclientset := fake.NewSimpleClientset()
   135  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   136  	err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
   137  	assert.Nil(t, err)
   138  	policy := `
   139  p, alice, repositories, *, foo/*, allow
   140  p, bob, repositories, *, foo/https://github.com/argoproj/argo-cd.git, allow
   141  p, cathy, repositories, *, foo/*, allow
   142  `
   143  	_ = enf.SetUserPolicy(policy)
   144  
   145  	assert.True(t, enf.Enforce("alice", "repositories", "delete", "foo/https://github.com/argoproj/argo-cd.git"))
   146  	assert.True(t, enf.Enforce("alice", "repositories", "delete", "foo/https://github.com/golang/go.git"))
   147  
   148  	assert.True(t, enf.Enforce("bob", "repositories", "delete", "foo/https://github.com/argoproj/argo-cd.git"))
   149  	assert.False(t, enf.Enforce("bob", "repositories", "delete", "foo/https://github.com/golang/go.git"))
   150  
   151  }
   152  
   153  func TestEnableDisableEnforce(t *testing.T) {
   154  	kubeclientset := fake.NewSimpleClientset(fakeConfigMap())
   155  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   156  	policy := `
   157  p, alice, *, get, foo/obj, allow
   158  p, mike, *, get, foo/obj, deny
   159  `
   160  	_ = enf.SetUserPolicy(policy)
   161  	enf.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool {
   162  		return false
   163  	})
   164  
   165  	assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   166  	assert.False(t, enf.Enforce("alice", "applications/resources", "delete", "foo/obj"))
   167  	assert.False(t, enf.Enforce("mike", "applications", "get", "foo/obj"))
   168  	assert.False(t, enf.Enforce("mike", "applications/resources", "delete", "foo/obj"))
   169  	assert.False(t, enf.Enforce(nil, "applications/resources", "delete", "foo/obj"))
   170  	assert.False(t, enf.Enforce(&jwt.StandardClaims{}, "applications/resources", "delete", "foo/obj"))
   171  
   172  	enf.EnableEnforce(false)
   173  	assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   174  	assert.True(t, enf.Enforce("alice", "applications/resources", "delete", "foo/obj"))
   175  	assert.True(t, enf.Enforce("mike", "applications", "get", "foo/obj"))
   176  	assert.True(t, enf.Enforce("mike", "applications/resources", "delete", "foo/obj"))
   177  	assert.True(t, enf.Enforce(nil, "applications/resources", "delete", "foo/obj"))
   178  	assert.True(t, enf.Enforce(&jwt.StandardClaims{}, "applications/resources", "delete", "foo/obj"))
   179  }
   180  
   181  func TestUpdatePolicy(t *testing.T) {
   182  	kubeclientset := fake.NewSimpleClientset(fakeConfigMap())
   183  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   184  
   185  	_ = enf.SetUserPolicy("p, alice, *, get, foo/obj, allow")
   186  	assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   187  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   188  
   189  	_ = enf.SetUserPolicy("p, bob, *, get, foo/obj, allow")
   190  	assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   191  	assert.True(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   192  
   193  	_ = enf.SetUserPolicy("")
   194  	assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   195  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   196  
   197  	_ = enf.SetBuiltinPolicy("p, alice, *, get, foo/obj, allow")
   198  	assert.True(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   199  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   200  
   201  	_ = enf.SetBuiltinPolicy("p, bob, *, get, foo/obj, allow")
   202  	assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   203  	assert.True(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   204  
   205  	_ = enf.SetBuiltinPolicy("")
   206  	assert.False(t, enf.Enforce("alice", "applications", "get", "foo/obj"))
   207  	assert.False(t, enf.Enforce("bob", "applications", "get", "foo/obj"))
   208  }
   209  
   210  func TestNoPolicy(t *testing.T) {
   211  	cm := fakeConfigMap()
   212  	kubeclientset := fake.NewSimpleClientset(cm)
   213  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   214  	assert.False(t, enf.Enforce("admin", "applications", "delete", "foo/bar"))
   215  }
   216  
   217  // TestClaimsEnforcerFunc tests
   218  func TestClaimsEnforcerFunc(t *testing.T) {
   219  	kubeclientset := fake.NewSimpleClientset()
   220  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   221  	claims := jwt.StandardClaims{
   222  		Subject: "foo",
   223  	}
   224  	assert.False(t, enf.Enforce(&claims, "applications", "get", "foo/bar"))
   225  	enf.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool {
   226  		return true
   227  	})
   228  	assert.True(t, enf.Enforce(&claims, "applications", "get", "foo/bar"))
   229  }
   230  
   231  // TestDefaultRoleWithRuntimePolicy tests the ability for a default role to still take affect when
   232  // enforcing a runtime policy
   233  func TestDefaultRoleWithRuntimePolicy(t *testing.T) {
   234  	kubeclientset := fake.NewSimpleClientset()
   235  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   236  	err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
   237  	assert.Nil(t, err)
   238  	runtimePolicy := assets.BuiltinPolicyCSV
   239  	assert.False(t, enf.EnforceRuntimePolicy(runtimePolicy, "bob", "applications", "get", "foo/bar"))
   240  	enf.SetDefaultRole("role:readonly")
   241  	assert.True(t, enf.EnforceRuntimePolicy(runtimePolicy, "bob", "applications", "get", "foo/bar"))
   242  }
   243  
   244  // TestClaimsEnforcerFuncWithRuntimePolicy tests the ability for claims enforcer function to still
   245  // take effect when enforcing a runtime policy
   246  func TestClaimsEnforcerFuncWithRuntimePolicy(t *testing.T) {
   247  	kubeclientset := fake.NewSimpleClientset()
   248  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   249  	err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
   250  	assert.Nil(t, err)
   251  	runtimePolicy := assets.BuiltinPolicyCSV
   252  	claims := jwt.StandardClaims{
   253  		Subject: "foo",
   254  	}
   255  	assert.False(t, enf.EnforceRuntimePolicy(runtimePolicy, claims, "applications", "get", "foo/bar"))
   256  	enf.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool {
   257  		return true
   258  	})
   259  	assert.True(t, enf.EnforceRuntimePolicy(runtimePolicy, claims, "applications", "get", "foo/bar"))
   260  }
   261  
   262  // TestInvalidRuntimePolicy tests when an invalid policy is supplied, it falls back to normal enforcement
   263  func TestInvalidRuntimePolicy(t *testing.T) {
   264  	cm := fakeConfigMap()
   265  	kubeclientset := fake.NewSimpleClientset(cm)
   266  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   267  	err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
   268  	assert.Nil(t, err)
   269  	_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
   270  	assert.True(t, enf.EnforceRuntimePolicy("", "admin", "applications", "update", "foo/bar"))
   271  	assert.False(t, enf.EnforceRuntimePolicy("", "role:readonly", "applications", "update", "foo/bar"))
   272  	badPolicy := "this, is, not, a, good, policy"
   273  	assert.True(t, enf.EnforceRuntimePolicy(badPolicy, "admin", "applications", "update", "foo/bar"))
   274  	assert.False(t, enf.EnforceRuntimePolicy(badPolicy, "role:readonly", "applications", "update", "foo/bar"))
   275  }
   276  
   277  func TestValidatePolicy(t *testing.T) {
   278  	goodPolicies := []string{
   279  		"p, role:admin, projects, delete, *, allow",
   280  		"",
   281  		"#",
   282  		`p, "role,admin", projects, delete, *, allow`,
   283  		` p, role:admin, projects, delete, *, allow `,
   284  	}
   285  	for _, good := range goodPolicies {
   286  		assert.Nil(t, ValidatePolicy(good))
   287  	}
   288  	badPolicies := []string{
   289  		"this, is, not, a, good, policy",
   290  		"this\ttoo",
   291  	}
   292  	for _, bad := range badPolicies {
   293  		assert.Error(t, ValidatePolicy(bad))
   294  	}
   295  }
   296  
   297  // TestEnforceErrorMessage ensures we give descriptive error message
   298  func TestEnforceErrorMessage(t *testing.T) {
   299  	kubeclientset := fake.NewSimpleClientset()
   300  	enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfigMapName, nil)
   301  	err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
   302  	assert.Nil(t, err)
   303  
   304  	err = enf.EnforceErr("admin", "applications", "get", "foo/bar")
   305  	assert.Error(t, err)
   306  	assert.Equal(t, "rpc error: code = PermissionDenied desc = permission denied: applications, get, foo/bar", err.Error())
   307  
   308  	err = enf.EnforceErr()
   309  	assert.Error(t, err)
   310  	assert.Equal(t, "rpc error: code = PermissionDenied desc = permission denied", err.Error())
   311  
   312  	// nolint:staticcheck
   313  	ctx := context.WithValue(context.Background(), "claims", &jwt.StandardClaims{Subject: "proj:default:admin"})
   314  	err = enf.EnforceErr(ctx.Value("claims"), "project")
   315  	assert.Error(t, err)
   316  	assert.Equal(t, "rpc error: code = PermissionDenied desc = permission denied: project, sub: proj:default:admin", err.Error())
   317  
   318  	iat := time.Unix(int64(1593035962), 0).Format(time.RFC3339)
   319  	exp := fmt.Sprintf("rpc error: code = PermissionDenied desc = permission denied: project, sub: proj:default:admin, iat: %s", iat)
   320  	// nolint:staticcheck
   321  	ctx = context.WithValue(context.Background(), "claims", &jwt.StandardClaims{Subject: "proj:default:admin", IssuedAt: jwt.NewTime(1593035962)})
   322  	err = enf.EnforceErr(ctx.Value("claims"), "project")
   323  	assert.Error(t, err)
   324  	assert.Equal(t, exp, err.Error())
   325  
   326  	// nolint:staticcheck
   327  	ctx = context.WithValue(context.Background(), "claims", &jwt.StandardClaims{ExpiresAt: jwt.NewTime(1)})
   328  	err = enf.EnforceErr(ctx.Value("claims"), "project")
   329  	assert.Error(t, err)
   330  	assert.Equal(t, "rpc error: code = PermissionDenied desc = permission denied: project", err.Error())
   331  
   332  	// nolint:staticcheck
   333  	ctx = context.WithValue(context.Background(), "claims", &jwt.StandardClaims{Subject: "proj:default:admin", IssuedAt: nil})
   334  	err = enf.EnforceErr(ctx.Value("claims"), "project")
   335  	assert.Error(t, err)
   336  	assert.Equal(t, "rpc error: code = PermissionDenied desc = permission denied: project, sub: proj:default:admin", err.Error())
   337  
   338  }