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