github.com/argoproj/argo-cd/v3@v3.2.1/server/rbacpolicy/rbacpolicy_test.go (about) 1 package rbacpolicy 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/golang-jwt/jwt/v5" 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/client-go/kubernetes/fake" 12 13 "github.com/argoproj/argo-cd/v3/common" 14 argoappv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 15 "github.com/argoproj/argo-cd/v3/test" 16 "github.com/argoproj/argo-cd/v3/util/rbac" 17 ) 18 19 func newFakeProj() *argoappv1.AppProject { 20 jwtTokenByRole := make(map[string]argoappv1.JWTTokens) 21 jwtTokenByRole["my-role"] = argoappv1.JWTTokens{Items: []argoappv1.JWTToken{{IssuedAt: 1234}}} 22 23 return &argoappv1.AppProject{ 24 ObjectMeta: metav1.ObjectMeta{ 25 Name: "my-proj", 26 Namespace: test.FakeArgoCDNamespace, 27 }, 28 Spec: argoappv1.AppProjectSpec{ 29 Roles: []argoappv1.ProjectRole{ 30 { 31 Name: "my-role", 32 Policies: []string{ 33 "p, proj:my-proj:my-role, applications, create, my-proj/*, allow", 34 "p, proj:my-proj:my-role, logs, get, my-proj/*, allow", 35 "p, proj:my-proj:my-role, exec, create, my-proj/*, allow", 36 }, 37 Groups: []string{ 38 "my-org:my-team", 39 }, 40 JWTTokens: []argoappv1.JWTToken{ 41 { 42 IssuedAt: 1234, 43 }, 44 }, 45 }, 46 }, 47 }, 48 Status: argoappv1.AppProjectStatus{JWTTokensByRole: jwtTokenByRole}, 49 } 50 } 51 52 func TestEnforceAllPolicies(t *testing.T) { 53 kubeclientset := fake.NewClientset(test.NewFakeConfigMap()) 54 projLister := test.NewFakeProjLister(newFakeProj()) 55 enf := rbac.NewEnforcer(kubeclientset, test.FakeArgoCDNamespace, common.ArgoCDConfigMapName, nil) 56 enf.EnableLog(true) 57 _ = enf.SetBuiltinPolicy(`p, alice, applications, create, my-proj/*, allow` + "\n" + `p, alice, logs, get, my-proj/*, allow` + "\n" + `p, alice, exec, create, my-proj/*, allow`) 58 _ = enf.SetUserPolicy(`p, bob, applications, create, my-proj/*, allow` + "\n" + `p, bob, logs, get, my-proj/*, allow` + "\n" + `p, bob, exec, create, my-proj/*, allow`) 59 rbacEnf := NewRBACPolicyEnforcer(enf, projLister) 60 enf.SetClaimsEnforcerFunc(rbacEnf.EnforceClaims) 61 62 claims := jwt.MapClaims{"sub": "alice"} 63 assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 64 assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app")) 65 assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app")) 66 67 claims = jwt.MapClaims{"sub": "bob"} 68 assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 69 assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app")) 70 assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app")) 71 72 claims = jwt.MapClaims{"sub": "qwertyuiop", "federated_claims": map[string]any{"user_id": "bob"}} 73 assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 74 assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app")) 75 assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app")) 76 77 claims = jwt.MapClaims{"sub": "proj:my-proj:my-role", "iat": 1234} 78 assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 79 assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app")) 80 assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app")) 81 82 claims = jwt.MapClaims{"groups": []string{"my-org:my-team"}} 83 assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 84 assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app")) 85 assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app")) 86 87 claims = jwt.MapClaims{"sub": "cathy"} 88 assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 89 assert.False(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app")) 90 assert.False(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app")) 91 92 // AWS cognito returns its groups in cognito:groups 93 rbacEnf.SetScopes([]string{"cognito:groups"}) 94 claims = jwt.MapClaims{"cognito:groups": []string{"my-org:my-team"}} 95 assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 96 } 97 98 func TestEnforceActionActions(t *testing.T) { 99 kubeclientset := fake.NewClientset(test.NewFakeConfigMap()) 100 projLister := test.NewFakeProjLister(newFakeProj()) 101 enf := rbac.NewEnforcer(kubeclientset, test.FakeArgoCDNamespace, common.ArgoCDConfigMapName, nil) 102 enf.EnableLog(true) 103 _ = enf.SetBuiltinPolicy(fmt.Sprintf(`p, alice, applications, %s/*, my-proj/*, allow 104 p, bob, applications, %s/argoproj.io/Rollout/*, my-proj/*, allow 105 p, cam, applications, %s/argoproj.io/Rollout/resume, my-proj/*, allow 106 `, rbac.ActionAction, rbac.ActionAction, rbac.ActionAction)) 107 rbacEnf := NewRBACPolicyEnforcer(enf, projLister) 108 enf.SetClaimsEnforcerFunc(rbacEnf.EnforceClaims) 109 110 // Alice has wild-card approval for all actions 111 claims := jwt.MapClaims{"sub": "alice"} 112 assert.True(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/Rollout/resume", "my-proj/my-app")) 113 claims = jwt.MapClaims{"sub": "alice"} 114 assert.True(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/NewCrd/abort", "my-proj/my-app")) 115 // Bob has wild-card approval for all actions under argoproj.io/Rollout 116 claims = jwt.MapClaims{"sub": "bob"} 117 assert.True(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/Rollout/resume", "my-proj/my-app")) 118 claims = jwt.MapClaims{"sub": "bob"} 119 assert.False(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/NewCrd/abort", "my-proj/my-app")) 120 // Cam only has approval for actions/argoproj.io/Rollout:resume 121 claims = jwt.MapClaims{"sub": "cam"} 122 assert.True(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/Rollout/resume", "my-proj/my-app")) 123 claims = jwt.MapClaims{"sub": "cam"} 124 assert.False(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/Rollout/abort", "my-proj/my-app")) 125 126 // Eve does not have approval for any actions 127 claims = jwt.MapClaims{"sub": "eve"} 128 assert.False(t, enf.Enforce(claims, "applications", rbac.ActionAction+"/argoproj.io/Rollout/resume", "my-proj/my-app")) 129 } 130 131 func TestInvalidatedCache(t *testing.T) { 132 kubeclientset := fake.NewClientset(test.NewFakeConfigMap()) 133 projLister := test.NewFakeProjLister(newFakeProj()) 134 enf := rbac.NewEnforcer(kubeclientset, test.FakeArgoCDNamespace, common.ArgoCDConfigMapName, nil) 135 enf.EnableLog(true) 136 _ = enf.SetBuiltinPolicy(`p, alice, applications, create, my-proj/*, allow` + "\n" + `p, alice, logs, get, my-proj/*, allow` + "\n" + `p, alice, exec, create, my-proj/*, allow`) 137 _ = enf.SetUserPolicy(`p, bob, applications, create, my-proj/*, allow` + "\n" + `p, bob, logs, get, my-proj/*, allow` + "\n" + `p, bob, exec, create, my-proj/*, allow`) 138 rbacEnf := NewRBACPolicyEnforcer(enf, projLister) 139 enf.SetClaimsEnforcerFunc(rbacEnf.EnforceClaims) 140 141 claims := jwt.MapClaims{"sub": "alice"} 142 assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 143 assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app")) 144 assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app")) 145 146 claims = jwt.MapClaims{"sub": "bob"} 147 assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 148 assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app")) 149 assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app")) 150 151 _ = enf.SetBuiltinPolicy(`p, alice, applications, create, my-proj2/*, allow` + "\n" + `p, alice, logs, get, my-proj2/*, allow` + "\n" + `p, alice, exec, create, my-proj2/*, allow`) 152 _ = enf.SetUserPolicy(`p, bob, applications, create, my-proj2/*, allow` + "\n" + `p, bob, logs, get, my-proj2/*, allow` + "\n" + `p, bob, exec, create, my-proj2/*, allow`) 153 claims = jwt.MapClaims{"sub": "alice"} 154 assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj2/my-app")) 155 assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj2/my-app")) 156 assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj2/my-app")) 157 158 claims = jwt.MapClaims{"sub": "bob"} 159 assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj2/my-app")) 160 assert.True(t, enf.Enforce(claims, "logs", "get", "my-proj2/my-app")) 161 assert.True(t, enf.Enforce(claims, "exec", "create", "my-proj2/my-app")) 162 163 claims = jwt.MapClaims{"sub": "alice"} 164 assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 165 assert.False(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app")) 166 assert.False(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app")) 167 168 claims = jwt.MapClaims{"sub": "bob"} 169 assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app")) 170 assert.False(t, enf.Enforce(claims, "logs", "get", "my-proj/my-app")) 171 assert.False(t, enf.Enforce(claims, "exec", "create", "my-proj/my-app")) 172 } 173 174 func TestGetScopes_DefaultScopes(t *testing.T) { 175 rbacEnforcer := NewRBACPolicyEnforcer(nil, nil) 176 177 scopes := rbacEnforcer.GetScopes() 178 assert.Equal(t, scopes, rbac.DefaultScopes) 179 } 180 181 func TestGetScopes_CustomScopes(t *testing.T) { 182 rbacEnforcer := NewRBACPolicyEnforcer(nil, nil) 183 customScopes := []string{"custom"} 184 rbacEnforcer.SetScopes(customScopes) 185 186 scopes := rbacEnforcer.GetScopes() 187 assert.Equal(t, scopes, customScopes) 188 } 189 190 func Test_getProjectFromRequest(t *testing.T) { 191 tests := []struct { 192 name string 193 resource string 194 action string 195 arg string 196 }{ 197 { 198 name: "valid project/repo string", 199 resource: "repositories", 200 action: "create", 201 arg: newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps", 202 }, 203 { 204 name: "applicationsets with project/repo string", 205 resource: "applicationsets", 206 action: "create", 207 arg: newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps", 208 }, 209 { 210 name: "applicationsets with project/repo string", 211 resource: "applicationsets", 212 action: "*", 213 arg: newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps", 214 }, 215 { 216 name: "applicationsets with project/repo string", 217 resource: "applicationsets", 218 action: "get", 219 arg: newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps", 220 }, 221 } 222 223 for _, tt := range tests { 224 t.Run(tt.name, func(t *testing.T) { 225 fp := newFakeProj() 226 projLister := test.NewFakeProjLister(fp) 227 rbacEnforcer := NewRBACPolicyEnforcer(nil, projLister) 228 229 project := rbacEnforcer.getProjectFromRequest("", tt.resource, tt.action, tt.arg) 230 require.Equal(t, fp.Name, project.Name) 231 }) 232 } 233 }