diff --git a/server/application/application.go b/server/application/application.go index 645071839a928..f7d78fbf63c15 100644 --- a/server/application/application.go +++ b/server/application/application.go @@ -70,10 +70,7 @@ const ( foregroundPropagationPolicy string = "foreground" ) -var ( - watchAPIBufferSize = env.ParseNumFromEnv(argocommon.EnvWatchAPIBufferSize, 1000, 0, math.MaxInt32) - permissionDeniedErr = status.Error(codes.PermissionDenied, "permission denied") -) +var watchAPIBufferSize = env.ParseNumFromEnv(argocommon.EnvWatchAPIBufferSize, 1000, 0, math.MaxInt32) // Server provides an Application service type Server struct { @@ -174,7 +171,7 @@ func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespa // but the app is in a different project" response. We don't want the user inferring the existence of the // app from response time. _, _ = getApp() - return nil, nil, permissionDeniedErr + return nil, nil, argocommon.PermissionDeniedAPIError } } a, err := getApp() @@ -187,10 +184,10 @@ func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespa // We don't know if the user was allowed to get the Application, and we don't want to leak information about // the Application's existence. Return 403. logCtx.Warn("application does not exist") - return nil, nil, permissionDeniedErr + return nil, nil, argocommon.PermissionDeniedAPIError } logCtx.Errorf("failed to get application: %s", err) - return nil, nil, permissionDeniedErr + return nil, nil, argocommon.PermissionDeniedAPIError } // Even if we performed an initial RBAC check (because the request was fully parameterized), we still need to // perform a second RBAC check to ensure that the user has access to the actual Application's project (not just the @@ -208,7 +205,7 @@ func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespa } // The user didn't specify a project. We always return permission denied for both lack of access and lack of // existence. - return nil, nil, permissionDeniedErr + return nil, nil, argocommon.PermissionDeniedAPIError } effectiveProject := "default" if a.Spec.Project != "" { @@ -1352,7 +1349,7 @@ func (s *Server) getAppResources(ctx context.Context, a *appv1.Application) (*ap func (s *Server) getAppLiveResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) { a, _, err := s.getApplicationEnforceRBACInformer(ctx, action, q.GetProject(), q.GetAppNamespace(), q.GetName()) - if err != nil && errors.Is(err, permissionDeniedErr) && (action == rbacpolicy.ActionDelete || action == rbacpolicy.ActionUpdate) { + if err != nil && errors.Is(err, argocommon.PermissionDeniedAPIError) && (action == rbacpolicy.ActionDelete || action == rbacpolicy.ActionUpdate) { // If users dont have permission on the whole applications, maybe they have fine-grained access to the specific resources action = fmt.Sprintf("%s/%s/%s/%s/%s", action, q.GetGroup(), q.GetKind(), q.GetNamespace(), q.GetResourceName()) a, _, err = s.getApplicationEnforceRBACInformer(ctx, action, q.GetProject(), q.GetAppNamespace(), q.GetName()) diff --git a/server/application/application_test.go b/server/application/application_test.go index f796937649013..8161f39a1a7f9 100644 --- a/server/application/application_test.go +++ b/server/application/application_test.go @@ -794,10 +794,10 @@ func TestNoAppEnumeration(t *testing.T) { require.NoError(t, err) // nolint:staticcheck _, err = appServer.Get(noRoleCtx, &application.ApplicationQuery{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") // nolint:staticcheck _, err = appServer.Get(adminCtx, &application.ApplicationQuery{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") // nolint:staticcheck _, err = appServer.Get(adminCtx, &application.ApplicationQuery{Name: ptr.To("doest-not-exist"), Project: []string{"test"}}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") @@ -807,9 +807,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.GetManifests(adminCtx, &application.ApplicationManifestQuery{Name: ptr.To("test")}) require.NoError(t, err) _, err = appServer.GetManifests(noRoleCtx, &application.ApplicationManifestQuery{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.GetManifests(adminCtx, &application.ApplicationManifestQuery{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.GetManifests(adminCtx, &application.ApplicationManifestQuery{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -818,9 +818,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.ListResourceEvents(adminCtx, &application.ApplicationResourceEventsQuery{Name: ptr.To("test")}) require.NoError(t, err) _, err = appServer.ListResourceEvents(noRoleCtx, &application.ApplicationResourceEventsQuery{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ListResourceEvents(adminCtx, &application.ApplicationResourceEventsQuery{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ListResourceEvents(adminCtx, &application.ApplicationResourceEventsQuery{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -835,12 +835,12 @@ func TestNoAppEnumeration(t *testing.T) { Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.example.com"}, Source: &appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."}, }}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.UpdateSpec(adminCtx, &application.ApplicationUpdateSpecRequest{Name: ptr.To("doest-not-exist"), Spec: &appsv1.ApplicationSpec{ Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.example.com"}, Source: &appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."}, }}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.UpdateSpec(adminCtx, &application.ApplicationUpdateSpecRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test"), Spec: &appsv1.ApplicationSpec{ Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.example.com"}, Source: &appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."}, @@ -852,9 +852,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`)}) require.NoError(t, err) _, err = appServer.Patch(noRoleCtx, &application.ApplicationPatchRequest{Name: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`)}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -863,9 +863,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.GetResource(adminCtx, &application.ApplicationResourceRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) require.NoError(t, err) _, err = appServer.GetResource(noRoleCtx, &application.ApplicationResourceRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.GetResource(adminCtx, &application.ApplicationResourceRequest{Name: ptr.To("doest-not-exist"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.GetResource(adminCtx, &application.ApplicationResourceRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -874,11 +874,11 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.PatchResource(adminCtx, &application.ApplicationResourcePatchRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) // This will always throw an error, because the kubectl mock for PatchResource is hard-coded to return nil. // The best we can do is to confirm we get past the permission check. - assert.NotEqual(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + assert.NotEqual(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.PatchResource(noRoleCtx, &application.ApplicationResourcePatchRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.PatchResource(adminCtx, &application.ApplicationResourcePatchRequest{Name: ptr.To("doest-not-exist"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.PatchResource(adminCtx, &application.ApplicationResourcePatchRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Patch: ptr.To(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -887,9 +887,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.DeleteResource(adminCtx, &application.ApplicationResourceDeleteRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) require.NoError(t, err) _, err = appServer.DeleteResource(noRoleCtx, &application.ApplicationResourceDeleteRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.DeleteResource(adminCtx, &application.ApplicationResourceDeleteRequest{Name: ptr.To("doest-not-exist"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.DeleteResource(adminCtx, &application.ApplicationResourceDeleteRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -898,9 +898,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.ResourceTree(adminCtx, &application.ResourcesQuery{ApplicationName: ptr.To("test")}) require.NoError(t, err) _, err = appServer.ResourceTree(noRoleCtx, &application.ResourcesQuery{ApplicationName: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ResourceTree(adminCtx, &application.ResourcesQuery{ApplicationName: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ResourceTree(adminCtx, &application.ResourcesQuery{ApplicationName: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -911,9 +911,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err = appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: ptr.To("test-multi"), SourceIndex: ptr.To(int32(0)), VersionId: ptr.To(int32(1))}) require.NoError(t, err) _, err = appServer.RevisionMetadata(noRoleCtx, &application.RevisionMetadataQuery{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -922,9 +922,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.RevisionChartDetails(adminCtx, &application.RevisionMetadataQuery{Name: ptr.To("test-helm")}) require.NoError(t, err) _, err = appServer.RevisionChartDetails(noRoleCtx, &application.RevisionMetadataQuery{Name: ptr.To("test-helm")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.RevisionChartDetails(adminCtx, &application.RevisionMetadataQuery{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.RevisionChartDetails(adminCtx, &application.RevisionMetadataQuery{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -933,9 +933,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.ManagedResources(adminCtx, &application.ResourcesQuery{ApplicationName: ptr.To("test")}) require.NoError(t, err) _, err = appServer.ManagedResources(noRoleCtx, &application.ResourcesQuery{ApplicationName: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ManagedResources(adminCtx, &application.ResourcesQuery{ApplicationName: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ManagedResources(adminCtx, &application.ResourcesQuery{ApplicationName: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -944,9 +944,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.Sync(adminCtx, &application.ApplicationSyncRequest{Name: ptr.To("test")}) require.NoError(t, err) _, err = appServer.Sync(noRoleCtx, &application.ApplicationSyncRequest{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.Sync(adminCtx, &application.ApplicationSyncRequest{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.Sync(adminCtx, &application.ApplicationSyncRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -958,9 +958,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.TerminateOperation(adminCtx, &application.OperationTerminateRequest{Name: ptr.To("test")}) require.NoError(t, err) _, err = appServer.TerminateOperation(noRoleCtx, &application.OperationTerminateRequest{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.TerminateOperation(adminCtx, &application.OperationTerminateRequest{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.TerminateOperation(adminCtx, &application.OperationTerminateRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -972,9 +972,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err = appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: ptr.To("test-multi"), Id: ptr.To(int64(1))}) require.NoError(t, err) _, err = appServer.Rollback(noRoleCtx, &application.ApplicationRollbackRequest{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -983,11 +983,11 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.ListResourceActions(adminCtx, &application.ApplicationResourceRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) require.NoError(t, err) _, err = appServer.ListResourceActions(noRoleCtx, &application.ApplicationResourceRequest{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ListResourceActions(noRoleCtx, &application.ApplicationResourceRequest{Group: ptr.To("argoproj.io"), Kind: ptr.To("Application"), Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ListResourceActions(adminCtx, &application.ApplicationResourceRequest{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ListResourceActions(adminCtx, &application.ApplicationResourceRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -996,11 +996,11 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Action: ptr.To("restart")}) require.NoError(t, err) _, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Group: ptr.To("argoproj.io"), Kind: ptr.To("Application"), Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -1009,9 +1009,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.GetApplicationSyncWindows(adminCtx, &application.ApplicationSyncWindowsQuery{Name: ptr.To("test")}) require.NoError(t, err) _, err = appServer.GetApplicationSyncWindows(noRoleCtx, &application.ApplicationSyncWindowsQuery{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.GetApplicationSyncWindows(adminCtx, &application.ApplicationSyncWindowsQuery{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.GetApplicationSyncWindows(adminCtx, &application.ApplicationSyncWindowsQuery{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -1020,9 +1020,9 @@ func TestNoAppEnumeration(t *testing.T) { err := appServer.GetManifestsWithFiles(&TestServerStream{ctx: adminCtx, appName: "test"}) require.NoError(t, err) err = appServer.GetManifestsWithFiles(&TestServerStream{ctx: noRoleCtx, appName: "test"}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") err = appServer.GetManifestsWithFiles(&TestServerStream{ctx: adminCtx, appName: "does-not-exist"}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") err = appServer.GetManifestsWithFiles(&TestServerStream{ctx: adminCtx, appName: "does-not-exist", project: "test"}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"does-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -1031,9 +1031,9 @@ func TestNoAppEnumeration(t *testing.T) { err := appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: ptr.To("test")}, &TestResourceTreeServer{ctx: adminCtx}) require.NoError(t, err) err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: ptr.To("test")}, &TestResourceTreeServer{ctx: noRoleCtx}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: ptr.To("does-not-exist")}, &TestResourceTreeServer{ctx: adminCtx}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: ptr.To("does-not-exist"), Project: ptr.To("test")}, &TestResourceTreeServer{ctx: adminCtx}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"does-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -1042,9 +1042,9 @@ func TestNoAppEnumeration(t *testing.T) { err := appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: ptr.To("test")}, &TestPodLogsServer{ctx: adminCtx}) require.NoError(t, err) err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: ptr.To("test")}, &TestPodLogsServer{ctx: noRoleCtx}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: ptr.To("does-not-exist")}, &TestPodLogsServer{ctx: adminCtx}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: ptr.To("does-not-exist"), Project: ptr.To("test")}, &TestPodLogsServer{ctx: adminCtx}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"does-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -1053,9 +1053,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.ListLinks(adminCtx, &application.ListAppLinksRequest{Name: ptr.To("test")}) require.NoError(t, err) _, err = appServer.ListLinks(noRoleCtx, &application.ListAppLinksRequest{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ListLinks(adminCtx, &application.ListAppLinksRequest{Name: ptr.To("does-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ListLinks(adminCtx, &application.ListAppLinksRequest{Name: ptr.To("does-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"does-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -1064,9 +1064,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.ListResourceLinks(adminCtx, &application.ApplicationResourceRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) require.NoError(t, err) _, err = appServer.ListResourceLinks(noRoleCtx, &application.ApplicationResourceRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ListResourceLinks(adminCtx, &application.ApplicationResourceRequest{Name: ptr.To("does-not-exist"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.ListResourceLinks(adminCtx, &application.ApplicationResourceRequest{Name: ptr.To("does-not-exist"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"does-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) @@ -1076,9 +1076,9 @@ func TestNoAppEnumeration(t *testing.T) { _, err := appServer.Delete(adminCtx, &application.ApplicationDeleteRequest{Name: ptr.To("test")}) require.NoError(t, err) _, err = appServer.Delete(noRoleCtx, &application.ApplicationDeleteRequest{Name: ptr.To("test")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.Delete(adminCtx, &application.ApplicationDeleteRequest{Name: ptr.To("doest-not-exist")}) - require.EqualError(t, err, permissionDeniedErr.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") + require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence") _, err = appServer.Delete(adminCtx, &application.ApplicationDeleteRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")}) assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message") }) diff --git a/server/application/websocket.go b/server/application/websocket.go index 6ff646abcaa32..1bf5c8a2d96fe 100644 --- a/server/application/websocket.go +++ b/server/application/websocket.go @@ -144,7 +144,7 @@ func (t *terminalSession) validatePermissions(p []byte) (int, error) { if err != nil { log.Errorf("permission denied message err: %v", err) } - return copy(p, EndOfTransmission), permissionDeniedErr + return copy(p, EndOfTransmission), common.PermissionDeniedAPIError } if err := t.terminalOpts.Enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, t.appRBACName); err != nil { @@ -152,7 +152,7 @@ func (t *terminalSession) validatePermissions(p []byte) (int, error) { if err != nil { log.Errorf("permission denied message err: %v", err) } - return copy(p, EndOfTransmission), permissionDeniedErr + return copy(p, EndOfTransmission), common.PermissionDeniedAPIError } return 0, nil } diff --git a/server/application/websocket_test.go b/server/application/websocket_test.go index d2222e1bdeb25..2575db2cedf28 100644 --- a/server/application/websocket_test.go +++ b/server/application/websocket_test.go @@ -160,7 +160,7 @@ func TestValidateWithoutPermissions(t *testing.T) { ts.ctx = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"test"}}) _, err := ts.validatePermissions([]byte{}) require.Error(t, err) - assert.EqualError(t, err, permissionDeniedErr.Error()) + assert.EqualError(t, err, common.PermissionDeniedAPIError.Error()) } testServerConnection(t, validate, true) diff --git a/server/repository/repository.go b/server/repository/repository.go index 191b720733e0f..02f20463c0ca8 100644 --- a/server/repository/repository.go +++ b/server/repository/repository.go @@ -16,6 +16,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/cache" + "github.com/argoproj/argo-cd/v2/common" repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -70,12 +71,10 @@ func NewServer( } } -var errPermissionDenied = status.Error(codes.PermissionDenied, "permission denied") - func (s *Server) getRepo(ctx context.Context, url, project string) (*appsv1.Repository, error) { repo, err := s.db.GetRepository(ctx, url, project) if err != nil { - return nil, errPermissionDenied + return nil, common.PermissionDeniedAPIError } return repo, nil } @@ -83,7 +82,7 @@ func (s *Server) getRepo(ctx context.Context, url, project string) (*appsv1.Repo func (s *Server) getWriteRepo(ctx context.Context, url, project string) (*appsv1.Repository, error) { repo, err := s.db.GetWriteRepository(ctx, url, project) if err != nil { - return nil, errPermissionDenied + return nil, common.PermissionDeniedAPIError } return repo, nil } @@ -281,7 +280,7 @@ func (s *Server) ListApps(ctx context.Context, q *repositorypkg.RepoAppsQuery) ( appRBACresource := fmt.Sprintf("%s/%s", q.AppProject, q.AppName) if !s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, appRBACresource) && !s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, appRBACresource) { - return nil, errPermissionDenied + return nil, common.PermissionDeniedAPIError } // Also ensure the repo is actually allowed in the project in question if err := s.isRepoPermittedInProject(ctx, q.Repo, q.AppProject); err != nil { @@ -340,11 +339,11 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta } else { // if we get here we are returning repo details of an existing app if q.AppProject != app.Spec.Project { - return nil, errPermissionDenied + return nil, common.PermissionDeniedAPIError } // verify caller is not making a request with arbitrary source values which were not in our history if !isSourceInHistory(app, *q.Source, q.SourceIndex, q.VersionId) { - return nil, errPermissionDenied + return nil, common.PermissionDeniedAPIError } } // Ensure the repo is actually allowed in the project in question @@ -637,7 +636,7 @@ func getRepository(ctx context.Context, listRepositories func(context.Context, * } if len(foundRepos) == 0 { - return nil, errPermissionDenied + return nil, common.PermissionDeniedAPIError } var foundRepo *v1alpha1.Repository diff --git a/server/repository/repository_test.go b/server/repository/repository_test.go index 7b4b77bca2ab1..7dddb79e6e5ba 100644 --- a/server/repository/repository_test.go +++ b/server/repository/repository_test.go @@ -348,7 +348,7 @@ func TestRepositoryServer(t *testing.T) { Repo: url, }) assert.Nil(t, repo) - assert.Equal(t, err, errPermissionDenied) + assert.Equal(t, err, common.PermissionDeniedAPIError) }) t.Run("Test_GetWithNotExistRepoShouldReturn404", func(t *testing.T) { @@ -529,7 +529,7 @@ func TestRepositoryServerListApps(t *testing.T) { AppProject: "default", }) assert.Nil(t, resp) - assert.Equal(t, err, errPermissionDenied) + assert.Equal(t, err, common.PermissionDeniedAPIError) }) t.Run("Test_WithAppCreateUpdatePrivileges", func(t *testing.T) { @@ -834,7 +834,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { AppName: "guestbook", AppProject: "mismatch", }) - assert.Equal(t, errPermissionDenied, err) + assert.Equal(t, err, common.PermissionDeniedAPIError) assert.Nil(t, resp) }) t.Run("Test_ExistingAppSourceNotInHistory", func(t *testing.T) { @@ -855,7 +855,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { AppName: "guestbook", AppProject: "default", }) - assert.Equal(t, errPermissionDenied, err) + assert.Equal(t, err, common.PermissionDeniedAPIError) assert.Nil(t, resp) }) t.Run("Test_ExistingAppSourceInHistory", func(t *testing.T) { @@ -912,7 +912,7 @@ func TestRepositoryServerGetAppDetails(t *testing.T) { SourceIndex: 0, VersionId: 1, }) - assert.Equal(t, errPermissionDenied, err) + assert.Equal(t, err, common.PermissionDeniedAPIError) assert.Nil(t, resp) }) t.Run("Test_ExistingAppMultiSourceInHistory", func(t *testing.T) { @@ -997,7 +997,7 @@ func TestGetRepository(t *testing.T) { q: &repository.RepoQuery{}, }, want: nil, - error: status.Error(codes.PermissionDenied, "permission denied"), + error: common.PermissionDeniedAPIError, }, { name: "empty project and no matching repos", @@ -1011,7 +1011,7 @@ func TestGetRepository(t *testing.T) { }, }, want: nil, - error: status.Error(codes.PermissionDenied, "permission denied"), + error: common.PermissionDeniedAPIError, }, { name: "empty project + matching repo with an empty project",