github.com/argoproj/argo-cd/v2@v2.10.9/server/project/project_test.go (about) 1 package project 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "testing" 8 9 "github.com/argoproj/argo-cd/v2/util/db" 10 11 "github.com/argoproj/pkg/sync" 12 "github.com/golang-jwt/jwt/v4" 13 "github.com/google/uuid" 14 "github.com/stretchr/testify/assert" 15 "google.golang.org/grpc/codes" 16 "google.golang.org/grpc/status" 17 corev1 "k8s.io/api/core/v1" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 "k8s.io/client-go/kubernetes/fake" 21 k8scache "k8s.io/client-go/tools/cache" 22 23 "github.com/argoproj/argo-cd/v2/common" 24 "github.com/argoproj/argo-cd/v2/pkg/apiclient/project" 25 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 26 apps "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake" 27 informer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions" 28 "github.com/argoproj/argo-cd/v2/server/rbacpolicy" 29 "github.com/argoproj/argo-cd/v2/test" 30 "github.com/argoproj/argo-cd/v2/util/assets" 31 jwtutil "github.com/argoproj/argo-cd/v2/util/jwt" 32 "github.com/argoproj/argo-cd/v2/util/rbac" 33 "github.com/argoproj/argo-cd/v2/util/session" 34 "github.com/argoproj/argo-cd/v2/util/settings" 35 ) 36 37 const testNamespace = "default" 38 39 func TestProjectServer(t *testing.T) { 40 kubeclientset := fake.NewSimpleClientset(&corev1.ConfigMap{ 41 ObjectMeta: v1.ObjectMeta{ 42 Namespace: testNamespace, 43 Name: "argocd-cm", 44 Labels: map[string]string{ 45 "app.kubernetes.io/part-of": "argocd", 46 }, 47 }, 48 }, &corev1.Secret{ 49 ObjectMeta: v1.ObjectMeta{ 50 Name: "argocd-secret", 51 Namespace: testNamespace, 52 }, 53 Data: map[string][]byte{ 54 "admin.password": []byte("test"), 55 "server.secretkey": []byte("test"), 56 }, 57 }) 58 settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace) 59 enforcer := newEnforcer(kubeclientset) 60 existingProj := v1alpha1.AppProject{ 61 ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: testNamespace}, 62 Spec: v1alpha1.AppProjectSpec{ 63 Destinations: []v1alpha1.ApplicationDestination{ 64 {Namespace: "ns1", Server: "https://server1"}, 65 {Namespace: "ns2", Server: "https://server2"}, 66 }, 67 SourceRepos: []string{"https://github.com/argoproj/argo-cd.git"}, 68 }, 69 } 70 existingApp := v1alpha1.Application{ 71 ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"}, 72 Spec: v1alpha1.ApplicationSpec{Source: &v1alpha1.ApplicationSource{}, Project: "test", Destination: v1alpha1.ApplicationDestination{Namespace: "ns3", Server: "https://server3"}}, 73 } 74 75 policyTemplate := "p, proj:%s:%s, applications, %s, %s/%s, %s" 76 77 ctx := context.Background() 78 fakeAppsClientset := apps.NewSimpleClientset() 79 factory := informer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, informer.WithNamespace(""), informer.WithTweakListOptions(func(options *metav1.ListOptions) {})) 80 projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer() 81 go projInformer.Run(ctx.Done()) 82 if !k8scache.WaitForCacheSync(ctx.Done(), projInformer.HasSynced) { 83 panic("Timed out waiting forfff caches to sync") 84 } 85 86 t.Run("TestNormalizeProj", func(t *testing.T) { 87 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 88 projectWithRole := existingProj.DeepCopy() 89 roleName := "roleName" 90 role1 := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} 91 projectWithRole.Spec.Roles = append(projectWithRole.Spec.Roles, role1) 92 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 93 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB) 94 err := projectServer.NormalizeProjs() 95 assert.NoError(t, err) 96 97 appList, err := projectServer.appclientset.ArgoprojV1alpha1().AppProjects(projectWithRole.Namespace).List(context.Background(), v1.ListOptions{}) 98 assert.NoError(t, err) 99 assert.Equal(t, appList.Items[0].Status.JWTTokensByRole[roleName].Items[0].IssuedAt, int64(1)) 100 assert.ElementsMatch(t, appList.Items[0].Status.JWTTokensByRole[roleName].Items, appList.Items[0].Spec.Roles[0].JWTTokens) 101 102 }) 103 104 t.Run("TestClusterUpdateDenied", func(t *testing.T) { 105 106 enforcer.SetDefaultRole("role:projects") 107 _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") 108 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 109 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 110 111 updatedProj := existingProj.DeepCopy() 112 updatedProj.Spec.Destinations = nil 113 114 _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) 115 116 assert.Equal(t, status.Error(codes.PermissionDenied, "permission denied: clusters, update, https://server1"), err) 117 }) 118 119 t.Run("TestReposUpdateDenied", func(t *testing.T) { 120 121 enforcer.SetDefaultRole("role:projects") 122 _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") 123 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 124 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 125 126 updatedProj := existingProj.DeepCopy() 127 updatedProj.Spec.SourceRepos = nil 128 129 _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) 130 131 assert.Equal(t, status.Error(codes.PermissionDenied, "permission denied: repositories, update, https://github.com/argoproj/argo-cd.git"), err) 132 }) 133 134 t.Run("TestClusterResourceWhitelistUpdateDenied", func(t *testing.T) { 135 136 enforcer.SetDefaultRole("role:projects") 137 _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") 138 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 139 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 140 141 updatedProj := existingProj.DeepCopy() 142 updatedProj.Spec.ClusterResourceWhitelist = []metav1.GroupKind{{}} 143 144 _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) 145 146 assert.Equal(t, status.Error(codes.PermissionDenied, "permission denied: clusters, update, https://server1"), err) 147 }) 148 149 t.Run("TestNamespaceResourceBlacklistUpdateDenied", func(t *testing.T) { 150 151 enforcer.SetDefaultRole("role:projects") 152 _ = enforcer.SetBuiltinPolicy("p, role:projects, projects, update, *, allow") 153 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 154 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 155 156 updatedProj := existingProj.DeepCopy() 157 updatedProj.Spec.NamespaceResourceBlacklist = []metav1.GroupKind{{}} 158 159 _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) 160 161 assert.Equal(t, status.Error(codes.PermissionDenied, "permission denied: clusters, update, https://server1"), err) 162 }) 163 164 enforcer = newEnforcer(kubeclientset) 165 166 t.Run("TestRemoveDestinationSuccessful", func(t *testing.T) { 167 existingApp := v1alpha1.Application{ 168 ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"}, 169 Spec: v1alpha1.ApplicationSpec{Source: &v1alpha1.ApplicationSource{}, Project: "test", Destination: v1alpha1.ApplicationDestination{Namespace: "ns3", Server: "https://server3"}}, 170 } 171 172 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 173 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 174 175 updatedProj := existingProj.DeepCopy() 176 updatedProj.Spec.Destinations = updatedProj.Spec.Destinations[1:] 177 178 _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) 179 180 assert.Nil(t, err) 181 }) 182 183 t.Run("TestRemoveDestinationUsedByApp", func(t *testing.T) { 184 existingApp := v1alpha1.Application{ 185 ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"}, 186 Spec: v1alpha1.ApplicationSpec{Source: &v1alpha1.ApplicationSource{}, Project: "test", Destination: v1alpha1.ApplicationDestination{Namespace: "ns1", Server: "https://server1"}}, 187 } 188 189 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 190 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 191 192 updatedProj := existingProj.DeepCopy() 193 updatedProj.Spec.Destinations = updatedProj.Spec.Destinations[1:] 194 195 _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) 196 197 assert.NotNil(t, err) 198 statusCode, _ := status.FromError(err) 199 assert.Equal(t, codes.InvalidArgument, statusCode.Code()) 200 }) 201 202 t.Run("TestRemoveSourceSuccessful", func(t *testing.T) { 203 existingApp := v1alpha1.Application{ 204 ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"}, 205 Spec: v1alpha1.ApplicationSpec{Source: &v1alpha1.ApplicationSource{}, Project: "test"}, 206 } 207 208 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 209 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 210 211 updatedProj := existingProj.DeepCopy() 212 updatedProj.Spec.SourceRepos = []string{} 213 214 _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) 215 216 assert.Nil(t, err) 217 }) 218 219 t.Run("TestRemoveSourceUsedByApp", func(t *testing.T) { 220 existingApp := v1alpha1.Application{ 221 ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"}, 222 Spec: v1alpha1.ApplicationSpec{Project: "test", Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argo-cd.git"}}, 223 } 224 225 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 226 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 227 228 updatedProj := existingProj.DeepCopy() 229 updatedProj.Spec.SourceRepos = []string{} 230 231 _, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) 232 233 assert.NotNil(t, err) 234 statusCode, _ := status.FromError(err) 235 assert.Equal(t, codes.InvalidArgument, statusCode.Code()) 236 }) 237 238 t.Run("TestRemoveSourceUsedByAppSuccessfulIfPermittedByAnotherSrc", func(t *testing.T) { 239 proj := existingProj.DeepCopy() 240 proj.Spec.SourceRepos = []string{"https://github.com/argoproj/argo-cd.git", "https://github.com/argoproj/*"} 241 existingApp := v1alpha1.Application{ 242 ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"}, 243 Spec: v1alpha1.ApplicationSpec{Project: "test", Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argo-cd.git"}}, 244 } 245 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 246 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 247 248 updatedProj := proj.DeepCopy() 249 updatedProj.Spec.SourceRepos = []string{"https://github.com/argoproj/*"} 250 251 res, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) 252 253 assert.NoError(t, err) 254 assert.ElementsMatch(t, res.Spec.SourceRepos, updatedProj.Spec.SourceRepos) 255 }) 256 257 t.Run("TestRemoveDestinationUsedByAppSuccessfulIfPermittedByAnotherDestination", func(t *testing.T) { 258 proj := existingProj.DeepCopy() 259 proj.Spec.Destinations = []v1alpha1.ApplicationDestination{ 260 {Namespace: "org1-team1", Server: "https://server1"}, 261 {Namespace: "org1-*", Server: "https://server1"}, 262 } 263 existingApp := v1alpha1.Application{ 264 ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"}, 265 Spec: v1alpha1.ApplicationSpec{Source: &v1alpha1.ApplicationSource{}, Project: "test", Destination: v1alpha1.ApplicationDestination{ 266 Server: "https://server1", 267 Namespace: "org1-team1", 268 }}, 269 } 270 271 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 272 273 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 274 275 updatedProj := proj.DeepCopy() 276 updatedProj.Spec.Destinations = []v1alpha1.ApplicationDestination{ 277 {Namespace: "org1-*", Server: "https://server1"}, 278 } 279 280 res, err := projectServer.Update(context.Background(), &project.ProjectUpdateRequest{Project: updatedProj}) 281 282 assert.NoError(t, err) 283 assert.ElementsMatch(t, res.Spec.Destinations, updatedProj.Spec.Destinations) 284 }) 285 286 t.Run("TestDeleteProjectSuccessful", func(t *testing.T) { 287 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 288 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 289 290 _, err := projectServer.Delete(context.Background(), &project.ProjectQuery{Name: "test"}) 291 292 assert.Nil(t, err) 293 }) 294 295 t.Run("TestDeleteDefaultProjectFailure", func(t *testing.T) { 296 defaultProj := v1alpha1.AppProject{ 297 ObjectMeta: v1.ObjectMeta{Name: "default", Namespace: "default"}, 298 Spec: v1alpha1.AppProjectSpec{}, 299 } 300 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 301 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&defaultProj), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 302 303 _, err := projectServer.Delete(context.Background(), &project.ProjectQuery{Name: defaultProj.Name}) 304 statusCode, _ := status.FromError(err) 305 assert.Equal(t, codes.InvalidArgument, statusCode.Code()) 306 }) 307 308 t.Run("TestDeleteProjectReferencedByApp", func(t *testing.T) { 309 existingApp := v1alpha1.Application{ 310 ObjectMeta: v1.ObjectMeta{Name: "test", Namespace: "default"}, 311 Spec: v1alpha1.ApplicationSpec{Project: "test"}, 312 } 313 314 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 315 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(&existingProj, &existingApp), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 316 317 _, err := projectServer.Delete(context.Background(), &project.ProjectQuery{Name: "test"}) 318 319 assert.NotNil(t, err) 320 statusCode, _ := status.FromError(err) 321 assert.Equal(t, codes.InvalidArgument, statusCode.Code()) 322 }) 323 324 // configure a user named "admin" which is denied by default 325 enforcer = newEnforcer(kubeclientset) 326 _ = enforcer.SetBuiltinPolicy(`p, *, *, *, *, deny`) 327 enforcer.SetClaimsEnforcerFunc(nil) 328 // nolint:staticcheck 329 ctx = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"my-group"}}) 330 policyEnf := rbacpolicy.NewRBACPolicyEnforcer(enforcer, nil) 331 policyEnf.SetScopes([]string{"groups"}) 332 333 tokenName := "testToken" 334 id := "testId" 335 336 t.Run("TestCreateTokenDenied", func(t *testing.T) { 337 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 338 projectWithRole := existingProj.DeepCopy() 339 projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}} 340 341 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 342 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) 343 _, err := projectServer.CreateToken(ctx, &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1}) 344 assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = permission denied: projects, update, test") 345 }) 346 347 t.Run("TestCreateTokenSuccessfullyUsingGroup", func(t *testing.T) { 348 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 349 projectWithRole := existingProj.DeepCopy() 350 projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName, Groups: []string{"my-group"}}} 351 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 352 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) 353 _, err := projectServer.CreateToken(ctx, &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1}) 354 assert.NoError(t, err) 355 }) 356 357 _ = enforcer.SetBuiltinPolicy(`p, role:admin, projects, update, *, allow`) 358 359 t.Run("TestCreateTokenSuccessfully", func(t *testing.T) { 360 projectWithRole := existingProj.DeepCopy() 361 projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}} 362 clientset := apps.NewSimpleClientset(projectWithRole) 363 364 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", nil, session.NewUserStateStorage(nil)) 365 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 366 projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) 367 tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 100}) 368 assert.NoError(t, err) 369 claims, _, err := sessionMgr.Parse(tokenResponse.Token) 370 assert.NoError(t, err) 371 372 mapClaims, err := jwtutil.MapClaims(claims) 373 subject, ok := mapClaims["sub"].(string) 374 assert.True(t, ok) 375 expectedSubject := fmt.Sprintf(JWTTokenSubFormat, projectWithRole.Name, tokenName) 376 assert.Equal(t, expectedSubject, subject) 377 assert.NoError(t, err) 378 }) 379 380 t.Run("TestCreateTokenWithIDSuccessfully", func(t *testing.T) { 381 projectWithRole := existingProj.DeepCopy() 382 projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}} 383 clientset := apps.NewSimpleClientset(projectWithRole) 384 385 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", nil, session.NewUserStateStorage(nil)) 386 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 387 projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) 388 tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id}) 389 assert.NoError(t, err) 390 claims, _, err := sessionMgr.Parse(tokenResponse.Token) 391 assert.NoError(t, err) 392 393 mapClaims, err := jwtutil.MapClaims(claims) 394 subject, ok := mapClaims["sub"].(string) 395 assert.True(t, ok) 396 expectedSubject := fmt.Sprintf(JWTTokenSubFormat, projectWithRole.Name, tokenName) 397 assert.Equal(t, expectedSubject, subject) 398 assert.NoError(t, err) 399 }) 400 401 t.Run("TestCreateTokenWithSameIdDeny", func(t *testing.T) { 402 projectWithRole := existingProj.DeepCopy() 403 projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}} 404 clientset := apps.NewSimpleClientset(projectWithRole) 405 406 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", nil, session.NewUserStateStorage(nil)) 407 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 408 projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) 409 tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id}) 410 411 assert.NoError(t, err) 412 claims, _, err := sessionMgr.Parse(tokenResponse.Token) 413 assert.NoError(t, err) 414 415 mapClaims, err := jwtutil.MapClaims(claims) 416 subject, ok := mapClaims["sub"].(string) 417 assert.True(t, ok) 418 expectedSubject := fmt.Sprintf(JWTTokenSubFormat, projectWithRole.Name, tokenName) 419 assert.Equal(t, expectedSubject, subject) 420 assert.NoError(t, err) 421 422 _, err1 := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id}) 423 expectedErr := fmt.Sprintf("rpc error: code = InvalidArgument desc = rpc error: code = InvalidArgument desc = Token id '%s' has been used. ", id) 424 assert.EqualError(t, err1, expectedErr) 425 }) 426 427 _ = enforcer.SetBuiltinPolicy(`p, *, *, *, *, deny`) 428 429 t.Run("TestDeleteTokenDenied", func(t *testing.T) { 430 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 431 projWithToken := existingProj.DeepCopy() 432 issuedAt := int64(1) 433 secondIssuedAt := issuedAt + 1 434 token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}} 435 projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) 436 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 437 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) 438 _, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: issuedAt}) 439 assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = permission denied: projects, update, test") 440 }) 441 442 t.Run("TestDeleteTokenSuccessfullyWithGroup", func(t *testing.T) { 443 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 444 projWithToken := existingProj.DeepCopy() 445 issuedAt := int64(1) 446 secondIssuedAt := issuedAt + 1 447 token := v1alpha1.ProjectRole{Name: tokenName, Groups: []string{"my-group"}, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}} 448 projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) 449 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 450 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) 451 _, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: issuedAt}) 452 assert.NoError(t, err) 453 }) 454 455 _ = enforcer.SetBuiltinPolicy(`p, role:admin, projects, get, *, allow 456 p, role:admin, projects, update, *, allow`) 457 458 t.Run("TestDeleteTokenSuccessfully", func(t *testing.T) { 459 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 460 projWithToken := existingProj.DeepCopy() 461 issuedAt := int64(1) 462 secondIssuedAt := issuedAt + 1 463 token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt}, {IssuedAt: secondIssuedAt}}} 464 projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) 465 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 466 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) 467 _, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: issuedAt}) 468 assert.NoError(t, err) 469 projWithoutToken, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name}) 470 assert.NoError(t, err) 471 assert.Len(t, projWithoutToken.Spec.Roles, 1) 472 assert.Len(t, projWithoutToken.Spec.Roles[0].JWTTokens, 1) 473 assert.Equal(t, projWithoutToken.Spec.Roles[0].JWTTokens[0].IssuedAt, secondIssuedAt) 474 }) 475 476 _ = enforcer.SetBuiltinPolicy(`p, role:admin, projects, get, *, allow 477 p, role:admin, projects, update, *, allow`) 478 479 t.Run("TestDeleteTokenByIdSuccessfully", func(t *testing.T) { 480 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 481 projWithToken := existingProj.DeepCopy() 482 issuedAt := int64(1) 483 secondIssuedAt := issuedAt + 1 484 id := "testId" 485 uniqueId, _ := uuid.NewRandom() 486 secondId := uniqueId.String() 487 token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: issuedAt, ID: id}, {IssuedAt: secondIssuedAt, ID: secondId}}} 488 projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) 489 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 490 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) 491 _, err := projectServer.DeleteToken(ctx, &project.ProjectTokenDeleteRequest{Project: projWithToken.Name, Role: tokenName, Iat: secondIssuedAt, Id: id}) 492 assert.NoError(t, err) 493 projWithoutToken, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name}) 494 assert.NoError(t, err) 495 assert.Len(t, projWithoutToken.Spec.Roles, 1) 496 assert.Len(t, projWithoutToken.Spec.Roles[0].JWTTokens, 1) 497 assert.Equal(t, projWithoutToken.Spec.Roles[0].JWTTokens[0].IssuedAt, secondIssuedAt) 498 }) 499 500 enforcer = newEnforcer(kubeclientset) 501 502 t.Run("TestCreateTwoTokensInRoleSuccess", func(t *testing.T) { 503 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 504 projWithToken := existingProj.DeepCopy() 505 tokenName := "testToken" 506 token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} 507 projWithToken.Spec.Roles = append(projWithToken.Spec.Roles, token) 508 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 509 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithToken), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr, argoDB) 510 _, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projWithToken.Name, Role: tokenName}) 511 assert.Nil(t, err) 512 projWithTwoTokens, err := projectServer.Get(context.Background(), &project.ProjectQuery{Name: projWithToken.Name}) 513 assert.Nil(t, err) 514 assert.Len(t, projWithTwoTokens.Spec.Roles, 1) 515 assert.Len(t, projWithTwoTokens.Spec.Roles[0].JWTTokens, 2) 516 }) 517 518 t.Run("TestAddWildcardSource", func(t *testing.T) { 519 520 proj := existingProj.DeepCopy() 521 wildSourceRepo := "*" 522 proj.Spec.SourceRepos = append(proj.Spec.SourceRepos, wildSourceRepo) 523 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 524 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(proj), enforcer, sync.NewKeyLock(), nil, policyEnf, projInformer, settingsMgr, argoDB) 525 request := &project.ProjectUpdateRequest{Project: proj} 526 updatedProj, err := projectServer.Update(context.Background(), request) 527 assert.Nil(t, err) 528 assert.Equal(t, wildSourceRepo, updatedProj.Spec.SourceRepos[1]) 529 }) 530 531 t.Run("TestCreateRolePolicySuccessfully", func(t *testing.T) { 532 action := "create" 533 object := "testApplication" 534 roleName := "testRole" 535 effect := "allow" 536 537 projWithRole := existingProj.DeepCopy() 538 role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} 539 policy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect) 540 role.Policies = append(role.Policies, policy) 541 projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) 542 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 543 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, policyEnf, projInformer, settingsMgr, argoDB) 544 request := &project.ProjectUpdateRequest{Project: projWithRole} 545 _, err := projectServer.Update(context.Background(), request) 546 assert.Nil(t, err) 547 t.Log(projWithRole.Spec.Roles[0].Policies[0]) 548 expectedPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, role.Name, action, projWithRole.Name, object, effect) 549 assert.Equal(t, projWithRole.Spec.Roles[0].Policies[0], expectedPolicy) 550 }) 551 552 t.Run("TestValidatePolicyDuplicatePolicyFailure", func(t *testing.T) { 553 action := "create" 554 object := "testApplication" 555 roleName := "testRole" 556 effect := "allow" 557 558 projWithRole := existingProj.DeepCopy() 559 role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} 560 policy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect) 561 role.Policies = append(role.Policies, policy) 562 role.Policies = append(role.Policies, policy) 563 projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) 564 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 565 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 566 request := &project.ProjectUpdateRequest{Project: projWithRole} 567 _, err := projectServer.Update(context.Background(), request) 568 expectedErr := fmt.Sprintf("rpc error: code = AlreadyExists desc = policy '%s' already exists for role '%s'", policy, roleName) 569 assert.EqualError(t, err, expectedErr) 570 }) 571 572 t.Run("TestValidateProjectAccessToSeparateProjectObjectFailure", func(t *testing.T) { 573 action := "create" 574 object := "testApplication" 575 roleName := "testRole" 576 otherProject := "other-project" 577 effect := "allow" 578 579 projWithRole := existingProj.DeepCopy() 580 role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} 581 policy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, otherProject, object, effect) 582 role.Policies = append(role.Policies, policy) 583 projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) 584 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 585 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 586 request := &project.ProjectUpdateRequest{Project: projWithRole} 587 _, err := projectServer.Update(context.Background(), request) 588 assert.Contains(t, err.Error(), "object must be of form 'test/*' or 'test/<APPNAME>'") 589 }) 590 591 t.Run("TestValidateProjectIncorrectProjectInRoleFailure", func(t *testing.T) { 592 action := "create" 593 object := "testApplication" 594 roleName := "testRole" 595 otherProject := "other-project" 596 effect := "allow" 597 598 projWithRole := existingProj.DeepCopy() 599 role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} 600 invalidPolicy := fmt.Sprintf(policyTemplate, otherProject, roleName, action, projWithRole.Name, object, effect) 601 role.Policies = append(role.Policies, invalidPolicy) 602 projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) 603 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 604 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 605 request := &project.ProjectUpdateRequest{Project: projWithRole} 606 _, err := projectServer.Update(context.Background(), request) 607 assert.Contains(t, err.Error(), "policy subject must be: 'proj:test:testRole'") 608 }) 609 610 t.Run("TestValidateProjectIncorrectTokenInRoleFailure", func(t *testing.T) { 611 action := "create" 612 object := "testApplication" 613 roleName := "testRole" 614 otherToken := "other-token" 615 effect := "allow" 616 617 projWithRole := existingProj.DeepCopy() 618 role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} 619 invalidPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, otherToken, action, projWithRole.Name, object, effect) 620 role.Policies = append(role.Policies, invalidPolicy) 621 projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) 622 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 623 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 624 request := &project.ProjectUpdateRequest{Project: projWithRole} 625 _, err := projectServer.Update(context.Background(), request) 626 assert.Contains(t, err.Error(), "policy subject must be: 'proj:test:testRole'") 627 }) 628 629 t.Run("TestValidateProjectInvalidEffectFailure", func(t *testing.T) { 630 action := "create" 631 object := "testApplication" 632 roleName := "testRole" 633 effect := "testEffect" 634 635 projWithRole := existingProj.DeepCopy() 636 role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} 637 invalidPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect) 638 role.Policies = append(role.Policies, invalidPolicy) 639 projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) 640 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 641 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 642 request := &project.ProjectUpdateRequest{Project: projWithRole} 643 _, err := projectServer.Update(context.Background(), request) 644 assert.Contains(t, err.Error(), "effect must be: 'allow' or 'deny'") 645 }) 646 647 t.Run("TestNormalizeProjectRolePolicies", func(t *testing.T) { 648 action := "create" 649 object := "testApplication" 650 roleName := "testRole" 651 effect := "allow" 652 653 projWithRole := existingProj.DeepCopy() 654 role := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}} 655 noSpacesPolicyTemplate := strings.Replace(policyTemplate, " ", "", -1) 656 invalidPolicy := fmt.Sprintf(noSpacesPolicyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect) 657 role.Policies = append(role.Policies, invalidPolicy) 658 projWithRole.Spec.Roles = append(projWithRole.Spec.Roles, role) 659 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 660 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projWithRole), enforcer, sync.NewKeyLock(), nil, nil, projInformer, settingsMgr, argoDB) 661 request := &project.ProjectUpdateRequest{Project: projWithRole} 662 updateProj, err := projectServer.Update(context.Background(), request) 663 assert.Nil(t, err) 664 expectedPolicy := fmt.Sprintf(policyTemplate, projWithRole.Name, roleName, action, projWithRole.Name, object, effect) 665 assert.Equal(t, expectedPolicy, updateProj.Spec.Roles[0].Policies[0]) 666 }) 667 668 t.Run("TestSyncWindowsActive", func(t *testing.T) { 669 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 670 projectWithSyncWindows := existingProj.DeepCopy() 671 projectWithSyncWindows.Spec.SyncWindows = v1alpha1.SyncWindows{} 672 win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"} 673 projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win) 674 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 675 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB) 676 res, err := projectServer.GetSyncWindowsState(ctx, &project.SyncWindowsQuery{Name: projectWithSyncWindows.Name}) 677 assert.NoError(t, err) 678 assert.Equal(t, 1, len(res.Windows)) 679 }) 680 681 t.Run("TestGetSyncWindowsStateCannotGetProjectDetails", func(t *testing.T) { 682 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 683 projectWithSyncWindows := existingProj.DeepCopy() 684 projectWithSyncWindows.Spec.SyncWindows = v1alpha1.SyncWindows{} 685 win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"} 686 projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win) 687 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 688 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB) 689 res, err := projectServer.GetSyncWindowsState(ctx, &project.SyncWindowsQuery{Name: "incorrect"}) 690 assert.Contains(t, err.Error(), "not found") 691 assert.Nil(t, res) 692 }) 693 694 t.Run("TestGetSyncWindowsStateDenied", func(t *testing.T) { 695 enforcer = newEnforcer(kubeclientset) 696 _ = enforcer.SetBuiltinPolicy(`p, *, *, *, *, deny`) 697 enforcer.SetClaimsEnforcerFunc(nil) 698 // nolint:staticcheck 699 ctx := context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"my-group"}}) 700 701 sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil)) 702 projectWithSyncWindows := existingProj.DeepCopy() 703 win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"} 704 projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win) 705 argoDB := db.NewDB("default", settingsMgr, kubeclientset) 706 projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithSyncWindows), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB) 707 _, err := projectServer.GetSyncWindowsState(ctx, &project.SyncWindowsQuery{Name: projectWithSyncWindows.Name}) 708 assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = permission denied: projects, get, test") 709 }) 710 711 } 712 713 func newEnforcer(kubeclientset *fake.Clientset) *rbac.Enforcer { 714 enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil) 715 _ = enforcer.SetBuiltinPolicy(assets.BuiltinPolicyCSV) 716 enforcer.SetDefaultRole("role:admin") 717 enforcer.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool { 718 return true 719 }) 720 return enforcer 721 }