Skip to content

Commit

Permalink
feat: allow you to patch every config property as needed (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
ilijamt authored Oct 12, 2024
1 parent b5ad45b commit d9128ff
Show file tree
Hide file tree
Showing 28 changed files with 1,262 additions and 564 deletions.
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,31 @@ Key Value
auto_rotate_before 48h0m0s
auto_rotate_token false
base_url https://gitlab.example.com
token_id 107
token_id 1
token_expires_at 2025-03-29T00:00:00Z
token_sha1_hash 1014647cd9bbf359d926fcacdf78e184db9dbedc
token_sha1_hash 9441e6e07d77a2d5601ab5d7cac5868d358d885c
type self-managed
```

After initial setup should you wish to change any value you can do so by using the patch command for example

```shell
$ vault patch gitlab/config type=saas auto_rotate_token=true auto_rotate_before=64h token=glpat-secret-admin-token
Key Value
--- -----
auto_rotate_before 64h0m0s
auto_rotate_token true
base_url https://gitlab.example.com
scopes api, read_api, read_user, sudo, admin_mode, create_runner, k8s_proxy, read_repository, write_repository, ai_features, read_service_ping
token_created_at 2024-07-11T18:53:26Z
token_expires_at 2025-07-11T00:00:00Z
token_id 2
token_sha1_hash c6e762667cadb936f0c8439b0d240661a270eba1
type saas
```

All the config properties as defined above in the Config section can be patched.

You may also need to configure the Max/Default TTL for a token that can be issued by setting:

Max TTL: `1 year`
Expand Down
24 changes: 11 additions & 13 deletions backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,17 @@ func (b *Backend) periodicFunc(ctx context.Context, request *logical.Request) er
var err error

b.lockClientMutex.Lock()
if config, err = getConfig(ctx, request.Storage); err != nil {
b.lockClientMutex.Unlock()
return err
}
b.lockClientMutex.Unlock()

if config == nil {
return nil
}

// If we need to autorotate the token, initiate the procedure to autorotate the token
if config.AutoRotateToken {
err = errors.Join(err, b.checkAndRotateConfigToken(ctx, request, config))
unlockLockClientMutex := sync.OnceFunc(func() { b.lockClientMutex.Unlock() })
defer unlockLockClientMutex()
if config, err = getConfig(ctx, request.Storage); err == nil {
unlockLockClientMutex()
if config == nil {
return nil
}
// If we need to autorotate the token, initiate the procedure to autorotate the token
if config.AutoRotateToken {
err = errors.Join(err, b.checkAndRotateConfigToken(ctx, request, config))
}
}

return err
Expand Down
155 changes: 130 additions & 25 deletions entry_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,138 @@ import (
"context"
"crypto/sha1"
"fmt"
"strconv"
"strings"
"time"

"github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
)

type EntryConfig struct {
TokenId int `json:"token_id" yaml:"token_id" mapstructure:"token_id"`
BaseURL string `json:"base_url" structs:"base_url" mapstructure:"base_url"`
Token string `json:"token" structs:"token" mapstructure:"token"`
Token string `json:"token" structs:"token" mapstructure:"token" validate:"min=10,max=40"`
AutoRotateToken bool `json:"auto_rotate_token" structs:"auto_rotate_token" mapstructure:"auto_rotate_token"`
AutoRotateBefore time.Duration `json:"auto_rotate_before" structs:"auto_rotate_before" mapstructure:"auto_rotate_before"`
TokenCreatedAt time.Time `json:"token_created_at" structs:"token_created_at" mapstructure:"token_created_at"`
TokenExpiresAt time.Time `json:"token_expires_at" structs:"token_expires_at" mapstructure:"token_expires_at"`
Scopes []string `json:"scopes" structs:"scopes" mapstructure:"scopes"`
Type Type `json:"type" structs:"type" mapstructure:"type"`
Type Type `json:"type" structs:"type" mapstructure:"type" validate:"gitlab-type"`
}

func (e EntryConfig) Response() *logical.Response {
func (e *EntryConfig) Merge(data *framework.FieldData) (warnings []string, changes map[string]string, err error) {
var er error
if data == nil {
return warnings, changes, multierror.Append(fmt.Errorf("data: %w", ErrNilValue))
}

if err = data.Validate(); err != nil {
return warnings, changes, multierror.Append(err)
}

changes = make(map[string]string)

if val, ok := data.GetOk("auto_rotate_token"); ok {
e.AutoRotateToken = val.(bool)
changes["auto_rotate_token"] = strconv.FormatBool(e.AutoRotateToken)
}

if typ, ok := data.GetOk("type"); ok {
var pType Type
if pType, er = TypeParse(typ.(string)); er != nil {
err = multierror.Append(err, er)
} else {
e.Type = pType
changes["type"] = pType.String()
}
}

if _, ok := data.GetOk("auto_rotate_before"); ok {
w, er := e.updateAutoRotateBefore(data)
if er != nil {
err = multierror.Append(err, er.Errors...)
} else {
changes["auto_rotate_before"] = e.AutoRotateBefore.String()
}
warnings = append(warnings, w...)
}

if val, ok := data.GetOk("base_url"); ok && len(val.(string)) > 0 {
e.BaseURL = val.(string)
changes["base_url"] = e.BaseURL
}

if val, ok := data.GetOk("token"); ok && len(val.(string)) > 0 {
e.Token = val.(string)
changes["token"] = strings.Repeat("*", len(e.Token))
}

return warnings, changes, err
}

func (e *EntryConfig) updateAutoRotateBefore(data *framework.FieldData) (warnings []string, err *multierror.Error) {
if val, ok := data.GetOk("auto_rotate_before"); ok {
atr, _ := convertToInt(val)
if atr > int(DefaultAutoRotateBeforeMaxTTL.Seconds()) {
err = multierror.Append(err, fmt.Errorf("auto_rotate_token can not be bigger than %s: %w", DefaultAutoRotateBeforeMaxTTL, ErrInvalidValue))
} else if atr <= int(DefaultAutoRotateBeforeMinTTL.Seconds())-1 {
err = multierror.Append(err, fmt.Errorf("auto_rotate_token can not be less than %s: %w", DefaultAutoRotateBeforeMinTTL, ErrInvalidValue))
} else {
e.AutoRotateBefore = time.Duration(atr) * time.Second
}
} else {
e.AutoRotateBefore = DefaultAutoRotateBeforeMinTTL
warnings = append(warnings, fmt.Sprintf("auto_rotate_token not specified setting to %s", DefaultAutoRotateBeforeMinTTL))
}
return warnings, err
}

func (e *EntryConfig) UpdateFromFieldData(data *framework.FieldData) (warnings []string, err error) {
if data == nil {
return warnings, multierror.Append(fmt.Errorf("data: %w", ErrNilValue))
}

if err = data.Validate(); err != nil {
return warnings, multierror.Append(err)
}

var er error
e.AutoRotateToken = data.Get("auto_rotate_token").(bool)

if token, ok := data.GetOk("token"); ok && len(token.(string)) > 0 {
e.Token = token.(string)
} else {
err = multierror.Append(err, fmt.Errorf("token: %w", ErrFieldRequired))
}

if typ, ok := data.GetOk("type"); ok {
if e.Type, er = TypeParse(typ.(string)); er != nil {
err = multierror.Append(err, er)
}
} else {
err = multierror.Append(err, fmt.Errorf("gitlab type: %w", ErrFieldRequired))
}

if baseUrl, ok := data.GetOk("base_url"); ok && len(baseUrl.(string)) > 0 {
e.BaseURL = baseUrl.(string)
} else {
err = multierror.Append(err, fmt.Errorf("base_url: %w", ErrFieldRequired))
}

{
w, er := e.updateAutoRotateBefore(data)
if er != nil {
err = multierror.Append(err, er.Errors...)
}
warnings = append(warnings, w...)
}

return warnings, err
}

func (e *EntryConfig) Response() *logical.Response {
return &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{},
Expand All @@ -35,7 +148,7 @@ func (e EntryConfig) Response() *logical.Response {
}
}

func (e EntryConfig) LogicalResponseData() map[string]any {
func (e *EntryConfig) LogicalResponseData() map[string]any {
var tokenExpiresAt, tokenCreatedAt = "", ""
if !e.TokenExpiresAt.IsZero() {
tokenExpiresAt = e.TokenExpiresAt.Format(time.RFC3339)
Expand All @@ -57,33 +170,25 @@ func (e EntryConfig) LogicalResponseData() map[string]any {
}
}

func getConfig(ctx context.Context, s logical.Storage) (*EntryConfig, error) {
func getConfig(ctx context.Context, s logical.Storage) (cfg *EntryConfig, err error) {
if s == nil {
return nil, fmt.Errorf("%w: local.Storage", ErrNilValue)
}
entry, err := s.Get(ctx, PathConfigStorage)
if err != nil {
return nil, err
var entry *logical.StorageEntry
if entry, err = s.Get(ctx, PathConfigStorage); err == nil {
if entry == nil {
return nil, nil
}
cfg = new(EntryConfig)
_ = entry.DecodeJSON(cfg)
}

if entry == nil {
return nil, nil
}

cfg := new(EntryConfig)
if err := entry.DecodeJSON(cfg); err != nil {
return nil, err
}
return cfg, nil
return cfg, err
}

func saveConfig(ctx context.Context, config EntryConfig, s logical.Storage) error {
var err error
func saveConfig(ctx context.Context, config EntryConfig, s logical.Storage) (err error) {
var storageEntry *logical.StorageEntry
storageEntry, err = logical.StorageEntryJSON(PathConfigStorage, config)
if err != nil {
return nil
if storageEntry, err = logical.StorageEntryJSON(PathConfigStorage, config); err == nil {
err = s.Put(ctx, storageEntry)
}

return s.Put(ctx, storageEntry)
return err
}
Loading

0 comments on commit d9128ff

Please sign in to comment.