Skip to content

Commit 49dc9c6

Browse files
authored
secret storage (#3128)
* feat(storage): add storage table * feat(db): add secret storage * feat(stoarge): add service * feat(keys): add storage model * feat(keys): model fields * feat(keys): add storages page * feat(keys): add secret storages controller * refactor: use services for access key descryption * feat(keys): add vault support * feat(keys): add endpoints * fix(keys): fill installer interface * fix: null pointers of services * fix(api): incorrect usage middleware * fix(secrets): fix sql query * feat(secrets): manipulation of storage * feat(storage): works * fix(storage): do not clear access key * feat(storage): vault settings * feat(storage): choose storage for access key * refactor(secrets): copy method to serializer * feat(secrets): add access key service * test: fix tests * test: fix tests * fix: services * feat(secrets): store keys to vault * feat(secrets): delete storage endpoint * feat(secrets): delete storage endpoint * fix(secrets): correct condition * feat: add icon * feat(secrets): add menu to new storage button * feat(secrets): add icon for strage * fix(secrets): allow empty token field for existing storage * feat(secrets): readonly secret storage * feat(secrets): add readonly badge * feat(secrets): delete secret from vault * feat(secrets): delete remote secret when delete access key * feat(secrets): use access key service * security(secrets): upgrade dep * refactor(secrets): split to foss and pro * refactor(service): rename package name * refactor(features): add GetFeatures function * feat(secrets): update ui * ci: dir to /tmp for deps * refactor(pro): add mock pro dir * chore: ingore go.work * test(secrets): fix mock * fix(secrets): sqlite migration * ci: fix docker image build
1 parent 7283631 commit 49dc9c6

File tree

87 files changed

+2542
-1046
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+2542
-1046
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,7 @@ __debug_bin*
3838

3939
/events.log
4040
/tasks.log
41-
/task_results.log
41+
/task_results.log
42+
/pro_impl/
43+
/go.work
44+
/go.work.sum

Taskfile.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ tasks:
3434
# - go install github.com/go-swagger/go-swagger/cmd/swagger@{{ .SWAGGER_VERSION }}
3535
- go install github.com/goreleaser/goreleaser@{{ .GORELEASER_VERSION }}
3636
# - go install github.com/golangci/golangci-lint/cmd/golangci-lint@{{ .GOLINTER_VERSION }}
37+
dir: /tmp
3738

3839
deps:be:
3940
desc: Vendor application dependencies

api/api_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,16 @@ func TestApiPing(t *testing.T) {
1515
req, _ := http.NewRequest("GET", "/api/ping", nil)
1616
rr := httptest.NewRecorder()
1717

18-
r := Route(nil, nil)
18+
r := Route(
19+
nil,
20+
nil,
21+
nil,
22+
nil,
23+
nil,
24+
nil,
25+
nil,
26+
nil,
27+
)
1928

2029
r.ServeHTTP(rr, req)
2130

api/apps.go

Lines changed: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,63 +6,13 @@ import (
66
"fmt"
77
"github.com/semaphoreui/semaphore/api/helpers"
88
"github.com/semaphoreui/semaphore/db"
9+
"github.com/semaphoreui/semaphore/pkg/conv"
910
"github.com/semaphoreui/semaphore/util"
1011
"net/http"
1112
"reflect"
1213
"sort"
13-
"strings"
1414
)
1515

16-
func structToFlatMap(obj any) map[string]any {
17-
result := make(map[string]any)
18-
val := reflect.ValueOf(obj)
19-
typ := reflect.TypeOf(obj)
20-
21-
if typ.Kind() == reflect.Ptr {
22-
val = val.Elem()
23-
typ = typ.Elem()
24-
}
25-
26-
if typ.Kind() != reflect.Struct {
27-
return result
28-
}
29-
30-
// Iterate over the struct fields
31-
for i := 0; i < val.NumField(); i++ {
32-
field := val.Field(i)
33-
fieldType := typ.Field(i)
34-
jsonTag := fieldType.Tag.Get("json")
35-
36-
// Use the json tag if it is set, otherwise use the field name
37-
fieldName := jsonTag
38-
if fieldName == "" || fieldName == "-" {
39-
fieldName = fieldType.Name
40-
} else {
41-
// Handle the case where the json tag might have options like `json:"name,omitempty"`
42-
fieldName = strings.Split(fieldName, ",")[0]
43-
}
44-
45-
// Check if the field is a struct itself
46-
if field.Kind() == reflect.Struct {
47-
// Convert nested struct to map
48-
nestedMap := structToFlatMap(field.Interface())
49-
// Add nested map to result with a prefixed key
50-
for k, v := range nestedMap {
51-
result[fieldName+"."+k] = v
52-
}
53-
} else if (field.Kind() == reflect.Ptr ||
54-
field.Kind() == reflect.Array ||
55-
field.Kind() == reflect.Slice ||
56-
field.Kind() == reflect.Map) && field.IsNil() {
57-
result[fieldName] = nil
58-
} else {
59-
result[fieldName] = field.Interface()
60-
}
61-
}
62-
63-
return result
64-
}
65-
6616
func validateAppID(str string) error {
6717
return nil
6818
}
@@ -170,7 +120,7 @@ func setApp(w http.ResponseWriter, r *http.Request) {
170120
return
171121
}
172122

173-
options := structToFlatMap(app)
123+
options := conv.StructToFlatMap(app)
174124

175125
for k, v := range options {
176126
t := reflect.TypeOf(v)

api/apps_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package api
22

33
import (
44
"fmt"
5+
"github.com/semaphoreui/semaphore/pkg/conv"
56
"testing"
67
)
78

@@ -32,7 +33,7 @@ func TestStructToMap(t *testing.T) {
3233
}
3334

3435
// Convert the struct to a flat map
35-
flatMap := structToFlatMap(&p)
36+
flatMap := conv.StructToFlatMap(&p)
3637

3738
if flatMap["address.city"] != "New York" {
3839
t.Fail()

api/integration.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/json"
77
"fmt"
88
"github.com/semaphoreui/semaphore/pkg/conv"
9+
"github.com/semaphoreui/semaphore/services/server"
910
task2 "github.com/semaphoreui/semaphore/services/tasks"
1011
"io"
1112
"net/http"
@@ -44,7 +45,17 @@ func hmacHashPayload(secret string, payloadBody []byte) string {
4445
return fmt.Sprintf("%x", sum)
4546
}
4647

47-
func ReceiveIntegration(w http.ResponseWriter, r *http.Request) {
48+
type IntegrationController struct {
49+
integrationService server.IntegrationService
50+
}
51+
52+
func NewIntegrationController(integrationService server.IntegrationService) *IntegrationController {
53+
return &IntegrationController{
54+
integrationService: integrationService,
55+
}
56+
}
57+
58+
func (c *IntegrationController) ReceiveIntegration(w http.ResponseWriter, r *http.Request) {
4859

4960
var err error
5061

@@ -95,7 +106,7 @@ func ReceiveIntegration(w http.ResponseWriter, r *http.Request) {
95106
panic("")
96107
}
97108

98-
err = db.FillIntegration(store, &integration)
109+
err = c.integrationService.FillIntegration(&integration)
99110
if err != nil {
100111
log.Error(err)
101112
return
@@ -288,7 +299,7 @@ func RunIntegration(integration db.Integration, project db.Project, r *http.Requ
288299
log.Error(err)
289300
return
290301
}
291-
302+
292303
pool := helpers.GetFromContext(r, "task_pool").(*task2.TaskPool)
293304

294305
_, err = pool.AddTask(taskDefinition, nil, "", integration.ProjectID, tpl.App.NeedTaskAlias())

api/projects/environment.go

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,29 @@ import (
44
"fmt"
55
"github.com/semaphoreui/semaphore/api/helpers"
66
"github.com/semaphoreui/semaphore/db"
7+
"github.com/semaphoreui/semaphore/services/server"
78
"net/http"
89
)
910

10-
func updateEnvironmentSecrets(store db.Store, env db.Environment) error {
11+
type EnvironmentController struct {
12+
accessKeyRepo db.AccessKeyManager
13+
accessKeyService server.AccessKeyService
14+
encryptionService server.AccessKeyEncryptionService
15+
}
16+
17+
func NewEnvironmentController(
18+
accessKeyRepo db.AccessKeyManager,
19+
encryptionService server.AccessKeyEncryptionService,
20+
accessKeyService server.AccessKeyService,
21+
) *EnvironmentController {
22+
return &EnvironmentController{
23+
accessKeyRepo: accessKeyRepo,
24+
accessKeyService: accessKeyService,
25+
encryptionService: encryptionService,
26+
}
27+
}
28+
29+
func (c *EnvironmentController) updateEnvironmentSecrets(env db.Environment) error {
1130
for _, secret := range env.Secrets {
1231
err := secret.Validate()
1332
if err != nil {
@@ -18,7 +37,7 @@ func updateEnvironmentSecrets(store db.Store, env db.Environment) error {
1837

1938
switch secret.Operation {
2039
case db.EnvironmentSecretCreate:
21-
key, err = store.CreateAccessKey(db.AccessKey{
40+
key, err = c.accessKeyService.CreateAccessKey(db.AccessKey{
2241
Name: secret.Name,
2342
String: secret.Secret,
2443
EnvironmentID: &env.ID,
@@ -27,7 +46,7 @@ func updateEnvironmentSecrets(store db.Store, env db.Environment) error {
2746
Owner: secret.Type.GetAccessKeyOwner(),
2847
})
2948
case db.EnvironmentSecretDelete:
30-
key, err = store.GetAccessKey(env.ProjectID, secret.ID)
49+
key, err = c.accessKeyRepo.GetAccessKey(env.ProjectID, secret.ID)
3150

3251
if err != nil {
3352
continue
@@ -37,9 +56,9 @@ func updateEnvironmentSecrets(store db.Store, env db.Environment) error {
3756
continue
3857
}
3958

40-
err = store.DeleteAccessKey(env.ProjectID, secret.ID)
59+
err = c.accessKeyService.DeleteAccessKey(env.ProjectID, secret.ID)
4160
case db.EnvironmentSecretUpdate:
42-
key, err = store.GetAccessKey(env.ProjectID, secret.ID)
61+
key, err = c.accessKeyRepo.GetAccessKey(env.ProjectID, secret.ID)
4362

4463
if err != nil {
4564
continue
@@ -61,15 +80,15 @@ func updateEnvironmentSecrets(store db.Store, env db.Environment) error {
6180
updateKey.OverrideSecret = true
6281
}
6382

64-
err = store.UpdateAccessKey(updateKey)
83+
err = c.accessKeyService.UpdateAccessKey(updateKey)
6584
}
6685
}
6786

6887
return nil
6988
}
7089

7190
// EnvironmentMiddleware ensures an environment exists and loads it to the context
72-
func EnvironmentMiddleware(next http.Handler) http.Handler {
91+
func (c *EnvironmentController) EnvironmentMiddleware(next http.Handler) http.Handler {
7392
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
7493
project := helpers.GetFromContext(r, "project").(db.Project)
7594
envID, err := helpers.GetIntParam("environment_id", w, r)
@@ -85,7 +104,7 @@ func EnvironmentMiddleware(next http.Handler) http.Handler {
85104
return
86105
}
87106

88-
if err = db.FillEnvironmentSecrets(helpers.Store(r), &env, false); err != nil {
107+
if err = c.encryptionService.FillEnvironmentSecrets(&env, false); err != nil {
89108
helpers.WriteError(w, err)
90109
return
91110
}
@@ -128,7 +147,7 @@ func GetEnvironment(w http.ResponseWriter, r *http.Request) {
128147
}
129148

130149
// UpdateEnvironment updates an existing environment in the database
131-
func UpdateEnvironment(w http.ResponseWriter, r *http.Request) {
150+
func (c *EnvironmentController) UpdateEnvironment(w http.ResponseWriter, r *http.Request) {
132151
oldEnv := helpers.GetFromContext(r, "environment").(db.Environment)
133152
var env db.Environment
134153
if !helpers.Bind(w, r, &env) {
@@ -162,7 +181,7 @@ func UpdateEnvironment(w http.ResponseWriter, r *http.Request) {
162181
Description: fmt.Sprintf("Environment %s updated", env.Name),
163182
})
164183

165-
if err := updateEnvironmentSecrets(helpers.Store(r), env); err != nil {
184+
if err := c.updateEnvironmentSecrets(env); err != nil {
166185
helpers.WriteError(w, err)
167186
return
168187
}
@@ -171,7 +190,7 @@ func UpdateEnvironment(w http.ResponseWriter, r *http.Request) {
171190
}
172191

173192
// AddEnvironment creates an environment in the database
174-
func AddEnvironment(w http.ResponseWriter, r *http.Request) {
193+
func (c *EnvironmentController) AddEnvironment(w http.ResponseWriter, r *http.Request) {
175194
project := helpers.GetFromContext(r, "project").(db.Project)
176195
var env db.Environment
177196

@@ -199,9 +218,9 @@ func AddEnvironment(w http.ResponseWriter, r *http.Request) {
199218
Description: fmt.Sprintf("Environment %s created", newEnv.Name),
200219
})
201220

202-
if err = updateEnvironmentSecrets(helpers.Store(r), newEnv); err != nil {
203-
//helpers.WriteError(w, err)
204-
//return
221+
if err = c.updateEnvironmentSecrets(newEnv); err != nil {
222+
helpers.WriteError(w, err)
223+
return
205224
}
206225

207226
// Reload env

api/projects/keys.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
package projects
22

33
import (
4+
"errors"
45
"fmt"
6+
"github.com/semaphoreui/semaphore/services/server"
57
"net/http"
68

79
"github.com/semaphoreui/semaphore/api/helpers"
810
"github.com/semaphoreui/semaphore/db"
911
)
1012

13+
type KeyController struct {
14+
accessKeyService server.AccessKeyService
15+
}
16+
17+
func NewKeyController(
18+
accessKeyService server.AccessKeyService,
19+
) *KeyController {
20+
return &KeyController{
21+
accessKeyService: accessKeyService,
22+
}
23+
}
24+
1125
// KeyMiddleware ensures a key exists and loads it to the context
1226
func KeyMiddleware(next http.Handler) http.Handler {
1327
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -62,7 +76,7 @@ func GetKeys(w http.ResponseWriter, r *http.Request) {
6276
}
6377

6478
// AddKey adds a new key to the database
65-
func AddKey(w http.ResponseWriter, r *http.Request) {
79+
func (c *KeyController) AddKey(w http.ResponseWriter, r *http.Request) {
6680
project := helpers.GetFromContext(r, "project").(db.Project)
6781
var key db.AccessKey
6882

@@ -84,7 +98,7 @@ func AddKey(w http.ResponseWriter, r *http.Request) {
8498
return
8599
}
86100

87-
newKey, err := helpers.Store(r).CreateAccessKey(key)
101+
newKey, err := c.accessKeyService.CreateAccessKey(key)
88102

89103
if err != nil {
90104
helpers.WriteError(w, err)
@@ -111,7 +125,7 @@ func AddKey(w http.ResponseWriter, r *http.Request) {
111125

112126
// UpdateKey updates key in database
113127
// nolint: gocyclo
114-
func UpdateKey(w http.ResponseWriter, r *http.Request) {
128+
func (c *KeyController) UpdateKey(w http.ResponseWriter, r *http.Request) {
115129
var key db.AccessKey
116130
oldKey := helpers.GetFromContext(r, "accessKey").(db.AccessKey)
117131

@@ -136,7 +150,7 @@ func UpdateKey(w http.ResponseWriter, r *http.Request) {
136150
}
137151
}
138152

139-
err = helpers.Store(r).UpdateAccessKey(key)
153+
err = c.accessKeyService.UpdateAccessKey(key)
140154
if err != nil {
141155
helpers.WriteError(w, err)
142156
return
@@ -154,11 +168,11 @@ func UpdateKey(w http.ResponseWriter, r *http.Request) {
154168
}
155169

156170
// RemoveKey deletes a key from the database
157-
func RemoveKey(w http.ResponseWriter, r *http.Request) {
171+
func (c *KeyController) RemoveKey(w http.ResponseWriter, r *http.Request) {
158172
key := helpers.GetFromContext(r, "accessKey").(db.AccessKey)
159173

160-
err := helpers.Store(r).DeleteAccessKey(*key.ProjectID, key.ID)
161-
if err == db.ErrInvalidOperation {
174+
err := c.accessKeyService.DeleteAccessKey(*key.ProjectID, key.ID)
175+
if errors.Is(err, db.ErrInvalidOperation) {
162176
helpers.WriteJSON(w, http.StatusBadRequest, map[string]any{
163177
"error": "Access Key is in use by one or more templates",
164178
"inUse": true,

api/projects/project.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"github.com/gorilla/mux"
55
"github.com/semaphoreui/semaphore/api/helpers"
66
"github.com/semaphoreui/semaphore/db"
7-
"github.com/semaphoreui/semaphore/services"
7+
"github.com/semaphoreui/semaphore/services/server"
88
"github.com/semaphoreui/semaphore/util"
99
log "github.com/sirupsen/logrus"
1010
"net/http"
@@ -63,7 +63,7 @@ func GetMustCanMiddleware(permissions db.ProjectUserPermission) mux.MiddlewareFu
6363
}
6464

6565
type ProjectController struct {
66-
ProjectService services.ProjectService
66+
ProjectService server.ProjectService
6767
}
6868

6969
func (c *ProjectController) UpdateProject(w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)