Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion configs/vui.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ ui:
theme: "default"
show_hidden_secrets: false

vaults:
profiles:
vault-token:
engine: "vault"
address: "http://localhost:8200"
auth_method: "token"
namespace: ""
auth_config:
token: "${VAULT_TOKEN}"

vault-ldap:
engine: "vault"
address: "http://localhost:8200"
auth_method: "ldap"
namespace: ""
Expand All @@ -23,6 +25,7 @@ vaults:
password: "${LDAP_PASSWORD}"

vault-aws:
engine: "vault"
address: "http://localhost:8200"
auth_method: "aws"
namespace: ""
Expand All @@ -33,6 +36,7 @@ vaults:
aws_session_token: "${AWS_SESSION_TOKEN}"

vault-k8s:
engine: "vault"
address: "http://localhost:8200"
auth_method: "kubernetes"
namespace: ""
Expand All @@ -43,13 +47,15 @@ vaults:
k8s_ttl: 3600

vault-oidc:
engine: "vault"
address: "http://localhost:8200"
auth_method: "oidc"
namespace: ""
auth_config:
oidc_role: "vui-oidc-role"

vault-jwt:
engine: "vault"
address: "http://localhost:8200"
auth_method: "jwt"
namespace: ""
Expand All @@ -58,6 +64,7 @@ vaults:
jwt: "${JWT}"

vault-userpass:
engine: "vault"
address: "http://localhost:8200"
auth_method: "userpass"
namespace: ""
Expand All @@ -66,6 +73,7 @@ vaults:
password: "${USERPASS_PASSWORD}"

vault-cert:
engine: "vault"
address: "https://localhost:8208"
cert_path: "./sandbox/files/vault.crt"
auth_method: "cert"
Expand All @@ -76,6 +84,7 @@ vaults:
cert_key_path: "./sandbox/vol/certs/client.key"

