Skip to content

Commit d9128ff

Browse files
authored
feat: allow you to patch every config property as needed (#118)
1 parent b5ad45b commit d9128ff

28 files changed

+1262
-564
lines changed

README.md

+21-2
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,31 @@ Key Value
174174
auto_rotate_before 48h0m0s
175175
auto_rotate_token false
176176
base_url https://gitlab.example.com
177-
token_id 107
177+
token_id 1
178178
token_expires_at 2025-03-29T00:00:00Z
179-
token_sha1_hash 1014647cd9bbf359d926fcacdf78e184db9dbedc
179+
token_sha1_hash 9441e6e07d77a2d5601ab5d7cac5868d358d885c
180180
type self-managed
181181
```
182182

183+
After initial setup should you wish to change any value you can do so by using the patch command for example
184+
185+
```shell
186+
$ vault patch gitlab/config type=saas auto_rotate_token=true auto_rotate_before=64h token=glpat-secret-admin-token
187+
Key Value
188+
--- -----
189+
auto_rotate_before 64h0m0s
190+
auto_rotate_token true
191+
base_url https://gitlab.example.com
192+
scopes api, read_api, read_user, sudo, admin_mode, create_runner, k8s_proxy, read_repository, write_repository, ai_features, read_service_ping
193+
token_created_at 2024-07-11T18:53:26Z
194+
token_expires_at 2025-07-11T00:00:00Z
195+
token_id 2
196+
token_sha1_hash c6e762667cadb936f0c8439b0d240661a270eba1
197+
type saas
198+
```
199+
200+
All the config properties as defined above in the Config section can be patched.
201+
183202
You may also need to configure the Max/Default TTL for a token that can be issued by setting:
184203

185204
Max TTL: `1 year`

backend.go

+11-13
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,17 @@ func (b *Backend) periodicFunc(ctx context.Context, request *logical.Request) er
9393
var err error
9494

9595
b.lockClientMutex.Lock()
96-
if config, err = getConfig(ctx, request.Storage); err != nil {
97-
b.lockClientMutex.Unlock()
98-
return err
99-
}
100-
b.lockClientMutex.Unlock()
101-
102-
if config == nil {
103-
return nil
104-
}
105-
106-
// If we need to autorotate the token, initiate the procedure to autorotate the token
107-
if config.AutoRotateToken {
108-
err = errors.Join(err, b.checkAndRotateConfigToken(ctx, request, config))
96+
unlockLockClientMutex := sync.OnceFunc(func() { b.lockClientMutex.Unlock() })
97+
defer unlockLockClientMutex()
98+
if config, err = getConfig(ctx, request.Storage); err == nil {
99+
unlockLockClientMutex()
100+
if config == nil {
101+
return nil
102+
}
103+
// If we need to autorotate the token, initiate the procedure to autorotate the token
104+
if config.AutoRotateToken {
105+
err = errors.Join(err, b.checkAndRotateConfigToken(ctx, request, config))
106+
}
109107
}
110108

111109
return err

entry_config.go

+130-25
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,138 @@ import (
44
"context"
55
"crypto/sha1"
66
"fmt"
7+
"strconv"
78
"strings"
89
"time"
910

11+
"github.com/hashicorp/go-multierror"
12+
"github.com/hashicorp/vault/sdk/framework"
1013
"github.com/hashicorp/vault/sdk/logical"
1114
)
1215

1316
type EntryConfig struct {
1417
TokenId int `json:"token_id" yaml:"token_id" mapstructure:"token_id"`
1518
BaseURL string `json:"base_url" structs:"base_url" mapstructure:"base_url"`
16-
Token string `json:"token" structs:"token" mapstructure:"token"`
19+
Token string `json:"token" structs:"token" mapstructure:"token" validate:"min=10,max=40"`
1720
AutoRotateToken bool `json:"auto_rotate_token" structs:"auto_rotate_token" mapstructure:"auto_rotate_token"`
1821
AutoRotateBefore time.Duration `json:"auto_rotate_before" structs:"auto_rotate_before" mapstructure:"auto_rotate_before"`
1922
TokenCreatedAt time.Time `json:"token_created_at" structs:"token_created_at" mapstructure:"token_created_at"`
2023
TokenExpiresAt time.Time `json:"token_expires_at" structs:"token_expires_at" mapstructure:"token_expires_at"`
2124
Scopes []string `json:"scopes" structs:"scopes" mapstructure:"scopes"`
22-
Type Type `json:"type" structs:"type" mapstructure:"type"`
25+
Type Type `json:"type" structs:"type" mapstructure:"type" validate:"gitlab-type"`
2326
}
2427

25-
func (e EntryConfig) Response() *logical.Response {
28+
func (e *EntryConfig) Merge(data *framework.FieldData) (warnings []string, changes map[string]string, err error) {
29+
var er error
30+
if data == nil {
31+
return warnings, changes, multierror.Append(fmt.Errorf("data: %w", ErrNilValue))
32+
}
33+
34+
if err = data.Validate(); err != nil {
35+
return warnings, changes, multierror.Append(err)
36+
}
37+
38+
changes = make(map[string]string)
39+
40+
if val, ok := data.GetOk("auto_rotate_token"); ok {
41+
e.AutoRotateToken = val.(bool)
42+
changes["auto_rotate_token"] = strconv.FormatBool(e.AutoRotateToken)
43+
}
44+
45+
if typ, ok := data.GetOk("type"); ok {
46+
var pType Type
47+
if pType, er = TypeParse(typ.(string)); er != nil {
48+
err = multierror.Append(err, er)
49+
} else {
50+
e.Type = pType
51+
changes["type"] = pType.String()
52+
}
53+
}
54+
55+
if _, ok := data.GetOk("auto_rotate_before"); ok {
56+
w, er := e.updateAutoRotateBefore(data)
57+
if er != nil {
58+
err = multierror.Append(err, er.Errors...)
59+
} else {
60+
changes["auto_rotate_before"] = e.AutoRotateBefore.String()
61+
}
62+
warnings = append(warnings, w...)
63+
}
64+
65+
if val, ok := data.GetOk("base_url"); ok && len(val.(string)) > 0 {
66+
e.BaseURL = val.(string)
67+
changes["base_url"] = e.BaseURL
68+
}
69+
70+
if val, ok := data.GetOk("token"); ok && len(val.(string)) > 0 {
71+
e.Token = val.(string)
72+
changes["token"] = strings.Repeat("*", len(e.Token))
73+
}
74+
75+
return warnings, changes, err
76+
}
77+
78+
func (e *EntryConfig) updateAutoRotateBefore(data *framework.FieldData) (warnings []string, err *multierror.Error) {
79+
if val, ok := data.GetOk("auto_rotate_before"); ok {
80+
atr, _ := convertToInt(val)
81+
if atr > int(DefaultAutoRotateBeforeMaxTTL.Seconds()) {
82+
err = multierror.Append(err, fmt.Errorf("auto_rotate_token can not be bigger than %s: %w", DefaultAutoRotateBeforeMaxTTL, ErrInvalidValue))
83+
} else if atr <= int(DefaultAutoRotateBeforeMinTTL.Seconds())-1 {
84+
err = multierror.Append(err, fmt.Errorf("auto_rotate_token can not be less than %s: %w", DefaultAutoRotateBeforeMinTTL, ErrInvalidValue))
85+
} else {
86+
e.AutoRotateBefore = time.Duration(atr) * time.Second
87+
}
88+
} else {
89+
e.AutoRotateBefore = DefaultAutoRotateBeforeMinTTL
90+
warnings = append(warnings, fmt.Sprintf("auto_rotate_token not specified setting to %s", DefaultAutoRotateBeforeMinTTL))
91+
}
92+
return warnings, err
93+
}
94+
95+
func (e *EntryConfig) UpdateFromFieldData(data *framework.FieldData) (warnings []string, err error) {
96+
if data == nil {
97+
return warnings, multierror.Append(fmt.Errorf("data: %w", ErrNilValue))
98+
}
99+
100+
if err = data.Validate(); err != nil {
101+
return warnings, multierror.Append(err)
102+
}
103+
104+
var er error
105+
e.AutoRotateToken = data.Get("auto_rotate_token").(bool)
106+
107+
if token, ok := data.GetOk("token"); ok && len(token.(string)) > 0 {
108+
e.Token = token.(string)
109+
} else {
110+
err = multierror.Append(err, fmt.Errorf("token: %w", ErrFieldRequired))
111+
}
112+
113+
if typ, ok := data.GetOk("type"); ok {
114+
if e.Type, er = TypeParse(typ.(string)); er != nil {
115+
err = multierror.Append(err, er)
116+
}
117+
} else {
118+
err = multierror.Append(err, fmt.Errorf("gitlab type: %w", ErrFieldRequired))
119+
}
120+
121+
if baseUrl, ok := data.GetOk("base_url"); ok && len(baseUrl.(string)) > 0 {
122+
e.BaseURL = baseUrl.(string)
123+
} else {
124+
err = multierror.Append(err, fmt.Errorf("base_url: %w", ErrFieldRequired))
125+
}
126+
127+
{
128+
w, er := e.updateAutoRotateBefore(data)
129+
if er != nil {
130+
err = multierror.Append(err, er.Errors...)
131+
}
132+
warnings = append(warnings, w...)
133+
}
134+
135+
return warnings, err
136+
}
137+
138+
func (e *EntryConfig) Response() *logical.Response {
26139
return &logical.Response{
27140
Secret: &logical.Secret{
28141
LeaseOptions: logical.LeaseOptions{},
@@ -35,7 +148,7 @@ func (e EntryConfig) Response() *logical.Response {
35148
}
36149
}
37150

38-
func (e EntryConfig) LogicalResponseData() map[string]any {
151+
func (e *EntryConfig) LogicalResponseData() map[string]any {
39152
var tokenExpiresAt, tokenCreatedAt = "", ""
40153
if !e.TokenExpiresAt.IsZero() {
41154
tokenExpiresAt = e.TokenExpiresAt.Format(time.RFC3339)
@@ -57,33 +170,25 @@ func (e EntryConfig) LogicalResponseData() map[string]any {
57170
}
58171
}
59172

60-
func getConfig(ctx context.Context, s logical.Storage) (*EntryConfig, error) {
173+
func getConfig(ctx context.Context, s logical.Storage) (cfg *EntryConfig, err error) {
61174
if s == nil {
62175
return nil, fmt.Errorf("%w: local.Storage", ErrNilValue)
63176
}
64-
entry, err := s.Get(ctx, PathConfigStorage)
65-
if err != nil {
66-
return nil, err
177+
var entry *logical.StorageEntry
178+
if entry, err = s.Get(ctx, PathConfigStorage); err == nil {
179+
if entry == nil {
180+
return nil, nil
181+
}
182+
cfg = new(EntryConfig)
183+
_ = entry.DecodeJSON(cfg)
67184
}
68-
69-
if entry == nil {
70-
return nil, nil
71-
}
72-
73-
cfg := new(EntryConfig)
74-
if err := entry.DecodeJSON(cfg); err != nil {
75-
return nil, err
76-
}
77-
return cfg, nil
185+
return cfg, err
78186
}
79187

80-
func saveConfig(ctx context.Context, config EntryConfig, s logical.Storage) error {
81-
var err error
188+
func saveConfig(ctx context.Context, config EntryConfig, s logical.Storage) (err error) {
82189
var storageEntry *logical.StorageEntry
83-
storageEntry, err = logical.StorageEntryJSON(PathConfigStorage, config)
84-
if err != nil {
85-
return nil
190+
if storageEntry, err = logical.StorageEntryJSON(PathConfigStorage, config); err == nil {
191+
err = s.Put(ctx, storageEntry)
86192
}
87-
88-
return s.Put(ctx, storageEntry)
193+
return err
89194
}

0 commit comments

Comments
 (0)