diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index b06d1260b..08d04cb70 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -90,13 +90,13 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro executionStore := database.ProvideExecutionStore(db) pipelineStore := database.ProvidePipelineStore(db) executionController := execution.ProvideController(db, authorizer, executionStore, pipelineStore, spaceStore) - spaceController := space.ProvideController(db, provider, pathUID, authorizer, pathStore, pipelineStore, spaceStore, repoStore, principalStore, repoController, membershipStore) - pipelineController := pipeline.ProvideController(db, pathUID, pathStore, repoStore, authorizer, pipelineStore, spaceStore) encrypter, err := database.ProvideEncryptor(databaseConfig) if err != nil { return nil, err } secretStore := database.ProvideSecretStore(encrypter, db) + spaceController := space.ProvideController(db, provider, pathUID, authorizer, pathStore, pipelineStore, secretStore, spaceStore, repoStore, principalStore, repoController, membershipStore) + pipelineController := pipeline.ProvideController(db, pathUID, pathStore, repoStore, authorizer, pipelineStore, spaceStore) secretController := secret.ProvideController(db, pathUID, pathStore, secretStore, authorizer, spaceStore) pullReqStore := database.ProvidePullReqStore(db, principalInfoCache) pullReqActivityStore := database.ProvidePullReqActivityStore(db, principalInfoCache) diff --git a/internal/api/controller/space/controller.go b/internal/api/controller/space/controller.go index 5e7be791b..3249c82e1 100644 --- a/internal/api/controller/space/controller.go +++ b/internal/api/controller/space/controller.go @@ -21,6 +21,7 @@ type Controller struct { authorizer authz.Authorizer pathStore store.PathStore pipelineStore store.PipelineStore + secretStore store.SecretStore spaceStore store.SpaceStore repoStore store.RepoStore principalStore store.PrincipalStore @@ -30,9 +31,9 @@ type Controller struct { func NewController(db *sqlx.DB, urlProvider *url.Provider, uidCheck check.PathUID, authorizer authz.Authorizer, - pathStore store.PathStore, pipelineStore store.PipelineStore, spaceStore store.SpaceStore, - repoStore store.RepoStore, principalStore store.PrincipalStore, repoCtrl *repo.Controller, - membershipStore store.MembershipStore, + pathStore store.PathStore, pipelineStore store.PipelineStore, secretStore store.SecretStore, + spaceStore store.SpaceStore, repoStore store.RepoStore, principalStore store.PrincipalStore, + repoCtrl *repo.Controller, membershipStore store.MembershipStore, ) *Controller { return &Controller{ db: db, @@ -41,6 +42,7 @@ func NewController(db *sqlx.DB, urlProvider *url.Provider, authorizer: authorizer, pathStore: pathStore, pipelineStore: pipelineStore, + secretStore: secretStore, spaceStore: spaceStore, repoStore: repoStore, principalStore: principalStore, diff --git a/internal/api/controller/space/list_secrets.go b/internal/api/controller/space/list_secrets.go new file mode 100644 index 000000000..0e175d798 --- /dev/null +++ b/internal/api/controller/space/list_secrets.go @@ -0,0 +1,52 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by the Polyform Free Trial License +// that can be found in the LICENSE.md file for this repository. +package space + +import ( + "context" + "fmt" + + apiauth "github.com/harness/gitness/internal/api/auth" + "github.com/harness/gitness/internal/auth" + "github.com/harness/gitness/store/database/dbtx" + "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" +) + +// ListSecrets lists the secrets in a space. +func (c *Controller) ListSecrets(ctx context.Context, session *auth.Session, + spaceRef string, filter *types.SecretFilter) ([]types.Secret, int64, error) { + space, err := c.spaceStore.FindByRef(ctx, spaceRef) + if err != nil { + return nil, 0, fmt.Errorf("failed to find parent space: %w", err) + } + + err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionSpaceView, true) + if err != nil { + return nil, 0, fmt.Errorf("could not authorize: %w", err) + } + + var count int64 + var secrets []types.Secret + + err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) (err error) { + var dbErr error + count, dbErr = c.secretStore.Count(ctx, space.ID, filter) + if dbErr != nil { + return fmt.Errorf("failed to count child executions: %w", err) + } + + secrets, dbErr = c.secretStore.List(ctx, space.ID, filter) + if dbErr != nil { + return fmt.Errorf("failed to list child executions: %w", err) + } + + return dbErr + }, dbtx.TxDefaultReadOnly) + if err != nil { + return secrets, count, err + } + + return secrets, count, nil +} diff --git a/internal/api/controller/space/wire.go b/internal/api/controller/space/wire.go index 9162675bb..9b126de43 100644 --- a/internal/api/controller/space/wire.go +++ b/internal/api/controller/space/wire.go @@ -21,12 +21,11 @@ var WireSet = wire.NewSet( ) func ProvideController(db *sqlx.DB, urlProvider *url.Provider, uidCheck check.PathUID, authorizer authz.Authorizer, - pathStore store.PathStore, pipelineStore store.PipelineStore, spaceStore store.SpaceStore, repoStore store.RepoStore, - principalStore store.PrincipalStore, repoCtrl *repo.Controller, - membershipStore store.MembershipStore, + pathStore store.PathStore, pipelineStore store.PipelineStore, secretStore store.SecretStore, + spaceStore store.SpaceStore, repoStore store.RepoStore, principalStore store.PrincipalStore, + repoCtrl *repo.Controller, membershipStore store.MembershipStore, ) *Controller { return NewController(db, urlProvider, uidCheck, authorizer, - pathStore, pipelineStore, spaceStore, repoStore, - principalStore, repoCtrl, - membershipStore) + pathStore, pipelineStore, secretStore, spaceStore, repoStore, + principalStore, repoCtrl, membershipStore) } diff --git a/internal/api/handler/space/list_secrets.go b/internal/api/handler/space/list_secrets.go new file mode 100644 index 000000000..d5e0f1eb2 --- /dev/null +++ b/internal/api/handler/space/list_secrets.go @@ -0,0 +1,42 @@ +// Copyright 2022 Harness Inc. All rights reserved. +// Use of this source code is governed by the Polyform Free Trial License +// that can be found in the LICENSE.md file for this repository. + +package space + +import ( + "net/http" + + "github.com/harness/gitness/internal/api/controller/space" + "github.com/harness/gitness/internal/api/render" + "github.com/harness/gitness/internal/api/request" + "github.com/harness/gitness/types" +) + +func HandleListSecrets(spaceCtrl *space.Controller) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + session, _ := request.AuthSessionFrom(ctx) + spaceRef, err := request.GetSpaceRefFromPath(r) + if err != nil { + render.TranslatedUserError(w, err) + return + } + + filter := request.ParseSecretFilter(r) + ret, totalCount, err := spaceCtrl.ListSecrets(ctx, session, spaceRef, filter) + if err != nil { + render.TranslatedUserError(w, err) + return + } + + // Strip out data in the returned value + secrets := []types.Secret{} + for _, s := range ret { + secrets = append(secrets, *s.Copy()) + } + + render.Pagination(r, w, filter.Page, filter.Size, int(totalCount)) + render.JSON(w, http.StatusOK, secrets) + } +} diff --git a/internal/api/openapi/space.go b/internal/api/openapi/space.go index 0a043ae47..b6e5a0573 100644 --- a/internal/api/openapi/space.go +++ b/internal/api/openapi/space.go @@ -242,6 +242,18 @@ func spaceOperations(reflector *openapi3.Reflector) { _ = reflector.SetJSONResponse(&opPipelines, new(usererror.Error), http.StatusNotFound) _ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/pipelines", opPipelines) + opSecrets := openapi3.Operation{} + opSecrets.WithTags("space") + opSecrets.WithMapOfAnything(map[string]interface{}{"operationId": "listSecrets"}) + opSecrets.WithParameters(queryParameterQueryRepo, queryParameterPage, queryParameterLimit) + _ = reflector.SetRequest(&opSecrets, new(spaceRequest), http.MethodGet) + _ = reflector.SetJSONResponse(&opSecrets, []types.Secret{}, http.StatusOK) + _ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusInternalServerError) + _ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusUnauthorized) + _ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusForbidden) + _ = reflector.SetJSONResponse(&opSecrets, new(usererror.Error), http.StatusNotFound) + _ = reflector.Spec.AddOperation(http.MethodGet, "/spaces/{space_ref}/secrets", opSecrets) + opServiceAccounts := openapi3.Operation{} opServiceAccounts.WithTags("space") opServiceAccounts.WithMapOfAnything(map[string]interface{}{"operationId": "listServiceAccounts"}) diff --git a/internal/router/api.go b/internal/router/api.go index 882ef5a36..da2d3c949 100644 --- a/internal/router/api.go +++ b/internal/router/api.go @@ -166,6 +166,7 @@ func setupSpaces(r chi.Router, spaceCtrl *space.Controller) { r.Get("/repos", handlerspace.HandleListRepos(spaceCtrl)) r.Get("/service-accounts", handlerspace.HandleListServiceAccounts(spaceCtrl)) r.Get("/pipelines", handlerspace.HandleListPipelines(spaceCtrl)) + r.Get("/secrets", handlerspace.HandleListSecrets(spaceCtrl)) // Child collections r.Route("/paths", func(r chi.Router) { diff --git a/internal/store/database.go b/internal/store/database.go index 951ee12b8..d5d276600 100644 --- a/internal/store/database.go +++ b/internal/store/database.go @@ -478,6 +478,9 @@ type ( // Create creates a new secret Create(ctx context.Context, secret *types.Secret) error + // Count the number of secrets in a space matching the given filter. + Count(ctx context.Context, spaceID int64, filter *types.SecretFilter) (int64, error) + // Update tries to update a secret. Update(ctx context.Context, secret *types.Secret) (*types.Secret, error) diff --git a/internal/store/database/secret.go b/internal/store/database/secret.go index a1ec03bf8..96ef4b8ee 100644 --- a/internal/store/database/secret.go +++ b/internal/store/database/secret.go @@ -234,6 +234,32 @@ func (s *secretStore) DeleteByUID(ctx context.Context, spaceID int64, uid string return nil } +// Count of secrets in a space. +func (s *secretStore) Count(ctx context.Context, parentID int64, opts *types.SecretFilter) (int64, error) { + stmt := database.Builder. + Select("count(*)"). + From("secrets"). + Where("secret_space_id = ?", parentID) + + if opts.Query != "" { + stmt = stmt.Where("secret_uid LIKE ?", fmt.Sprintf("%%%s%%", opts.Query)) + } + + sql, args, err := stmt.ToSql() + if err != nil { + return 0, errors.Wrap(err, "Failed to convert query to sql") + } + + db := dbtx.GetAccessor(ctx, s.db) + + var count int64 + err = db.QueryRowContext(ctx, sql, args...).Scan(&count) + if err != nil { + return 0, database.ProcessSQLErrorf(err, "Failed executing count query") + } + return count, nil +} + // helper function returns the same secret with encrypted data func enc(encrypt encrypt.Encrypter, secret *types.Secret) (*types.Secret, error) { s := *secret