vault-azure-todo:
engine: "vault"
address: "http://localhost:8201"
auth_method: "azure"
namespace: ""
Expand Down
12 changes: 9 additions & 3 deletions internal/backend/profiles_interactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,18 @@ func (i *profileInteractor) ReloadConfiguration() error {
func (i *profileInteractor) initializeConnections() error {
factory := engines.NewEnginesFactory(i.logger)

for name, profile := range i.config.Vaults {
for name, profile := range i.config.Profiles {
p := profile

client, err := factory.SetupEngine("vault", &p) // TODO: add support for other engines
// Use the engine from the profile, default to "vault" for backward compatibility
engine := p.Engine
if engine == "" {
engine = "vault"
}

client, err := factory.SetupEngine(engine, &p)
if err != nil {
i.logger.Warnf("Failed to setup engine for profile '%s': %v", name, err)
i.logger.Warnf("Failed to setup engine '%s' for profile '%s': %v", engine, name, err)
continue
}

Expand Down
24 changes: 13 additions & 11 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import (

// Config represents the application configuration
type Config struct {
App AppConfig `mapstructure:"app"`
UI UIConfig `mapstructure:"ui"`
Vaults map[string]VaultProfile `mapstructure:"vaults"`
App AppConfig `mapstructure:"app"`
UI UIConfig `mapstructure:"ui"`
Profiles map[string]Profile `mapstructure:"profiles"`
}

// AppConfig contains general application settings
Expand All @@ -28,8 +28,9 @@ type UIConfig struct {
ShowHiddenSecrets bool `mapstructure:"show_hidden_secrets"`
}

// VaultProfile represents a vault connection profile
type VaultProfile struct {
// Profile represents a connection profile
type Profile struct {
Engine string `mapstructure:"engine"`
Address string `mapstructure:"address"`
CertPath string `mapstructure:"cert_path,omitempty"`
AuthMethod string `mapstructure:"auth_method"`
Expand Down Expand Up @@ -138,11 +139,12 @@ func setDefaults() {
viper.SetDefault("ui.theme", "default")
viper.SetDefault("ui.show_hidden_secrets", false)

// Vaults defaults
viper.SetDefault("vaults.local.address", "http://localhost:8200")
viper.SetDefault("vaults.local.auth_method", "token")
viper.SetDefault("vaults.local.namespace", "")
viper.SetDefault("vaults.local.auth_config.token", "${VAULT_TOKEN}")
// Profiles defaults
viper.SetDefault("profiles.local.engine", "vault")
viper.SetDefault("profiles.local.address", "http://localhost:8200")
viper.SetDefault("profiles.local.auth_method", "token")
viper.SetDefault("profiles.local.namespace", "")
viper.SetDefault("profiles.local.auth_config.token", "${VAULT_TOKEN}")
}

// Save saves the configuration to a file
Expand All @@ -157,7 +159,7 @@ func (c *Config) Save() error {
// Convert config back to viper format for saving
viper.Set("app", c.App)
viper.Set("ui", c.UI)
viper.Set("vaults", c.Vaults)
viper.Set("profiles", c.Profiles)

return viper.WriteConfigAs(configFile)
}
8 changes: 5 additions & 3 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@ func TestLoad(t *testing.T) {
assert.Equal(t, "info", config.App.LogLevel)
assert.Equal(t, false, config.UI.ShowHiddenSecrets)
assert.Equal(t, "default", config.UI.Theme)
assert.Equal(t, "http://localhost:8200", config.Vaults["local"].Address)
assert.Equal(t, "token", config.Vaults["local"].AuthMethod)
assert.Equal(t, "http://localhost:8200", config.Profiles["local"].Address)
assert.Equal(t, "token", config.Profiles["local"].AuthMethod)
assert.Equal(t, "vault", config.Profiles["local"].Engine)
}

func TestConfigSave(t *testing.T) {
config := &Config{
UI: UIConfig{
Theme: "light",
},
Vaults: map[string]VaultProfile{
Profiles: map[string]Profile{
"default": {
Engine: "vault",
Address: "https://test.vault.com",
AuthMethod: "token",
},
Expand Down
2 changes: 1 addition & 1 deletion internal/engines/engines_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func NewEnginesFactory(logger *logrus.Logger) *EnginesFactory {
}
}

func (f *EnginesFactory) SetupEngine(name string, profile *config.VaultProfile) (SecretEngine, error) {
func (f *EnginesFactory) SetupEngine(name string, profile *config.Profile) (SecretEngine, error) {
switch name {
case "vault":
return vault.NewVaultClient(f.logger, profile)
Expand Down
6 changes: 4 additions & 2 deletions internal/engines/engines_factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ func TestEnginesFactory_SetupEngine(t *testing.T) {
assert.NotNil(t, factory)

t.Run("vault", func(t *testing.T) {
engine, err := factory.SetupEngine("vault", &config.VaultProfile{
engine, err := factory.SetupEngine("vault", &config.Profile{
Engine: "vault",
Address: "http://localhost:8200",
AuthMethod: "token",
})
Expand All @@ -23,7 +24,8 @@ func TestEnginesFactory_SetupEngine(t *testing.T) {
})

t.Run("unknown", func(t *testing.T) {
engine, err := factory.SetupEngine("unknown", &config.VaultProfile{
engine, err := factory.SetupEngine("unknown", &config.Profile{
Engine: "unknown",
Address: "http://localhost:8200",
})
assert.Error(t, err)
Expand Down
2 changes: 1 addition & 1 deletion internal/engines/vault/auth/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

// authenticateWithAWS authenticates using AWS
func (am *AuthManager) authenticateWithAWS(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) authenticateWithAWS(client *api.Client, profile *config.Profile) error {
accessKeyID := profile.AuthConfig.AWSAccessKeyID
if accessKeyID == "" {
return fmt.Errorf("aws_access_key_id is required for AWS authentication")
Expand Down
2 changes: 1 addition & 1 deletion internal/engines/vault/auth/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

// authenticateWithAzure authenticates using Azure
func (am *AuthManager) authenticateWithAzure(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) authenticateWithAzure(client *api.Client, profile *config.Profile) error {
role := profile.AuthConfig.AzureRole
if role == "" {
return fmt.Errorf("azure_role is required for Azure authentication")
Expand Down
2 changes: 1 addition & 1 deletion internal/engines/vault/auth/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

// authenticateWithCert authenticates using certificates
func (am *AuthManager) authenticateWithCert(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) authenticateWithCert(client *api.Client, profile *config.Profile) error {
certName := profile.AuthConfig.CertName
if certName == "" {
return fmt.Errorf("cert_name is required for cert authentication")
Expand Down
4 changes: 2 additions & 2 deletions internal/engines/vault/auth/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

// authenticateWithGCP authenticates using GCP
func (am *AuthManager) authenticateWithGCP(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) authenticateWithGCP(client *api.Client, profile *config.Profile) error {
role := profile.AuthConfig.GCPRole
if role == "" {
return fmt.Errorf("gcp_role is required for GCP authentication")
Expand Down Expand Up @@ -45,7 +45,7 @@ func (am *AuthManager) authenticateWithGCP(client *api.Client, profile *config.V
}

// getGCPCredentials gets GCP credentials from various sources
func (am *AuthManager) getGCPCredentials(profile *config.VaultProfile) (string, error) {
func (am *AuthManager) getGCPCredentials(profile *config.Profile) (string, error) {
// Check if credentials are provided directly
if credentials := profile.AuthConfig.GCPCredentials; credentials != "" {
return credentials, nil
Expand Down
2 changes: 1 addition & 1 deletion internal/engines/vault/auth/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

// authenticateWithJWT authenticates using JWT
func (am *AuthManager) authenticateWithJWT(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) authenticateWithJWT(client *api.Client, profile *config.Profile) error {
role := profile.AuthConfig.JWTRole
if role == "" {
return fmt.Errorf("jwt_role is required for JWT authentication")
Expand Down
4 changes: 2 additions & 2 deletions internal/engines/vault/auth/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
)

// authenticateWithKubernetes authenticates using Kubernetes
func (am *AuthManager) authenticateWithKubernetes(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) authenticateWithKubernetes(client *api.Client, profile *config.Profile) error {
role := profile.AuthConfig.K8sRole
if role == "" {
return fmt.Errorf("k8s_role is required for Kubernetes authentication")
Expand Down Expand Up @@ -58,7 +58,7 @@ func (am *AuthManager) authenticateWithKubernetes(client *api.Client, profile *c
}

// getKubernetesToken gets Kubernetes service account token
func (am *AuthManager) getKubernetesToken(profile *config.VaultProfile) (string, error) {
func (am *AuthManager) getKubernetesToken(profile *config.Profile) (string, error) {
homeDir, _ := os.UserHomeDir()

var (
Expand Down
2 changes: 1 addition & 1 deletion internal/engines/vault/auth/ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

// authenticateWithLDAP authenticates using LDAP
func (am *AuthManager) authenticateWithLDAP(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) authenticateWithLDAP(client *api.Client, profile *config.Profile) error {
username := profile.AuthConfig.Username
if username == "" {
return fmt.Errorf("username is required for LDAP authentication")
Expand Down
2 changes: 1 addition & 1 deletion internal/engines/vault/auth/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (am *AuthManager) VerifyAuthentication(client *api.Client) error {
}

// Authenticate authenticates a client using the specified profile
func (am *AuthManager) Authenticate(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) Authenticate(client *api.Client, profile *config.Profile) error {
switch profile.AuthMethod {
case "token":
return am.authenticateWithToken(client, profile)
Expand Down
2 changes: 1 addition & 1 deletion internal/engines/vault/auth/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/rvolykh/vui/internal/config"
)

func (am *AuthManager) authenticateWithOIDC(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) authenticateWithOIDC(client *api.Client, profile *config.Profile) error {
role := profile.AuthConfig.OIDCRole
if role == "" {
return fmt.Errorf("oidc_role is required for OIDC authentication")
Expand Down
2 changes: 1 addition & 1 deletion internal/engines/vault/auth/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

// authenticateWithToken authenticates using a token
func (am *AuthManager) authenticateWithToken(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) authenticateWithToken(client *api.Client, profile *config.Profile) error {
token := profile.AuthConfig.Token
if token == "" {
return fmt.Errorf("token is required for token authentication")
Expand Down
2 changes: 1 addition & 1 deletion internal/engines/vault/auth/userpass.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

// authenticateWithUserpass authenticates using userpass
func (am *AuthManager) authenticateWithUserpass(client *api.Client, profile *config.VaultProfile) error {
func (am *AuthManager) authenticateWithUserpass(client *api.Client, profile *config.Profile) error {
username := profile.AuthConfig.Username
if username == "" {
return fmt.Errorf("username is required for userpass authentication")
Expand Down
4 changes: 2 additions & 2 deletions internal/engines/vault/vault_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import (

type VaultClient struct {
apiClient *api.Client
profile *config.VaultProfile
profile *config.Profile
logger *logrus.Logger
}

func NewVaultClient(logger *logrus.Logger, profile *config.VaultProfile) (*VaultClient, error) {
func NewVaultClient(logger *logrus.Logger, profile *config.Profile) (*VaultClient, error) {
apiConfig := api.DefaultConfig()
apiConfig.Address = profile.Address

Expand Down
3 changes: 2 additions & 1 deletion internal/engines/vault/vault_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ func TestVaultClient(t *testing.T) {
logger := logrus.New()
logger.SetLevel(logrus.ErrorLevel)

profile := &config.VaultProfile{
profile := &config.Profile{
Engine: "vault",
Address: "http://localhost:8200",
}

Expand Down
4 changes: 2 additions & 2 deletions internal/ui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,12 @@ func (a *App) refresh() {
a.layout.Refresh()
}

// switchVault shows the vault profiles screen for switching vaults
// switchVault shows the profiles screen for switching profiles
func (a *App) switchVault() {
a.showVaultProfiles()
}

// showVaultProfiles shows the vault profiles screen
// showVaultProfiles shows the profiles screen
func (a *App) showVaultProfiles() {
// Mark that we're on the profiles screen
a.onProfilesScreen = true
Expand Down
3 changes: 2 additions & 1 deletion internal/ui/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ func TestNewApp(t *testing.T) {
// Create test configuration
cfg := &config.Config{
App: config.AppConfig{},
Vaults: map[string]config.VaultProfile{
Profiles: map[string]config.Profile{
"test": {
Engine: "vault",
Address: "http://localhost:8200",
AuthMethod: "token",
},
Expand Down
6 changes: 4 additions & 2 deletions internal/ui/layout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import (
func TestLayoutOfflineMode(t *testing.T) {
// Create test configuration
cfg := &config.Config{
Vaults: map[string]config.VaultProfile{
Profiles: map[string]config.Profile{
"default": {
Engine: "vault",
Address: "http://localhost:8200",
AuthMethod: "token",
},
Expand Down Expand Up @@ -41,8 +42,9 @@ func TestLayoutOfflineMode(t *testing.T) {
func TestLayoutStructure(t *testing.T) {
// Test that the layout structure is correct
cfg := &config.Config{
Vaults: map[string]config.VaultProfile{
Profiles: map[string]config.Profile{
"default": {
Engine: "vault",
Address: "http://localhost:8200",
AuthMethod: "token",
},
Expand Down
Loading