feat(vault-tui): implement KV client and service for managing secrets
- Added internal/vault/client.go for creating a Vault client with configuration settings. - Introduced internal/vault/errors.go to classify Vault API errors for better UI handling. - Created internal/vault/kv.go to manage KV secrets, including listing, reading, writing, and deleting operations. - Implemented internal/vault/mounts.go to list and describe secret engine mounts. - Developed internal/vault/service.go to provide a unified entry point for Vault operations. - Added internal/vault/kv_test.go for comprehensive testing of KV operations. - Introduced internal/ui/toast.go for transient notifications in the UI. - Added renovate.json for dependency management and updates.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/natefinch/atomic"
|
||||
)
|
||||
|
||||
// profileStore keeps one token per profile under the XDG state directory.
|
||||
// It exists because vault-cli storage is single-valued (~/.vault-token):
|
||||
// logging into "prod" would silently clobber "dev". Uses the same
|
||||
// write-then-atomic-rename pattern as Vault's own InternalTokenHelper.
|
||||
type profileStore struct {
|
||||
path string
|
||||
}
|
||||
|
||||
// NewProfileStore returns a Store scoped to one profile name, rooted at
|
||||
// stateDir (see config.StateDir), e.g. stateDir/tokens/<profile>.token.
|
||||
func NewProfileStore(stateDir, profile string) Store {
|
||||
return profileStore{path: filepath.Join(stateDir, "tokens", sanitize(profile)+".token")}
|
||||
}
|
||||
|
||||
// NewFileStore is a profileStore pinned to an explicit path, used for
|
||||
// token.file in the config.
|
||||
func NewFileStore(path string) Store {
|
||||
return profileStore{path: path}
|
||||
}
|
||||
|
||||
func sanitize(name string) string {
|
||||
return strings.NewReplacer("/", "_", "\\", "_", "..", "_").Replace(name)
|
||||
}
|
||||
|
||||
func (s profileStore) Kind() string { return "profile" }
|
||||
func (s profileStore) Location() string { return s.path }
|
||||
|
||||
func (s profileStore) Get(context.Context) (string, error) {
|
||||
b, err := os.ReadFile(s.path)
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(b)), nil
|
||||
}
|
||||
|
||||
func (s profileStore) Store(_ context.Context, tok string) error {
|
||||
dir := filepath.Dir(s.path)
|
||||
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||
return fmt.Errorf("creating token directory %q: %w", dir, err)
|
||||
}
|
||||
tmp := s.path + ".tmp"
|
||||
if err := os.WriteFile(tmp, []byte(tok), 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
return atomic.ReplaceFile(tmp, s.path)
|
||||
}
|
||||
|
||||
func (s profileStore) Erase(context.Context) error {
|
||||
if err := os.Remove(s.path); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user