harness/types/gitspace_settings.go

198 lines
5.6 KiB
Go

// 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 types
import (
"encoding/json"
"fmt"
"reflect"
"slices"
"sort"
"strings"
"github.com/harness/gitness/types/enum"
)
type GitspaceSettingsFilter struct {
ListQueryFilter
}
type CriteriaKey string
type SettingsData struct {
Data map[string]any `json:"data,omitempty"` // generic, user-defined
Criteria GitspaceSettingsCriteria `json:"criteria,omitempty"` // criteria for the settings
}
type GitspaceSettings struct {
ID int64 `json:"-"`
Settings SettingsData `json:"settings"`
SettingsType enum.GitspaceSettingsType `json:"settings_type,omitempty"`
CriteriaKey CriteriaKey `json:"criteria_key,omitempty"`
SpaceID int64 `json:"-"`
Created int64 `json:"created,omitempty"`
Updated int64 `json:"updated,omitempty"`
}
type GitspaceSettingsCriteria map[string]any
var ApplyAlwaysToSpaceCriteria = GitspaceSettingsCriteria{}
func flattenCriteria(prefix string, input map[string]any, out map[string]string) {
const maxDepth = 10 // Add depth protection
flattenCriteriaWithDepth(prefix, input, out, 0, maxDepth)
}
func flattenCriteriaWithDepth(prefix string, input map[string]any, out map[string]string, depth, maxDepth int) {
if depth > maxDepth {
return
}
for k, v := range input {
key := k
if prefix != "" {
key = prefix + "." + k
}
switch child := v.(type) {
case map[string]any:
flattenCriteriaWithDepth(key, child, out, depth+1, maxDepth)
default:
out[key] = fmt.Sprintf("%v", v)
}
}
}
func (c GitspaceSettingsCriteria) ToKey() (CriteriaKey, error) {
if len(c) == 0 {
return "", nil
}
flat := make(map[string]string)
flattenCriteria("", c, flat)
keys := make([]string, 0, len(flat))
for k := range flat {
keys = append(keys, k)
}
sort.Strings(keys)
parts := make([]string, 0, len(keys)*2)
for _, k := range keys {
parts = append(parts, k, flat[k])
}
return CriteriaKey(strings.Join(parts, "/")), nil
}
type IDESettings struct {
AccessList *AccessList[enum.IDEType] `json:"access_list,omitempty"` // Access control for IDEs
DisableSSH bool `json:"disable_ssh"`
}
type AccessList[T comparable] struct {
Mode ListMode `json:"mode"`
List []T `json:"list"`
}
func (a *AccessList[T]) IsAllowed(item T) bool {
if a == nil || len(a.List) == 0 {
return true // If no list, assume unrestricted
}
found := slices.Contains(a.List, item)
if a.Mode == ListModeAllow {
return found
}
// ListModeDeny
return !found
}
func (a *AccessList[T]) Remove(item T) bool {
if a == nil || len(a.List) == 0 {
return false
}
for i, entry := range a.List {
if entry == item {
a.List = append(a.List[:i], a.List[i+1:]...)
return true
}
}
return false
}
type ListMode string
const (
ListModeAllow ListMode = "allow"
ListModeDeny ListMode = "deny"
)
type SCMProviderSettings struct {
AccessList *AccessList[enum.GitspaceCodeRepoType] `json:"access_list,omitempty"` // Allow/Deny list for SCM providers
}
type DevcontainerSettings struct {
DevcontainerImage DevcontainerImage `json:"devcontainer_image"` // Devcontainer image settings
}
type DevcontainerImage struct {
AccessList *AccessList[string] `json:"access_list,omitempty"` // Allow/Deny list for container images
ImageConnectorRef string `json:"image_connector_ref,omitempty"` // Connector reference for the image
ImageName string `json:"image_name,omitempty"` // Name of the container image
}
type GitspaceConfigSettings struct {
IDEs IDESettings `json:"ide"` // allow list of IDEs
SCMProviders SCMProviderSettings `json:"scm"` // allow list of SCMs
Devcontainer DevcontainerSettings `json:"devcontainer"` // allow list of devcontainer images
}
type InfraProviderSettings struct {
AccessList *AccessList[string] `json:"access_list,omitempty"`
AutoStoppingTimeInMins *int `json:"auto_stopping_time_in_mins,omitempty"`
InfraProviderType enum.InfraProviderType `json:"infra_provider_type,omitempty"`
}
var settingsTypeRegistry = map[enum.GitspaceSettingsType]reflect.Type{
enum.SettingsTypeInfraProvider: reflect.TypeOf(InfraProviderSettings{}),
enum.SettingsTypeGitspaceConfig: reflect.TypeOf(GitspaceConfigSettings{}),
}
func DecodeSettings[T any](data map[string]any) (*T, error) {
jsonData, err := json.Marshal(data)
if err != nil {
return nil, err
}
var out T
if err := json.Unmarshal(jsonData, &out); err != nil {
return nil, err
}
return &out, nil
}
func ValidateAndDecodeSettings(settingsType enum.GitspaceSettingsType, data map[string]any) (any, error) {
typ, ok := settingsTypeRegistry[settingsType]
if !ok {
return nil, fmt.Errorf("no schema registered for settings type: %s", settingsType)
}
// Allocate a new struct
ptr := reflect.New(typ).Interface()
jsonData, err := json.Marshal(data)
if err != nil {
return nil, err
}
if err := json.Unmarshal(jsonData, ptr); err != nil {
return nil, fmt.Errorf("schema mismatch: %w", err)
}
return ptr, nil
}