diff --git a/server/server.go b/server/server.go index b6f8d4cff4..e2aac4f3d6 100644 --- a/server/server.go +++ b/server/server.go @@ -106,7 +106,7 @@ func NewServer(opts ArgoCDServerOpts) *ArgoCDServer { errors.CheckError(err) sessionMgr := util_session.NewSessionManager(settings) - enf := rbac.NewEnforcer(opts.KubeClientset, opts.Namespace, common.ArgoCDRBACConfigMapName) + enf := rbac.NewEnforcer(opts.KubeClientset, opts.Namespace, common.ArgoCDRBACConfigMapName, nil) enf.EnableEnforce(!opts.DisableAuth) err = enf.SetBuiltinPolicy(builtinPolicy) errors.CheckError(err) diff --git a/test/e2e/fixture.go b/test/e2e/fixture.go index 38c5bde5fc..721ef9d94a 100644 --- a/test/e2e/fixture.go +++ b/test/e2e/fixture.go @@ -248,7 +248,7 @@ func NewFixture() (*Fixture, error) { return nil, err } db := db.NewDB(namespace, kubeClient) - enforcer := rbac.NewEnforcer(kubeClient, namespace, common.ArgoCDRBACConfigMapName) + enforcer := rbac.NewEnforcer(kubeClient, namespace, common.ArgoCDRBACConfigMapName, nil) enforcer.SetDefaultRole("role:admin") fixture := &Fixture{ diff --git a/util/rbac/rbac.go b/util/rbac/rbac.go index 625f91987e..8b455fbdf6 100644 --- a/util/rbac/rbac.go +++ b/util/rbac/rbac.go @@ -28,11 +28,15 @@ const ( defaultRBACSyncPeriod = 10 * time.Minute ) +// ClaimsEnforcerFunc is func template +type ClaimsEnforcerFunc func(rvals ...interface{}) bool + type Enforcer struct { *casbin.Enforcer - clientset kubernetes.Interface - namespace string - configmap string + clientset kubernetes.Interface + namespace string + configmap string + claimsEnforcerFunc ClaimsEnforcerFunc model model.Model defaultRole string @@ -40,18 +44,19 @@ type Enforcer struct { userDefinedPolicy string } -func NewEnforcer(clientset kubernetes.Interface, namespace, configmap string) *Enforcer { +func NewEnforcer(clientset kubernetes.Interface, namespace, configmap string, claimsEnforcer ClaimsEnforcerFunc) *Enforcer { box := packr.NewBox(".") modelConf := box.String(builtinModelFile) model := casbin.NewModel(modelConf) enf := casbin.Enforcer{} enf.EnableLog(false) return &Enforcer{ - Enforcer: &enf, - clientset: clientset, - namespace: namespace, - configmap: configmap, - model: model, + Enforcer: &enf, + clientset: clientset, + namespace: namespace, + configmap: configmap, + model: model, + claimsEnforcerFunc: claimsEnforcer, } } @@ -61,6 +66,13 @@ func (e *Enforcer) SetDefaultRole(roleName string) { e.defaultRole = roleName } +// SetClaimsEnforcerFunc sets a claims enforce function during enforcement. The claims enforce function +// can extract claims from JWT token and do the proper enforcement based on user, group or any information +// available in the input parameter list +func (e *Enforcer) SetClaimsEnforcerFunc(claimsEnforcer ClaimsEnforcerFunc) { + e.claimsEnforcerFunc = claimsEnforcer +} + // Enforce is a wrapper around casbin.Enforce to additionally enforce a default role func (e *Enforcer) Enforce(rvals ...interface{}) bool { if e.Enforcer.Enforce(rvals...) { @@ -75,6 +87,16 @@ func (e *Enforcer) Enforce(rvals ...interface{}) bool { // EnforceClaims checks if the first value is a jwt.Claims and runs enforce against its groups and sub func (e *Enforcer) EnforceClaims(rvals ...interface{}) bool { + + // User default claims enforcer if it is nil + if e.claimsEnforcerFunc == nil { + return e.defaultEnforceClaims(rvals...) + } + + return e.claimsEnforcerFunc(rvals...) +} + +func (e *Enforcer) defaultEnforceClaims(rvals ...interface{}) bool { claims, ok := rvals[0].(jwt.Claims) if !ok { if rvals[0] == nil { diff --git a/util/rbac/rbac_test.go b/util/rbac/rbac_test.go index 5dc18efe84..20ab723b8c 100644 --- a/util/rbac/rbac_test.go +++ b/util/rbac/rbac_test.go @@ -47,7 +47,7 @@ func fakeConfigMap(policy ...string) *apiv1.ConfigMap { // TestBuiltinPolicyEnforcer tests the builtin policy rules func TestBuiltinPolicyEnforcer(t *testing.T) { kubeclientset := fake.NewSimpleClientset() - enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName) + enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil) err := enf.syncUpdate(fakeConfigMap()) assert.Nil(t, err) @@ -85,7 +85,7 @@ func TestBuiltinPolicyEnforcer(t *testing.T) { func TestPolicyInformer(t *testing.T) { cm := fakeConfigMap() kubeclientset := fake.NewSimpleClientset(cm) - enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName) + enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil) ctx := context.Background() ctx, cancel := context.WithCancel(ctx) @@ -113,7 +113,7 @@ func TestPolicyInformer(t *testing.T) { // TestProjectIsolationEnforcement verifies the ability to create Project specific policies func TestProjectIsolationEnforcement(t *testing.T) { kubeclientset := fake.NewSimpleClientset(fakeConfigMap()) - enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName) + enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil) policy := ` p, role:foo-admin, *, *, foo/* p, role:bar-admin, *, *, bar/* @@ -133,7 +133,7 @@ g, bob, role:bar-admin // TestProjectReadOnly verifies the ability to have a read only role in a Project func TestProjectReadOnly(t *testing.T) { kubeclientset := fake.NewSimpleClientset(fakeConfigMap()) - enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName) + enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil) policy := ` p, role:foo-readonly, *, get, foo/* g, alice, role:foo-readonly @@ -148,7 +148,7 @@ g, alice, role:foo-readonly func TestEnforceClaims(t *testing.T) { kubeclientset := fake.NewSimpleClientset(fakeConfigMap()) - enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName) + enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil) enf.SetBuiltinPolicy(box.String(builtinPolicyFile)) policy := ` g, org2:team2, role:admin @@ -179,7 +179,7 @@ g, bob, role:admin // TestDefaultRole tests the ability to set a default role func TestDefaultRole(t *testing.T) { kubeclientset := fake.NewSimpleClientset() - enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName) + enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil) err := enf.syncUpdate(fakeConfigMap()) assert.Nil(t, err) enf.SetBuiltinPolicy(box.String(builtinPolicyFile)) @@ -196,7 +196,7 @@ func TestDefaultRole(t *testing.T) { // TestURLAsObjectName tests the ability to have a URL as an object name func TestURLAsObjectName(t *testing.T) { kubeclientset := fake.NewSimpleClientset() - enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName) + enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil) err := enf.syncUpdate(fakeConfigMap()) assert.Nil(t, err) policy := ` @@ -216,7 +216,7 @@ p, cathy, repositories, *, foo/* func TestEnforceNilClaims(t *testing.T) { kubeclientset := fake.NewSimpleClientset(fakeConfigMap()) - enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName) + enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil) enf.SetBuiltinPolicy(box.String(builtinPolicyFile)) assert.False(t, enf.EnforceClaims(nil, "applications", "get", "foo/obj")) enf.SetDefaultRole("role:readonly")