github.com/argoproj/argo-cd/v2@v2.10.9/server/project/project.go (about) 1 package project 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "strings" 8 9 "github.com/argoproj/gitops-engine/pkg/utils/kube" 10 "github.com/argoproj/pkg/sync" 11 "github.com/golang-jwt/jwt/v4" 12 "github.com/google/uuid" 13 log "github.com/sirupsen/logrus" 14 "google.golang.org/grpc/codes" 15 "google.golang.org/grpc/status" 16 v1 "k8s.io/api/core/v1" 17 apierr "k8s.io/apimachinery/pkg/api/errors" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "k8s.io/apimachinery/pkg/fields" 20 "k8s.io/client-go/kubernetes" 21 "k8s.io/client-go/tools/cache" 22 "k8s.io/client-go/util/retry" 23 24 "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" 25 "github.com/argoproj/argo-cd/v2/pkg/apiclient/project" 26 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 27 appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" 28 listersv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1" 29 "github.com/argoproj/argo-cd/v2/server/deeplinks" 30 "github.com/argoproj/argo-cd/v2/server/rbacpolicy" 31 "github.com/argoproj/argo-cd/v2/util/argo" 32 "github.com/argoproj/argo-cd/v2/util/db" 33 jwtutil "github.com/argoproj/argo-cd/v2/util/jwt" 34 "github.com/argoproj/argo-cd/v2/util/rbac" 35 "github.com/argoproj/argo-cd/v2/util/session" 36 "github.com/argoproj/argo-cd/v2/util/settings" 37 ) 38 39 const ( 40 // JWTTokenSubFormat format of the JWT token subject that Argo CD vends out. 41 JWTTokenSubFormat = "proj:%s:%s" 42 ) 43 44 // Server provides a Project service 45 type Server struct { 46 ns string 47 enf *rbac.Enforcer 48 policyEnf *rbacpolicy.RBACPolicyEnforcer 49 appclientset appclientset.Interface 50 kubeclientset kubernetes.Interface 51 auditLogger *argo.AuditLogger 52 projectLock sync.KeyLock 53 sessionMgr *session.SessionManager 54 projInformer cache.SharedIndexInformer 55 settingsMgr *settings.SettingsManager 56 db db.ArgoDB 57 } 58 59 // NewServer returns a new instance of the Project service 60 func NewServer(ns string, kubeclientset kubernetes.Interface, appclientset appclientset.Interface, enf *rbac.Enforcer, projectLock sync.KeyLock, sessionMgr *session.SessionManager, policyEnf *rbacpolicy.RBACPolicyEnforcer, 61 projInformer cache.SharedIndexInformer, settingsMgr *settings.SettingsManager, db db.ArgoDB) *Server { 62 auditLogger := argo.NewAuditLogger(ns, kubeclientset, "argocd-server") 63 return &Server{enf: enf, policyEnf: policyEnf, appclientset: appclientset, kubeclientset: kubeclientset, ns: ns, projectLock: projectLock, auditLogger: auditLogger, sessionMgr: sessionMgr, 64 projInformer: projInformer, settingsMgr: settingsMgr, db: db} 65 } 66 67 func validateProject(proj *v1alpha1.AppProject) error { 68 err := proj.ValidateProject() 69 if err != nil { 70 return err 71 } 72 err = rbac.ValidatePolicy(proj.ProjectPoliciesString()) 73 if err != nil { 74 return status.Errorf(codes.InvalidArgument, "policy syntax error: %s", err.Error()) 75 } 76 return nil 77 } 78 79 // CreateToken creates a new token to access a project 80 func (s *Server) CreateToken(ctx context.Context, q *project.ProjectTokenCreateRequest) (*project.ProjectTokenResponse, error) { 81 var resp *project.ProjectTokenResponse 82 err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { 83 var createErr error 84 resp, createErr = s.createToken(ctx, q) 85 return createErr 86 }) 87 return resp, err 88 } 89 90 func (s *Server) createToken(ctx context.Context, q *project.ProjectTokenCreateRequest) (*project.ProjectTokenResponse, error) { 91 prj, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, q.Project, metav1.GetOptions{}) 92 if err != nil { 93 return nil, err 94 } 95 err = validateProject(prj) 96 if err != nil { 97 return nil, fmt.Errorf("error validating project: %w", err) 98 } 99 100 s.projectLock.Lock(q.Project) 101 defer s.projectLock.Unlock(q.Project) 102 103 role, _, err := prj.GetRoleByName(q.Role) 104 if err != nil { 105 return nil, status.Errorf(codes.NotFound, "project '%s' does not have role '%s'", q.Project, q.Role) 106 } 107 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionUpdate, q.Project); err != nil { 108 if !jwtutil.IsMember(jwtutil.Claims(ctx.Value("claims")), role.Groups, s.policyEnf.GetScopes()) { 109 return nil, err 110 } 111 } 112 id := q.Id 113 if err := prj.ValidateJWTTokenID(q.Role, q.Id); err != nil { 114 return nil, status.Errorf(codes.InvalidArgument, err.Error()) 115 } 116 if id == "" { 117 uniqueId, _ := uuid.NewRandom() 118 id = uniqueId.String() 119 } 120 subject := fmt.Sprintf(JWTTokenSubFormat, q.Project, q.Role) 121 jwtToken, err := s.sessionMgr.Create(subject, q.ExpiresIn, id) 122 if err != nil { 123 return nil, status.Error(codes.InvalidArgument, err.Error()) 124 } 125 parser := jwt.NewParser(jwt.WithoutClaimsValidation()) 126 claims := jwt.RegisteredClaims{} 127 _, _, err = parser.ParseUnverified(jwtToken, &claims) 128 if err != nil { 129 return nil, status.Error(codes.InvalidArgument, err.Error()) 130 } 131 var issuedAt, expiresAt int64 132 if claims.IssuedAt != nil { 133 issuedAt = claims.IssuedAt.Unix() 134 } 135 if claims.ExpiresAt != nil { 136 expiresAt = claims.ExpiresAt.Unix() 137 } 138 id = claims.ID 139 140 items := append(prj.Status.JWTTokensByRole[q.Role].Items, v1alpha1.JWTToken{IssuedAt: issuedAt, ExpiresAt: expiresAt, ID: id}) 141 if _, found := prj.Status.JWTTokensByRole[q.Role]; found { 142 prj.Status.JWTTokensByRole[q.Role] = v1alpha1.JWTTokens{Items: items} 143 } else { 144 tokensMap := make(map[string]v1alpha1.JWTTokens) 145 tokensMap[q.Role] = v1alpha1.JWTTokens{Items: items} 146 prj.Status.JWTTokensByRole = tokensMap 147 } 148 149 prj.NormalizeJWTTokens() 150 151 _, err = s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Update(ctx, prj, metav1.UpdateOptions{}) 152 if err != nil { 153 return nil, err 154 } 155 s.logEvent(prj, ctx, argo.EventReasonResourceCreated, "created token") 156 return &project.ProjectTokenResponse{Token: jwtToken}, nil 157 158 } 159 160 func (s *Server) ListLinks(ctx context.Context, q *project.ListProjectLinksRequest) (*application.LinksResponse, error) { 161 projName := q.GetName() 162 163 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionGet, projName); err != nil { 164 log.WithFields(map[string]interface{}{ 165 "project": projName, 166 }).Warnf("unauthorized access to project, error=%v", err.Error()) 167 return nil, fmt.Errorf("unauthorized access to project %v", projName) 168 } 169 170 proj, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, projName, metav1.GetOptions{}) 171 if err != nil { 172 return nil, err 173 } 174 175 // sanitize project jwt tokens 176 proj.Status = v1alpha1.AppProjectStatus{} 177 178 obj, err := kube.ToUnstructured(proj) 179 if err != nil { 180 return nil, fmt.Errorf("error getting application: %w", err) 181 } 182 183 deepLinks, err := s.settingsMgr.GetDeepLinks(settings.ProjectDeepLinks) 184 if err != nil { 185 return nil, fmt.Errorf("failed to read application deep links from configmap: %w", err) 186 } 187 188 deeplinksObj := deeplinks.CreateDeepLinksObject(nil, nil, nil, obj) 189 finalList, errorList := deeplinks.EvaluateDeepLinksResponse(deeplinksObj, obj.GetName(), deepLinks) 190 if len(errorList) > 0 { 191 log.Errorf("errorList while evaluating project deep links, %v", strings.Join(errorList, ", ")) 192 } 193 194 return finalList, nil 195 } 196 197 // DeleteToken deletes a token in a project 198 func (s *Server) DeleteToken(ctx context.Context, q *project.ProjectTokenDeleteRequest) (*project.EmptyResponse, error) { 199 var resp *project.EmptyResponse 200 err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { 201 var deleteErr error 202 resp, deleteErr = s.deleteToken(ctx, q) 203 return deleteErr 204 }) 205 return resp, err 206 } 207 208 func (s *Server) deleteToken(ctx context.Context, q *project.ProjectTokenDeleteRequest) (*project.EmptyResponse, error) { 209 prj, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, q.Project, metav1.GetOptions{}) 210 if err != nil { 211 return nil, err 212 } 213 err = validateProject(prj) 214 if err != nil { 215 return nil, fmt.Errorf("error validating project: %w", err) 216 } 217 218 s.projectLock.Lock(q.Project) 219 defer s.projectLock.Unlock(q.Project) 220 221 role, roleIndex, err := prj.GetRoleByName(q.Role) 222 if err != nil { 223 return &project.EmptyResponse{}, nil 224 } 225 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionUpdate, q.Project); err != nil { 226 if !jwtutil.IsMember(jwtutil.Claims(ctx.Value("claims")), role.Groups, s.policyEnf.GetScopes()) { 227 return nil, err 228 } 229 } 230 231 err = prj.RemoveJWTToken(roleIndex, q.Iat, q.Id) 232 if err != nil { 233 return &project.EmptyResponse{}, nil 234 } 235 236 _, err = s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Update(ctx, prj, metav1.UpdateOptions{}) 237 if err != nil { 238 return nil, err 239 } 240 s.logEvent(prj, ctx, argo.EventReasonResourceDeleted, "deleted token") 241 242 return &project.EmptyResponse{}, nil 243 } 244 245 // Create a new project 246 func (s *Server) Create(ctx context.Context, q *project.ProjectCreateRequest) (*v1alpha1.AppProject, error) { 247 if q.Project == nil { 248 return nil, status.Errorf(codes.InvalidArgument, "missing payload 'project' in request") 249 } 250 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionCreate, q.Project.Name); err != nil { 251 return nil, err 252 } 253 q.Project.NormalizePolicies() 254 err := validateProject(q.Project) 255 if err != nil { 256 return nil, fmt.Errorf("error validating project: %w", err) 257 } 258 res, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Create(ctx, q.Project, metav1.CreateOptions{}) 259 if apierr.IsAlreadyExists(err) { 260 existing, getErr := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, q.Project.Name, metav1.GetOptions{}) 261 if getErr != nil { 262 return nil, status.Errorf(codes.Internal, "unable to check existing project details: %v", getErr) 263 } 264 if q.GetUpsert() { 265 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionUpdate, q.GetProject().Name); err != nil { 266 return nil, err 267 } 268 existing.Spec = q.GetProject().Spec 269 res, err = s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Update(ctx, existing, metav1.UpdateOptions{}) 270 } else { 271 if !reflect.DeepEqual(existing.Spec, q.GetProject().Spec) { 272 return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("project", existing.Spec, q.GetProject().Spec)) 273 } 274 return existing, nil 275 } 276 } 277 if err == nil { 278 s.logEvent(res, ctx, argo.EventReasonResourceCreated, "created project") 279 } 280 return res, err 281 } 282 283 // List returns list of projects 284 func (s *Server) List(ctx context.Context, q *project.ProjectQuery) (*v1alpha1.AppProjectList, error) { 285 list, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).List(ctx, metav1.ListOptions{}) 286 if list != nil { 287 newItems := make([]v1alpha1.AppProject, 0) 288 for i := range list.Items { 289 project := list.Items[i] 290 if s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionGet, project.Name) { 291 newItems = append(newItems, project) 292 } 293 } 294 list.Items = newItems 295 } 296 return list, err 297 } 298 299 // GetDetailedProject returns a project with scoped resources 300 func (s *Server) GetDetailedProject(ctx context.Context, q *project.ProjectQuery) (*project.DetailedProjectsResponse, error) { 301 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionGet, q.Name); err != nil { 302 return nil, err 303 } 304 proj, repositories, clusters, err := argo.GetAppProjectWithScopedResources(q.Name, listersv1alpha1.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) 305 if err != nil { 306 return nil, err 307 } 308 proj.NormalizeJWTTokens() 309 globalProjects := argo.GetGlobalProjects(proj, listersv1alpha1.NewAppProjectLister(s.projInformer.GetIndexer()), s.settingsMgr) 310 311 return &project.DetailedProjectsResponse{ 312 GlobalProjects: globalProjects, 313 Project: proj, 314 Repositories: repositories, 315 Clusters: clusters, 316 }, err 317 } 318 319 // Get returns a project by name 320 func (s *Server) Get(ctx context.Context, q *project.ProjectQuery) (*v1alpha1.AppProject, error) { 321 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionGet, q.Name); err != nil { 322 return nil, err 323 } 324 proj, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, q.Name, metav1.GetOptions{}) 325 if err != nil { 326 return nil, err 327 } 328 proj.NormalizeJWTTokens() 329 return proj, err 330 } 331 332 // GetGlobalProjects returns global projects 333 func (s *Server) GetGlobalProjects(ctx context.Context, q *project.ProjectQuery) (*project.GlobalProjectsResponse, error) { 334 projOrig, err := s.Get(ctx, q) 335 if err != nil { 336 return nil, err 337 } 338 339 globalProjects := argo.GetGlobalProjects(projOrig, listersv1alpha1.NewAppProjectLister(s.projInformer.GetIndexer()), s.settingsMgr) 340 341 res := &project.GlobalProjectsResponse{} 342 res.Items = globalProjects 343 return res, nil 344 } 345 346 // Update updates a project 347 func (s *Server) Update(ctx context.Context, q *project.ProjectUpdateRequest) (*v1alpha1.AppProject, error) { 348 if q.Project == nil { 349 return nil, status.Errorf(codes.InvalidArgument, "missing payload 'project' in request") 350 } 351 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionUpdate, q.Project.Name); err != nil { 352 return nil, err 353 } 354 q.Project.NormalizePolicies() 355 q.Project.NormalizeJWTTokens() 356 err := validateProject(q.Project) 357 if err != nil { 358 return nil, err 359 } 360 s.projectLock.Lock(q.Project.Name) 361 defer s.projectLock.Unlock(q.Project.Name) 362 363 oldProj, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, q.Project.Name, metav1.GetOptions{}) 364 if err != nil { 365 return nil, err 366 } 367 368 for _, cluster := range difference(q.Project.Spec.DestinationClusters(), oldProj.Spec.DestinationClusters()) { 369 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, cluster); err != nil { 370 return nil, err 371 } 372 } 373 374 for _, repoUrl := range difference(q.Project.Spec.SourceRepos, oldProj.Spec.SourceRepos) { 375 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionUpdate, repoUrl); err != nil { 376 return nil, err 377 } 378 } 379 380 clusterResourceWhitelistsEqual := reflect.DeepEqual(q.Project.Spec.ClusterResourceWhitelist, oldProj.Spec.ClusterResourceWhitelist) 381 clusterResourceBlacklistsEqual := reflect.DeepEqual(q.Project.Spec.ClusterResourceBlacklist, oldProj.Spec.ClusterResourceBlacklist) 382 namespacesResourceBlacklistsEqual := reflect.DeepEqual(q.Project.Spec.NamespaceResourceBlacklist, oldProj.Spec.NamespaceResourceBlacklist) 383 namespacesResourceWhitelistsEqual := reflect.DeepEqual(q.Project.Spec.NamespaceResourceWhitelist, oldProj.Spec.NamespaceResourceWhitelist) 384 if !clusterResourceWhitelistsEqual || !clusterResourceBlacklistsEqual || !namespacesResourceBlacklistsEqual || !namespacesResourceWhitelistsEqual { 385 for _, cluster := range q.Project.Spec.DestinationClusters() { 386 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, cluster); err != nil { 387 return nil, err 388 } 389 } 390 } 391 392 appsList, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).List(ctx, metav1.ListOptions{}) 393 if err != nil { 394 return nil, err 395 } 396 397 var srcValidatedApps []v1alpha1.Application 398 var dstValidatedApps []v1alpha1.Application 399 getProjectClusters := func(project string) ([]*v1alpha1.Cluster, error) { 400 return s.db.GetProjectClusters(ctx, project) 401 } 402 403 for _, a := range argo.FilterByProjects(appsList.Items, []string{q.Project.Name}) { 404 if oldProj.IsSourcePermitted(a.Spec.GetSource()) { 405 srcValidatedApps = append(srcValidatedApps, a) 406 } 407 408 dstPermitted, err := oldProj.IsDestinationPermitted(a.Spec.Destination, getProjectClusters) 409 if err != nil { 410 return nil, err 411 } 412 413 if dstPermitted { 414 dstValidatedApps = append(dstValidatedApps, a) 415 } 416 } 417 418 invalidSrcCount := 0 419 invalidDstCount := 0 420 421 for _, a := range srcValidatedApps { 422 if !q.Project.IsSourcePermitted(a.Spec.GetSource()) { 423 invalidSrcCount++ 424 } 425 } 426 for _, a := range dstValidatedApps { 427 dstPermitted, err := q.Project.IsDestinationPermitted(a.Spec.Destination, getProjectClusters) 428 if err != nil { 429 return nil, err 430 } 431 432 if !dstPermitted { 433 invalidDstCount++ 434 } 435 } 436 437 var parts []string 438 if invalidSrcCount > 0 { 439 parts = append(parts, fmt.Sprintf("%d applications source became invalid", invalidSrcCount)) 440 } 441 if invalidDstCount > 0 { 442 parts = append(parts, fmt.Sprintf("%d applications destination became invalid", invalidDstCount)) 443 } 444 if len(parts) > 0 { 445 return nil, status.Errorf(codes.InvalidArgument, "as a result of project update %s", strings.Join(parts, " and ")) 446 } 447 448 res, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Update(ctx, q.Project, metav1.UpdateOptions{}) 449 if err == nil { 450 s.logEvent(res, ctx, argo.EventReasonResourceUpdated, "updated project") 451 } 452 return res, err 453 } 454 455 // Delete deletes a project 456 func (s *Server) Delete(ctx context.Context, q *project.ProjectQuery) (*project.EmptyResponse, error) { 457 if q.Name == v1alpha1.DefaultAppProjectName { 458 return nil, status.Errorf(codes.InvalidArgument, "name '%s' is reserved and cannot be deleted", q.Name) 459 } 460 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionDelete, q.Name); err != nil { 461 return nil, err 462 } 463 464 s.projectLock.Lock(q.Name) 465 defer s.projectLock.Unlock(q.Name) 466 467 p, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, q.Name, metav1.GetOptions{}) 468 if err != nil { 469 return nil, err 470 } 471 472 appsList, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).List(ctx, metav1.ListOptions{}) 473 if err != nil { 474 return nil, err 475 } 476 apps := argo.FilterByProjects(appsList.Items, []string{q.Name}) 477 if len(apps) > 0 { 478 return nil, status.Errorf(codes.InvalidArgument, "project is referenced by %d applications", len(apps)) 479 } 480 err = s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Delete(ctx, q.Name, metav1.DeleteOptions{}) 481 if err == nil { 482 s.logEvent(p, ctx, argo.EventReasonResourceDeleted, "deleted project") 483 } 484 return &project.EmptyResponse{}, err 485 } 486 487 func (s *Server) ListEvents(ctx context.Context, q *project.ProjectQuery) (*v1.EventList, error) { 488 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionGet, q.Name); err != nil { 489 return nil, err 490 } 491 proj, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, q.Name, metav1.GetOptions{}) 492 if err != nil { 493 return nil, err 494 } 495 fieldSelector := fields.SelectorFromSet(map[string]string{ 496 "involvedObject.name": proj.Name, 497 "involvedObject.uid": string(proj.UID), 498 "involvedObject.namespace": proj.Namespace, 499 }).String() 500 return s.kubeclientset.CoreV1().Events(s.ns).List(ctx, metav1.ListOptions{FieldSelector: fieldSelector}) 501 } 502 503 func (s *Server) logEvent(a *v1alpha1.AppProject, ctx context.Context, reason string, action string) { 504 eventInfo := argo.EventInfo{Type: v1.EventTypeNormal, Reason: reason} 505 user := session.Username(ctx) 506 if user == "" { 507 user = "Unknown user" 508 } 509 message := fmt.Sprintf("%s %s", user, action) 510 s.auditLogger.LogAppProjEvent(a, eventInfo, message, user) 511 } 512 513 func (s *Server) GetSyncWindowsState(ctx context.Context, q *project.SyncWindowsQuery) (*project.SyncWindowsResponse, error) { 514 if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceProjects, rbacpolicy.ActionGet, q.Name); err != nil { 515 return nil, err 516 } 517 proj, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(ctx, q.Name, metav1.GetOptions{}) 518 519 if err != nil { 520 return nil, err 521 } 522 523 res := &project.SyncWindowsResponse{} 524 525 windows := proj.Spec.SyncWindows.Active() 526 if windows.HasWindows() { 527 res.Windows = *windows 528 } else { 529 res.Windows = []*v1alpha1.SyncWindow{} 530 } 531 532 return res, nil 533 } 534 535 func (s *Server) NormalizeProjs() error { 536 projList, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).List(context.Background(), metav1.ListOptions{}) 537 if err != nil { 538 return status.Errorf(codes.Internal, "Error retrieving project list: %s", err.Error()) 539 } 540 for _, proj := range projList.Items { 541 for i := 0; i < 3; i++ { 542 if proj.NormalizeJWTTokens() { 543 _, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Update(context.Background(), &proj, metav1.UpdateOptions{}) 544 if err == nil { 545 log.Infof("Successfully normalized project %s.", proj.Name) 546 break 547 } 548 if !apierr.IsConflict(err) { 549 log.Warnf("Failed normalize project %s", proj.Name) 550 break 551 } 552 projGet, err := s.appclientset.ArgoprojV1alpha1().AppProjects(s.ns).Get(context.Background(), proj.Name, metav1.GetOptions{}) 553 if err != nil { 554 return status.Errorf(codes.Internal, "Error retrieving project: %s", err.Error()) 555 } 556 proj = *projGet 557 if i == 2 { 558 return status.Errorf(codes.Internal, "Failed normalize project %s", proj.Name) 559 } 560 } else { 561 break 562 } 563 } 564 } 565 return nil 566 }