mirror of https://github.com/harness/drone.git
Adding Repo Level Settings (#1145)
parent
4deed68349
commit
39a998eacd
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/harness/gitness/app/auth/authz"
|
||||
eventsgit "github.com/harness/gitness/app/events/git"
|
||||
"github.com/harness/gitness/app/services/protection"
|
||||
"github.com/harness/gitness/app/services/settings"
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/app/url"
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -38,7 +39,8 @@ type Controller struct {
|
|||
pullreqStore store.PullReqStore
|
||||
urlProvider url.Provider
|
||||
protectionManager *protection.Manager
|
||||
resourceLimiter limiter.ResourceLimiter
|
||||
limiter limiter.ResourceLimiter
|
||||
settings *settings.Service
|
||||
preReceiveExtender PreReceiveExtender
|
||||
updateExtender UpdateExtender
|
||||
postReceiveExtender PostReceiveExtender
|
||||
|
@ -53,6 +55,7 @@ func NewController(
|
|||
urlProvider url.Provider,
|
||||
protectionManager *protection.Manager,
|
||||
limiter limiter.ResourceLimiter,
|
||||
settings *settings.Service,
|
||||
preReceiveExtender PreReceiveExtender,
|
||||
updateExtender UpdateExtender,
|
||||
postReceiveExtender PostReceiveExtender,
|
||||
|
@ -66,7 +69,8 @@ func NewController(
|
|||
pullreqStore: pullreqStore,
|
||||
urlProvider: urlProvider,
|
||||
protectionManager: protectionManager,
|
||||
resourceLimiter: limiter,
|
||||
limiter: limiter,
|
||||
settings: settings,
|
||||
preReceiveExtender: preReceiveExtender,
|
||||
updateExtender: updateExtender,
|
||||
postReceiveExtender: postReceiveExtender,
|
||||
|
|
|
@ -46,7 +46,7 @@ func (c *Controller) PreReceive(
|
|||
return hook.Output{}, err
|
||||
}
|
||||
|
||||
if err := c.resourceLimiter.RepoSize(ctx, in.RepoID); err != nil {
|
||||
if err := c.limiter.RepoSize(ctx, in.RepoID); err != nil {
|
||||
return hook.Output{}, fmt.Errorf(
|
||||
"resource limit exceeded: %w",
|
||||
limiter.ErrMaxRepoSizeReached)
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/harness/gitness/app/services/importer"
|
||||
"github.com/harness/gitness/app/services/keywordsearch"
|
||||
"github.com/harness/gitness/app/services/protection"
|
||||
"github.com/harness/gitness/app/services/settings"
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/app/url"
|
||||
"github.com/harness/gitness/git"
|
||||
|
@ -56,6 +57,7 @@ type Controller struct {
|
|||
pipelineStore store.PipelineStore
|
||||
principalStore store.PrincipalStore
|
||||
ruleStore store.RuleStore
|
||||
settings *settings.Service
|
||||
principalInfoCache store.PrincipalInfoCache
|
||||
protectionManager *protection.Manager
|
||||
git git.Interface
|
||||
|
@ -79,6 +81,7 @@ func NewController(
|
|||
pipelineStore store.PipelineStore,
|
||||
principalStore store.PrincipalStore,
|
||||
ruleStore store.RuleStore,
|
||||
settings *settings.Service,
|
||||
principalInfoCache store.PrincipalInfoCache,
|
||||
protectionManager *protection.Manager,
|
||||
git git.Interface,
|
||||
|
@ -102,6 +105,7 @@ func NewController(
|
|||
pipelineStore: pipelineStore,
|
||||
principalStore: principalStore,
|
||||
ruleStore: ruleStore,
|
||||
settings: settings,
|
||||
principalInfoCache: principalInfoCache,
|
||||
protectionManager: protectionManager,
|
||||
git: git,
|
||||
|
@ -121,20 +125,11 @@ func (c *Controller) getRepo(
|
|||
ctx context.Context,
|
||||
repoRef string,
|
||||
) (*types.Repository, error) {
|
||||
if repoRef == "" {
|
||||
return nil, usererror.BadRequest("A valid repository reference must be provided.")
|
||||
}
|
||||
|
||||
repo, err := c.repoStore.FindByRef(ctx, repoRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find repository: %w", err)
|
||||
}
|
||||
|
||||
if repo.Importing {
|
||||
return nil, usererror.BadRequest("Repository import is in progress.")
|
||||
}
|
||||
|
||||
return repo, nil
|
||||
return GetRepo(
|
||||
ctx,
|
||||
c.repoStore,
|
||||
repoRef,
|
||||
)
|
||||
}
|
||||
|
||||
// getRepoCheckAccess fetches an active repo (not one that is currently being imported)
|
||||
|
@ -146,16 +141,15 @@ func (c *Controller) getRepoCheckAccess(
|
|||
reqPermission enum.Permission,
|
||||
orPublic bool,
|
||||
) (*types.Repository, error) {
|
||||
repo, err := c.getRepo(ctx, repoRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find repo: %w", err)
|
||||
}
|
||||
|
||||
if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, reqPermission, orPublic); err != nil {
|
||||
return nil, fmt.Errorf("access check failed: %w", err)
|
||||
}
|
||||
|
||||
return repo, nil
|
||||
return GetRepoCheckAccess(
|
||||
ctx,
|
||||
c.repoStore,
|
||||
c.authorizer,
|
||||
session,
|
||||
repoRef,
|
||||
reqPermission,
|
||||
orPublic,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Controller) validateParentRef(parentRef string) error {
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apiauth "github.com/harness/gitness/app/api/auth"
|
||||
"github.com/harness/gitness/app/api/usererror"
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/app/auth/authz"
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// GetRepo fetches an active repo (not one that is currently being imported).
|
||||
func GetRepo(
|
||||
ctx context.Context,
|
||||
repoStore store.RepoStore,
|
||||
repoRef string,
|
||||
) (*types.Repository, error) {
|
||||
if repoRef == "" {
|
||||
return nil, usererror.BadRequest("A valid repository reference must be provided.")
|
||||
}
|
||||
|
||||
repo, err := repoStore.FindByRef(ctx, repoRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find repository: %w", err)
|
||||
}
|
||||
|
||||
if repo.Importing {
|
||||
return nil, usererror.BadRequest("Repository import is in progress.")
|
||||
}
|
||||
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
// GetRepoCheckAccess fetches an active repo (not one that is currently being imported)
|
||||
// and checks if the current user has permission to access it.
|
||||
func GetRepoCheckAccess(
|
||||
ctx context.Context,
|
||||
repoStore store.RepoStore,
|
||||
authorizer authz.Authorizer,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
reqPermission enum.Permission,
|
||||
orPublic bool,
|
||||
) (*types.Repository, error) {
|
||||
repo, err := GetRepo(ctx, repoStore, repoRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find repo: %w", err)
|
||||
}
|
||||
|
||||
if err = apiauth.CheckRepo(ctx, authorizer, session, repo, reqPermission, orPublic); err != nil {
|
||||
return nil, fmt.Errorf("access check failed: %w", err)
|
||||
}
|
||||
|
||||
return repo, nil
|
||||
}
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/harness/gitness/app/services/importer"
|
||||
"github.com/harness/gitness/app/services/keywordsearch"
|
||||
"github.com/harness/gitness/app/services/protection"
|
||||
"github.com/harness/gitness/app/services/settings"
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/app/url"
|
||||
"github.com/harness/gitness/git"
|
||||
|
@ -48,6 +49,7 @@ func ProvideController(
|
|||
pipelineStore store.PipelineStore,
|
||||
principalStore store.PrincipalStore,
|
||||
ruleStore store.RuleStore,
|
||||
settings *settings.Service,
|
||||
principalInfoCache store.PrincipalInfoCache,
|
||||
protectionManager *protection.Manager,
|
||||
rpcClient git.Interface,
|
||||
|
@ -63,7 +65,7 @@ func ProvideController(
|
|||
return NewController(config, tx, urlProvider,
|
||||
authorizer, repoStore,
|
||||
spaceStore, pipelineStore,
|
||||
principalStore, ruleStore, principalInfoCache, protectionManager,
|
||||
principalStore, ruleStore, settings, principalInfoCache, protectionManager,
|
||||
rpcClient, importer, codeOwners, reporeporter, indexer, limiter, mtxManager, identifierCheck, repoChecks)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reposettings
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/app/auth/authz"
|
||||
"github.com/harness/gitness/app/services/settings"
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
authorizer authz.Authorizer
|
||||
repoStore store.RepoStore
|
||||
settings *settings.Service
|
||||
}
|
||||
|
||||
func NewController(
|
||||
authorizer authz.Authorizer,
|
||||
repoStore store.RepoStore,
|
||||
settings *settings.Service,
|
||||
) *Controller {
|
||||
return &Controller{
|
||||
authorizer: authorizer,
|
||||
repoStore: repoStore,
|
||||
settings: settings,
|
||||
}
|
||||
}
|
||||
|
||||
// getRepoCheckAccess fetches an active repo (not one that is currently being imported)
|
||||
// and checks if the current user has permission to access it.
|
||||
func (c *Controller) getRepoCheckAccess(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
reqPermission enum.Permission,
|
||||
orPublic bool,
|
||||
) (*types.Repository, error) {
|
||||
return repo.GetRepoCheckAccess(
|
||||
ctx,
|
||||
c.repoStore,
|
||||
c.authorizer,
|
||||
session,
|
||||
repoRef,
|
||||
reqPermission,
|
||||
orPublic,
|
||||
)
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reposettings
|
||||
|
||||
import (
|
||||
"github.com/harness/gitness/app/services/settings"
|
||||
|
||||
"github.com/gotidy/ptr"
|
||||
)
|
||||
|
||||
// SecuritySettings represents the security related part of repository settings as exposed externally.
|
||||
type SecuritySettings struct {
|
||||
SecretScanningEnabled *bool `json:"secret_scanning_enabled"`
|
||||
}
|
||||
|
||||
func GetDefaultSecuritySettings() *SecuritySettings {
|
||||
return &SecuritySettings{
|
||||
SecretScanningEnabled: ptr.Bool(settings.DefaultSecretScanningEnabled),
|
||||
}
|
||||
}
|
||||
|
||||
func GetSecuritySettingsMappings(s *SecuritySettings) []settings.SettingHandler {
|
||||
return []settings.SettingHandler{
|
||||
settings.Mapping(settings.KeySecretScanningEnabled, s.SecretScanningEnabled),
|
||||
}
|
||||
}
|
||||
|
||||
func GetSecuritySettingsAsKeyValues(s *SecuritySettings) []settings.KeyValue {
|
||||
kvs := make([]settings.KeyValue, 0, 1)
|
||||
if s.SecretScanningEnabled != nil {
|
||||
kvs = append(kvs, settings.KeyValue{Key: settings.KeySecretScanningEnabled, Value: *s.SecretScanningEnabled})
|
||||
}
|
||||
return kvs
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reposettings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// SecurityFind returns the security settings of a repo.
|
||||
func (c *Controller) SecurityFind(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
) (*SecuritySettings, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := GetDefaultSecuritySettings()
|
||||
mappings := GetSecuritySettingsMappings(out)
|
||||
err = c.settings.RepoMap(ctx, repo.ID, mappings...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to map settings: %w", err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reposettings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// SecurityUpdate updates the security settings of the repo.
|
||||
func (c *Controller) SecurityUpdate(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
repoRef string,
|
||||
in *SecuritySettings,
|
||||
) (*SecuritySettings, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoEdit, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.settings.RepoSetMany(ctx, repo.ID, GetSecuritySettingsAsKeyValues(in)...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set settings: %w", err)
|
||||
}
|
||||
|
||||
// read all settings and return complete config
|
||||
out := GetDefaultSecuritySettings()
|
||||
mappings := GetSecuritySettingsMappings(out)
|
||||
err = c.settings.RepoMap(ctx, repo.ID, mappings...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to map settings: %w", err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reposettings
|
||||
|
||||
import (
|
||||
"github.com/harness/gitness/app/auth/authz"
|
||||
"github.com/harness/gitness/app/services/settings"
|
||||
"github.com/harness/gitness/app/store"
|
||||
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(
|
||||
ProvideController,
|
||||
)
|
||||
|
||||
func ProvideController(
|
||||
authorizer authz.Authorizer,
|
||||
repoStore store.RepoStore,
|
||||
settings *settings.Service,
|
||||
) *Controller {
|
||||
return NewController(authorizer, repoStore, settings)
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reposettings
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/reposettings"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleSecurityFind(repoSettingCtrl *reposettings.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
settings, err := repoSettingCtrl.SecurityFind(ctx, session, repoRef)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, settings)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package reposettings
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/reposettings"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleSecurityUpdate(repoSettingCtrl *reposettings.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(reposettings.SecuritySettings)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
settings, err := repoSettingCtrl.SecurityUpdate(ctx, session, repoRef, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusOK, settings)
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/harness/gitness/app/auth/authz"
|
||||
eventsgit "github.com/harness/gitness/app/events/git"
|
||||
"github.com/harness/gitness/app/services/protection"
|
||||
"github.com/harness/gitness/app/services/settings"
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/app/url"
|
||||
"github.com/harness/gitness/git"
|
||||
|
@ -52,6 +53,7 @@ func ProvideController(
|
|||
protectionManager *protection.Manager,
|
||||
githookFactory hook.ClientFactory,
|
||||
limiter limiter.ResourceLimiter,
|
||||
settings *settings.Service,
|
||||
preReceiveExtender githook.PreReceiveExtender,
|
||||
updateExtender githook.UpdateExtender,
|
||||
postReceiveExtender githook.PostReceiveExtender,
|
||||
|
@ -65,6 +67,7 @@ func ProvideController(
|
|||
urlProvider,
|
||||
protectionManager,
|
||||
limiter,
|
||||
settings,
|
||||
preReceiveExtender,
|
||||
updateExtender,
|
||||
postReceiveExtender,
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/harness/gitness/app/api/controller/principal"
|
||||
"github.com/harness/gitness/app/api/controller/pullreq"
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/controller/reposettings"
|
||||
"github.com/harness/gitness/app/api/controller/secret"
|
||||
"github.com/harness/gitness/app/api/controller/serviceaccount"
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
|
@ -51,6 +52,7 @@ import (
|
|||
handlerprincipal "github.com/harness/gitness/app/api/handler/principal"
|
||||
handlerpullreq "github.com/harness/gitness/app/api/handler/pullreq"
|
||||
handlerrepo "github.com/harness/gitness/app/api/handler/repo"
|
||||
handlerreposettings "github.com/harness/gitness/app/api/handler/reposettings"
|
||||
"github.com/harness/gitness/app/api/handler/resource"
|
||||
handlersecret "github.com/harness/gitness/app/api/handler/secret"
|
||||
handlerserviceaccount "github.com/harness/gitness/app/api/handler/serviceaccount"
|
||||
|
@ -97,6 +99,7 @@ func NewAPIHandler(
|
|||
config *types.Config,
|
||||
authenticator authn.Authenticator,
|
||||
repoCtrl *repo.Controller,
|
||||
repoSettingsCtrl *reposettings.Controller,
|
||||
executionCtrl *execution.Controller,
|
||||
logCtrl *logs.Controller,
|
||||
spaceCtrl *space.Controller,
|
||||
|
@ -139,7 +142,7 @@ func NewAPIHandler(
|
|||
r.Use(middlewareauthn.Attempt(authenticator))
|
||||
|
||||
r.Route("/v1", func(r chi.Router) {
|
||||
setupRoutesV1(r, appCtx, config, repoCtrl, executionCtrl, triggerCtrl, logCtrl, pipelineCtrl,
|
||||
setupRoutesV1(r, appCtx, config, repoCtrl, repoSettingsCtrl, executionCtrl, triggerCtrl, logCtrl, pipelineCtrl,
|
||||
connectorCtrl, templateCtrl, pluginCtrl, secretCtrl, spaceCtrl, pullreqCtrl,
|
||||
webhookCtrl, githookCtrl, git, saCtrl, userCtrl, principalCtrl, checkCtrl, sysCtrl, uploadCtrl,
|
||||
searchCtrl)
|
||||
|
@ -167,6 +170,7 @@ func setupRoutesV1(r chi.Router,
|
|||
appCtx context.Context,
|
||||
config *types.Config,
|
||||
repoCtrl *repo.Controller,
|
||||
repoSettingsCtrl *reposettings.Controller,
|
||||
executionCtrl *execution.Controller,
|
||||
triggerCtrl *trigger.Controller,
|
||||
logCtrl *logs.Controller,
|
||||
|
@ -189,8 +193,8 @@ func setupRoutesV1(r chi.Router,
|
|||
searchCtrl *keywordsearch.Controller,
|
||||
) {
|
||||
setupSpaces(r, appCtx, spaceCtrl)
|
||||
setupRepos(r, repoCtrl, pipelineCtrl, executionCtrl, triggerCtrl, logCtrl, pullreqCtrl, webhookCtrl, checkCtrl,
|
||||
uploadCtrl)
|
||||
setupRepos(r, repoCtrl, repoSettingsCtrl, pipelineCtrl, executionCtrl, triggerCtrl,
|
||||
logCtrl, pullreqCtrl, webhookCtrl, checkCtrl, uploadCtrl)
|
||||
setupConnectors(r, connectorCtrl)
|
||||
setupTemplates(r, templateCtrl)
|
||||
setupSecrets(r, secretCtrl)
|
||||
|
@ -248,6 +252,7 @@ func setupSpaces(r chi.Router, appCtx context.Context, spaceCtrl *space.Controll
|
|||
|
||||
func setupRepos(r chi.Router,
|
||||
repoCtrl *repo.Controller,
|
||||
repoSettingsCtrl *reposettings.Controller,
|
||||
pipelineCtrl *pipeline.Controller,
|
||||
executionCtrl *execution.Controller,
|
||||
triggerCtrl *trigger.Controller,
|
||||
|
@ -269,6 +274,9 @@ func setupRepos(r chi.Router,
|
|||
r.Post("/purge", handlerrepo.HandlePurge(repoCtrl))
|
||||
r.Post("/restore", handlerrepo.HandleRestore(repoCtrl))
|
||||
|
||||
r.Get("/settings/security", handlerreposettings.HandleSecurityFind(repoSettingsCtrl))
|
||||
r.Patch("/settings/security", handlerreposettings.HandleSecurityUpdate(repoSettingsCtrl))
|
||||
|
||||
r.Post("/move", handlerrepo.HandleMove(repoCtrl))
|
||||
r.Get("/service-accounts", handlerrepo.HandleListServiceAccounts(repoCtrl))
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/harness/gitness/app/api/controller/principal"
|
||||
"github.com/harness/gitness/app/api/controller/pullreq"
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/controller/reposettings"
|
||||
"github.com/harness/gitness/app/api/controller/secret"
|
||||
"github.com/harness/gitness/app/api/controller/serviceaccount"
|
||||
"github.com/harness/gitness/app/api/controller/space"
|
||||
|
@ -92,6 +93,7 @@ func ProvideAPIHandler(
|
|||
config *types.Config,
|
||||
authenticator authn.Authenticator,
|
||||
repoCtrl *repo.Controller,
|
||||
repoSettingsCtrl *reposettings.Controller,
|
||||
executionCtrl *execution.Controller,
|
||||
logCtrl *logs.Controller,
|
||||
spaceCtrl *space.Controller,
|
||||
|
@ -114,7 +116,7 @@ func ProvideAPIHandler(
|
|||
searchCtrl *keywordsearch.Controller,
|
||||
) APIHandler {
|
||||
return NewAPIHandler(appCtx, config,
|
||||
authenticator, repoCtrl, executionCtrl, logCtrl, spaceCtrl, pipelineCtrl,
|
||||
authenticator, repoCtrl, repoSettingsCtrl, executionCtrl, logCtrl, spaceCtrl, pipelineCtrl,
|
||||
secretCtrl, triggerCtrl, connectorCtrl, templateCtrl, pluginCtrl, pullreqCtrl, webhookCtrl,
|
||||
githookCtrl, git, saCtrl, userCtrl, principalCtrl, checkCtrl, sysCtrl, blobCtrl, searchCtrl)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// RepoGet is a helper method for getting a setting of a specific type for a repo.
|
||||
func RepoGet[T any](
|
||||
ctx context.Context,
|
||||
s *Service,
|
||||
repoID int64,
|
||||
key Key,
|
||||
dflt T,
|
||||
) (T, error) {
|
||||
var out T
|
||||
ok, err := s.RepoGet(ctx, repoID, key, &out)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return dflt, nil
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// RepoGetRequired is a helper method for getting a setting of a specific type for a repo.
|
||||
// If the setting isn't found, an error is returned.
|
||||
func RepoGetRequired[T any](
|
||||
ctx context.Context,
|
||||
s *Service,
|
||||
repoID int64,
|
||||
key Key,
|
||||
) (T, error) {
|
||||
var out T
|
||||
ok, err := s.RepoGet(ctx, repoID, key, &out)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return out, fmt.Errorf("setting %q not found", key)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Mapping returns a SettingHandler that maps the value of the setting with the given key to the target.
|
||||
func Mapping[T any](key Key, target *T) SettingHandler {
|
||||
if target == nil {
|
||||
panic("mapping target can't be nil")
|
||||
}
|
||||
return &settingHandlerMapping[T]{
|
||||
key: key,
|
||||
required: false,
|
||||
target: target,
|
||||
}
|
||||
}
|
||||
|
||||
// MappingRequired returns a SettingHandler that maps the value of the setting with the given key to the target.
|
||||
// If the setting wasn't found an error is returned.
|
||||
func MappingRequired[T any](key Key, target *T) SettingHandler {
|
||||
if target == nil {
|
||||
panic("mapping target can't be nil")
|
||||
}
|
||||
return &settingHandlerMapping[T]{
|
||||
key: key,
|
||||
required: true,
|
||||
target: target,
|
||||
}
|
||||
}
|
||||
|
||||
var _ SettingHandler = (*settingHandlerMapping[any])(nil)
|
||||
|
||||
// settingHandlerMapping is a setting handler that maps the value of a setting to the provided target.
|
||||
type settingHandlerMapping[T any] struct {
|
||||
key Key
|
||||
required bool
|
||||
target *T
|
||||
}
|
||||
|
||||
func (q *settingHandlerMapping[T]) Key() Key {
|
||||
return q.key
|
||||
}
|
||||
|
||||
func (q *settingHandlerMapping[T]) Required() bool {
|
||||
return q.required
|
||||
}
|
||||
|
||||
func (q *settingHandlerMapping[T]) Handle(_ context.Context, raw []byte) error {
|
||||
err := json.Unmarshal(raw, q.target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal setting value: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
appstore "github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// KeyValue is a struct used for upserting many entries.
|
||||
type KeyValue struct {
|
||||
Key Key
|
||||
Value any
|
||||
}
|
||||
|
||||
// SettingHandler is an abstraction of a component that's handling a single setting value as part of
|
||||
// calling service.Map.
|
||||
type SettingHandler interface {
|
||||
Key() Key
|
||||
Required() bool
|
||||
Handle(ctx context.Context, raw []byte) error
|
||||
}
|
||||
|
||||
// Service is used to enhance interaction with the settings store.
|
||||
type Service struct {
|
||||
settingsStore appstore.SettingsStore
|
||||
}
|
||||
|
||||
func NewService(
|
||||
settingsStore appstore.SettingsStore,
|
||||
) *Service {
|
||||
return &Service{
|
||||
settingsStore: settingsStore,
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets the value of the setting with the given key for the given scope.
|
||||
func (s *Service) Set(
|
||||
ctx context.Context,
|
||||
scope enum.SettingsScope,
|
||||
scopeID int64,
|
||||
key Key,
|
||||
value any,
|
||||
) error {
|
||||
raw, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal setting value: %w", err)
|
||||
}
|
||||
|
||||
err = s.settingsStore.Upsert(
|
||||
ctx,
|
||||
scope,
|
||||
scopeID,
|
||||
string(key),
|
||||
raw,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upsert setting in store: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMany sets the value of the settings with the given keys for the given scope.
|
||||
func (s *Service) SetMany(
|
||||
ctx context.Context,
|
||||
scope enum.SettingsScope,
|
||||
scopeID int64,
|
||||
keyValues ...KeyValue,
|
||||
) error {
|
||||
// TODO: batch upsert
|
||||
for _, kv := range keyValues {
|
||||
if err := s.Set(ctx, scope, scopeID, kv.Key, kv.Value); err != nil {
|
||||
return fmt.Errorf("failed to set setting for key %q: %w", kv.Key, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns the value of the setting with the given key for the given scope.
|
||||
func (s *Service) Get(
|
||||
ctx context.Context,
|
||||
scope enum.SettingsScope,
|
||||
scopeID int64,
|
||||
key Key,
|
||||
out any,
|
||||
) (bool, error) {
|
||||
raw, err := s.settingsStore.Find(
|
||||
ctx,
|
||||
scope,
|
||||
scopeID,
|
||||
string(key),
|
||||
)
|
||||
if errors.Is(err, store.ErrResourceNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to find setting in store: %w", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(raw, &out)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to unmarshal setting value: %w", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Map maps all available settings using the provided handlers for the given scope.
|
||||
func (s *Service) Map(
|
||||
ctx context.Context,
|
||||
scope enum.SettingsScope,
|
||||
scopeID int64,
|
||||
handlers ...SettingHandler,
|
||||
) error {
|
||||
if len(handlers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
keys := make([]string, len(handlers))
|
||||
for i, m := range handlers {
|
||||
keys[i] = string(m.Key())
|
||||
}
|
||||
|
||||
rawValues, err := s.settingsStore.FindMany(
|
||||
ctx,
|
||||
scope,
|
||||
scopeID,
|
||||
keys...,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find settings in store: %w", err)
|
||||
}
|
||||
|
||||
for _, m := range handlers {
|
||||
rawValue, found := rawValues[string(m.Key())]
|
||||
if !found && m.Required() {
|
||||
return fmt.Errorf("required setting %q not found", m.Key())
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = m.Handle(ctx, rawValue); err != nil {
|
||||
return fmt.Errorf("failed to handle value for setting %q: %w", m.Key(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// RepoSet sets the value of the setting with the given key for the given repo.
|
||||
func (s *Service) RepoSet(
|
||||
ctx context.Context,
|
||||
repoID int64,
|
||||
key Key,
|
||||
value any,
|
||||
) error {
|
||||
return s.Set(
|
||||
ctx,
|
||||
enum.SettingsScopeRepo,
|
||||
repoID,
|
||||
key,
|
||||
value,
|
||||
)
|
||||
}
|
||||
|
||||
// RepoSetMany sets the value of the settings with the given keys for the given repo.
|
||||
func (s *Service) RepoSetMany(
|
||||
ctx context.Context,
|
||||
repoID int64,
|
||||
keyValues ...KeyValue,
|
||||
) error {
|
||||
return s.SetMany(
|
||||
ctx,
|
||||
enum.SettingsScopeRepo,
|
||||
repoID,
|
||||
keyValues...,
|
||||
)
|
||||
}
|
||||
|
||||
// RepoGet returns the value of the setting with the given key for the given repo.
|
||||
func (s *Service) RepoGet(
|
||||
ctx context.Context,
|
||||
repoID int64,
|
||||
key Key,
|
||||
out any,
|
||||
) (bool, error) {
|
||||
return s.Get(
|
||||
ctx,
|
||||
enum.SettingsScopeRepo,
|
||||
repoID,
|
||||
key,
|
||||
out,
|
||||
)
|
||||
}
|
||||
|
||||
// RepoMap maps all available settings using the provided handlers for the given repo.
|
||||
func (s *Service) RepoMap(
|
||||
ctx context.Context,
|
||||
repoID int64,
|
||||
handlers ...SettingHandler,
|
||||
) error {
|
||||
return s.Map(
|
||||
ctx,
|
||||
enum.SettingsScopeRepo,
|
||||
repoID,
|
||||
handlers...,
|
||||
)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package settings
|
||||
|
||||
type Key string
|
||||
|
||||
var (
|
||||
// KeySecretScanningEnabled [bool] enables secret scanning if set to true.
|
||||
KeySecretScanningEnabled Key = "secret_scanning_enabled"
|
||||
DefaultSecretScanningEnabled = false
|
||||
)
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
"github.com/harness/gitness/app/store"
|
||||
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
var WireSet = wire.NewSet(
|
||||
ProvideService,
|
||||
)
|
||||
|
||||
func ProvideService(
|
||||
settingsStore store.SettingsStore,
|
||||
) *Service {
|
||||
return NewService(settingsStore)
|
||||
}
|
|
@ -17,6 +17,7 @@ package store
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -239,6 +240,35 @@ type (
|
|||
ListSizeInfos(ctx context.Context) ([]*types.RepositorySizeInfo, error)
|
||||
}
|
||||
|
||||
// SettingsStore defines the settings storage.
|
||||
SettingsStore interface {
|
||||
// Find returns the value of the setting with the given key for the provided scope.
|
||||
Find(
|
||||
ctx context.Context,
|
||||
scope enum.SettingsScope,
|
||||
scopeID int64,
|
||||
key string,
|
||||
) (json.RawMessage, error)
|
||||
|
||||
// FindMany returns the values of the settings with the given keys for the provided scope.
|
||||
// NOTE: if a setting key doesn't exist the map just won't contain an entry for it (no error returned).
|
||||
FindMany(
|
||||
ctx context.Context,
|
||||
scope enum.SettingsScope,
|
||||
scopeID int64,
|
||||
keys ...string,
|
||||
) (map[string]json.RawMessage, error)
|
||||
|
||||
// Upsert upserts the value of the setting with the given key for the provided scope.
|
||||
Upsert(
|
||||
ctx context.Context,
|
||||
scope enum.SettingsScope,
|
||||
scopeID int64,
|
||||
key string,
|
||||
value json.RawMessage,
|
||||
) error
|
||||
}
|
||||
|
||||
// RepoGitInfoView defines the repository GitUID view.
|
||||
RepoGitInfoView interface {
|
||||
Find(ctx context.Context, id int64) (*types.RepositoryGitInfo, error)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE settings;
|
|
@ -0,0 +1,24 @@
|
|||
CREATE TABLE settings (
|
||||
setting_id SERIAL PRIMARY KEY
|
||||
,setting_space_id INTEGER
|
||||
,setting_repo_id INTEGER
|
||||
,setting_key TEXT NOT NULL
|
||||
,setting_value JSON
|
||||
|
||||
,CONSTRAINT fk_settings_space_id FOREIGN KEY (setting_space_id)
|
||||
REFERENCES spaces (space_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE CASCADE
|
||||
,CONSTRAINT fk_settings_repo_id FOREIGN KEY (setting_repo_id)
|
||||
REFERENCES repositories (repo_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX settings_space_id_key
|
||||
ON settings(setting_space_id, LOWER(setting_key))
|
||||
WHERE setting_space_id IS NOT NULL;
|
||||
|
||||
CREATE UNIQUE INDEX settings_repo_id_key
|
||||
ON settings(setting_repo_id, LOWER(setting_key))
|
||||
WHERE setting_repo_id IS NOT NULL;
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE settings;
|
|
@ -0,0 +1,24 @@
|
|||
CREATE TABLE settings (
|
||||
setting_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
,setting_space_id INTEGER
|
||||
,setting_repo_id INTEGER
|
||||
,setting_key TEXT NOT NULL
|
||||
,setting_value TEXT
|
||||
|
||||
,CONSTRAINT fk_settings_space_id FOREIGN KEY (setting_space_id)
|
||||
REFERENCES spaces (space_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE CASCADE
|
||||
,CONSTRAINT fk_settings_repo_id FOREIGN KEY (setting_repo_id)
|
||||
REFERENCES repositories (repo_id) MATCH SIMPLE
|
||||
ON UPDATE NO ACTION
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX settings_space_id_key
|
||||
ON settings(setting_space_id, LOWER(setting_key))
|
||||
WHERE setting_space_id IS NOT NULL;
|
||||
|
||||
CREATE UNIQUE INDEX settings_repo_id_key
|
||||
ON settings(setting_repo_id, LOWER(setting_key))
|
||||
WHERE setting_repo_id IS NOT NULL;
|
|
@ -0,0 +1,194 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store/database"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/guregu/null"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
var _ store.SettingsStore = (*SettingsStore)(nil)
|
||||
|
||||
// NewSettingsStore returns a new SettingsStore.
|
||||
func NewSettingsStore(db *sqlx.DB) *SettingsStore {
|
||||
return &SettingsStore{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// SettingsStore implements store.SettingsStore backed by a relational database.
|
||||
type SettingsStore struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
// setting is an internal representation used to store setting data in the database.
|
||||
type setting struct {
|
||||
ID int64 `db:"setting_id"`
|
||||
SpaceID null.Int `db:"setting_space_id"`
|
||||
RepoID null.Int `db:"setting_repo_id"`
|
||||
Key string `db:"setting_key"`
|
||||
Value json.RawMessage `db:"setting_value"`
|
||||
}
|
||||
|
||||
const (
|
||||
settingsColumns = `
|
||||
setting_id
|
||||
,setting_space_id
|
||||
,setting_repo_id
|
||||
,setting_key
|
||||
,setting_value`
|
||||
)
|
||||
|
||||
func (s *SettingsStore) Find(
|
||||
ctx context.Context,
|
||||
scope enum.SettingsScope,
|
||||
scopeID int64,
|
||||
key string,
|
||||
) (json.RawMessage, error) {
|
||||
stmt := database.Builder.
|
||||
Select(settingsColumns).
|
||||
From("settings").
|
||||
Where("LOWER(setting_key) = ?", strings.ToLower(key))
|
||||
|
||||
switch scope {
|
||||
case enum.SettingsScopeSpace:
|
||||
stmt = stmt.Where("setting_space_id = ?", scopeID)
|
||||
case enum.SettingsScopeRepo:
|
||||
stmt = stmt.Where("setting_repo_id = ?", scopeID)
|
||||
default:
|
||||
return nil, fmt.Errorf("setting scope %q is not supported", scope)
|
||||
}
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert query to sql: %w", err)
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
dst := &setting{}
|
||||
if err := db.GetContext(ctx, dst, sql, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Select query failed")
|
||||
}
|
||||
|
||||
return dst.Value, nil
|
||||
}
|
||||
|
||||
func (s *SettingsStore) FindMany(
|
||||
ctx context.Context,
|
||||
scope enum.SettingsScope,
|
||||
scopeID int64,
|
||||
keys ...string,
|
||||
) (map[string]json.RawMessage, error) {
|
||||
if len(keys) == 0 {
|
||||
return map[string]json.RawMessage{}, nil
|
||||
}
|
||||
|
||||
keysLower := make([]string, len(keys))
|
||||
for i, k := range keys {
|
||||
keysLower[i] = strings.ToLower(k)
|
||||
}
|
||||
|
||||
stmt := database.Builder.
|
||||
Select(settingsColumns).
|
||||
From("settings").
|
||||
Where(squirrel.Eq{"LOWER(setting_key)": keysLower})
|
||||
|
||||
switch scope {
|
||||
case enum.SettingsScopeSpace:
|
||||
stmt = stmt.Where("setting_space_id = ?", scopeID)
|
||||
case enum.SettingsScopeRepo:
|
||||
stmt = stmt.Where("setting_repo_id = ?", scopeID)
|
||||
default:
|
||||
return nil, fmt.Errorf("setting scope %q is not supported", scope)
|
||||
}
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert query to sql: %w", err)
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
dst := []*setting{}
|
||||
if err := db.SelectContext(ctx, &dst, sql, args...); err != nil {
|
||||
return nil, database.ProcessSQLErrorf(ctx, err, "Select query failed")
|
||||
}
|
||||
|
||||
out := map[string]json.RawMessage{}
|
||||
for _, d := range dst {
|
||||
out[d.Key] = d.Value
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *SettingsStore) Upsert(ctx context.Context,
|
||||
scope enum.SettingsScope,
|
||||
scopeID int64,
|
||||
key string,
|
||||
value json.RawMessage,
|
||||
) error {
|
||||
stmt := database.Builder.
|
||||
Insert("").
|
||||
Into("settings").
|
||||
Columns(
|
||||
"setting_space_id",
|
||||
"setting_repo_id",
|
||||
"setting_key",
|
||||
"setting_value",
|
||||
)
|
||||
|
||||
switch scope {
|
||||
case enum.SettingsScopeSpace:
|
||||
stmt = stmt.Values(null.IntFrom(scopeID), null.Int{}, key, value)
|
||||
stmt = stmt.Suffix(`ON CONFLICT (setting_space_id, LOWER(setting_key)) WHERE setting_space_id IS NOT NULL DO`)
|
||||
case enum.SettingsScopeRepo:
|
||||
stmt = stmt.Values(null.Int{}, null.IntFrom(scopeID), key, value)
|
||||
stmt = stmt.Suffix(`ON CONFLICT (setting_repo_id, LOWER(setting_key)) WHERE setting_repo_id IS NOT NULL DO`)
|
||||
default:
|
||||
return fmt.Errorf("setting scope %q is not supported", scope)
|
||||
}
|
||||
|
||||
stmt = stmt.Suffix(`
|
||||
UPDATE SET
|
||||
setting_value = EXCLUDED.setting_value
|
||||
WHERE
|
||||
settings.setting_value <> EXCLUDED.setting_value`)
|
||||
|
||||
sql, args, err := stmt.ToSql()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert query to sql: %w", err)
|
||||
}
|
||||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
if _, err := db.ExecContext(ctx, sql, args...); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "Upsert query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -52,6 +52,7 @@ var WireSet = wire.NewSet(
|
|||
ProvidePullReqFileViewStore,
|
||||
ProvideWebhookStore,
|
||||
ProvideWebhookExecutionStore,
|
||||
ProvideSettingsStore,
|
||||
ProvideCheckStore,
|
||||
ProvideConnectorStore,
|
||||
ProvideTemplateStore,
|
||||
|
@ -241,3 +242,8 @@ func ProvideCheckStore(db *sqlx.DB,
|
|||
) store.CheckStore {
|
||||
return NewCheckStore(db, principalInfoCache)
|
||||
}
|
||||
|
||||
// ProvideSettingsStore provides a settings store.
|
||||
func ProvideSettingsStore(db *sqlx.DB) store.SettingsStore {
|
||||
return NewSettingsStore(db)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/harness/gitness/app/api/controller/principal"
|
||||
"github.com/harness/gitness/app/api/controller/pullreq"
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/controller/reposettings"
|
||||
"github.com/harness/gitness/app/api/controller/secret"
|
||||
"github.com/harness/gitness/app/api/controller/service"
|
||||
"github.com/harness/gitness/app/api/controller/serviceaccount"
|
||||
|
@ -64,6 +65,7 @@ import (
|
|||
"github.com/harness/gitness/app/services/protection"
|
||||
pullreqservice "github.com/harness/gitness/app/services/pullreq"
|
||||
"github.com/harness/gitness/app/services/reposize"
|
||||
"github.com/harness/gitness/app/services/settings"
|
||||
"github.com/harness/gitness/app/services/trigger"
|
||||
"github.com/harness/gitness/app/services/usergroup"
|
||||
"github.com/harness/gitness/app/services/webhook"
|
||||
|
@ -112,6 +114,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
|
|||
space.WireSet,
|
||||
limiter.WireSet,
|
||||
repo.WireSet,
|
||||
reposettings.WireSet,
|
||||
pullreq.WireSet,
|
||||
controllerwebhook.WireSet,
|
||||
serviceaccount.WireSet,
|
||||
|
@ -181,6 +184,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
|
|||
cliserver.ProvideKeywordSearchConfig,
|
||||
keywordsearch.WireSet,
|
||||
controllerkeywordsearch.WireSet,
|
||||
settings.WireSet,
|
||||
usergroup.WireSet,
|
||||
openapi.WireSet,
|
||||
repo.ProvideRepoCheck,
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/harness/gitness/app/api/controller/principal"
|
||||
pullreq2 "github.com/harness/gitness/app/api/controller/pullreq"
|
||||
"github.com/harness/gitness/app/api/controller/repo"
|
||||
"github.com/harness/gitness/app/api/controller/reposettings"
|
||||
"github.com/harness/gitness/app/api/controller/secret"
|
||||
"github.com/harness/gitness/app/api/controller/service"
|
||||
"github.com/harness/gitness/app/api/controller/serviceaccount"
|
||||
|
@ -63,6 +64,7 @@ import (
|
|||
"github.com/harness/gitness/app/services/protection"
|
||||
"github.com/harness/gitness/app/services/pullreq"
|
||||
"github.com/harness/gitness/app/services/reposize"
|
||||
"github.com/harness/gitness/app/services/settings"
|
||||
trigger2 "github.com/harness/gitness/app/services/trigger"
|
||||
"github.com/harness/gitness/app/services/usergroup"
|
||||
"github.com/harness/gitness/app/services/webhook"
|
||||
|
@ -126,6 +128,8 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||
repoStore := database.ProvideRepoStore(db, spacePathCache, spacePathStore, spaceStore)
|
||||
pipelineStore := database.ProvidePipelineStore(db)
|
||||
ruleStore := database.ProvideRuleStore(db, principalInfoCache)
|
||||
settingsStore := database.ProvideSettingsStore(db)
|
||||
settingsService := settings.ProvideService(settingsStore)
|
||||
protectionManager, err := protection.ProvideManager(ruleStore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -190,7 +194,8 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||
}
|
||||
repoIdentifier := check.ProvideRepoIdentifierCheck()
|
||||
repoCheck := repo.ProvideRepoCheck()
|
||||
repoController := repo.ProvideController(config, transactor, provider, authorizer, repoStore, spaceStore, pipelineStore, principalStore, ruleStore, principalInfoCache, protectionManager, gitInterface, repository, codeownersService, reporter, indexer, resourceLimiter, mutexManager, repoIdentifier, repoCheck)
|
||||
repoController := repo.ProvideController(config, transactor, provider, authorizer, repoStore, spaceStore, pipelineStore, principalStore, ruleStore, settingsService, principalInfoCache, protectionManager, gitInterface, repository, codeownersService, reporter, indexer, resourceLimiter, mutexManager, repoIdentifier, repoCheck)
|
||||
reposettingsController := reposettings.ProvideController(authorizer, repoStore, settingsService)
|
||||
executionStore := database.ProvideExecutionStore(db)
|
||||
checkStore := database.ProvideCheckStore(db, principalInfoCache)
|
||||
stageStore := database.ProvideStageStore(db)
|
||||
|
@ -274,7 +279,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
githookController := githook.ProvideController(authorizer, principalStore, repoStore, reporter2, gitInterface, pullReqStore, provider, protectionManager, clientFactory, resourceLimiter, preReceiveExtender, updateExtender, postReceiveExtender)
|
||||
githookController := githook.ProvideController(authorizer, principalStore, repoStore, reporter2, gitInterface, pullReqStore, provider, protectionManager, clientFactory, resourceLimiter, settingsService, preReceiveExtender, updateExtender, postReceiveExtender)
|
||||
serviceaccountController := serviceaccount.NewController(principalUID, authorizer, principalStore, spaceStore, repoStore, tokenStore)
|
||||
principalController := principal.ProvideController(principalStore)
|
||||
v := check2.ProvideCheckSanitizers()
|
||||
|
@ -291,7 +296,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||
uploadController := upload.ProvideController(authorizer, repoStore, blobStore)
|
||||
searcher := keywordsearch.ProvideSearcher(localIndexSearcher)
|
||||
keywordsearchController := keywordsearch2.ProvideController(authorizer, searcher, repoController, spaceController)
|
||||
apiHandler := router.ProvideAPIHandler(ctx, config, authenticator, repoController, executionController, logsController, spaceController, pipelineController, secretController, triggerController, connectorController, templateController, pluginController, pullreqController, webhookController, githookController, gitInterface, serviceaccountController, controller, principalController, checkController, systemController, uploadController, keywordsearchController)
|
||||
apiHandler := router.ProvideAPIHandler(ctx, config, authenticator, repoController, reposettingsController, executionController, logsController, spaceController, pipelineController, secretController, triggerController, connectorController, templateController, pluginController, pullreqController, webhookController, githookController, gitInterface, serviceaccountController, controller, principalController, checkController, systemController, uploadController, keywordsearchController)
|
||||
gitHandler := router.ProvideGitHandler(provider, authenticator, repoController)
|
||||
openapiService := openapi.ProvideOpenAPIService()
|
||||
webHandler := router.ProvideWebHandler(config, openapiService)
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package enum
|
||||
|
||||
// SettingsScope defines the different scopes of a setting.
|
||||
type SettingsScope string
|
||||
|
||||
func (SettingsScope) Enum() []interface{} {
|
||||
return toInterfaceSlice(GetAllSettingsScopes())
|
||||
}
|
||||
|
||||
var (
|
||||
// SettingsScopeSpace defines settings stored on a space level.
|
||||
SettingsScopeSpace SettingsScope = "space"
|
||||
|
||||
// SettingsScopeRepo defines settings stored on a repo level.
|
||||
SettingsScopeRepo SettingsScope = "repo"
|
||||
)
|
||||
|
||||
func GetAllSettingsScopes() []SettingsScope {
|
||||
return []SettingsScope{
|
||||
SettingsScopeSpace,
|
||||
SettingsScopeRepo,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue