212 lines
5.7 KiB
Go
212 lines
5.7 KiB
Go
package secretstore
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const DefaultManifestSource = "default:auto (manifest not found)"
|
|
|
|
type DescribeRuntimeOptions struct {
|
|
ServiceName string
|
|
LookupEnv func(string) (string, bool)
|
|
KWalletAppID string
|
|
KWalletFolder string
|
|
BitwardenCommand string
|
|
BitwardenDebug bool
|
|
DisableBitwardenCache bool
|
|
Shell string
|
|
ManifestLoader ManifestLoader
|
|
ExecutableResolver ExecutableResolver
|
|
}
|
|
|
|
type RuntimeDescription struct {
|
|
ManifestSource string
|
|
DeclaredPolicy BackendPolicy
|
|
EffectivePolicy BackendPolicy
|
|
DisplayName string
|
|
Ready bool
|
|
ReadyError error
|
|
}
|
|
|
|
type PreflightStatus string
|
|
|
|
const (
|
|
PreflightStatusReady PreflightStatus = "ready"
|
|
PreflightStatusFail PreflightStatus = "fail"
|
|
)
|
|
|
|
type PreflightOptions = DescribeRuntimeOptions
|
|
|
|
type PreflightReport struct {
|
|
Status PreflightStatus
|
|
Summary string
|
|
Remediation string
|
|
Runtime RuntimeDescription
|
|
}
|
|
|
|
func DescribeRuntime(options DescribeRuntimeOptions) (RuntimeDescription, error) {
|
|
resolution, err := resolveManifestPolicy(OpenFromManifestOptions{
|
|
ServiceName: options.ServiceName,
|
|
LookupEnv: options.LookupEnv,
|
|
KWalletAppID: options.KWalletAppID,
|
|
KWalletFolder: options.KWalletFolder,
|
|
BitwardenCommand: options.BitwardenCommand,
|
|
DisableBitwardenCache: options.DisableBitwardenCache,
|
|
Shell: options.Shell,
|
|
ManifestLoader: options.ManifestLoader,
|
|
ExecutableResolver: options.ExecutableResolver,
|
|
})
|
|
if err != nil {
|
|
return RuntimeDescription{}, err
|
|
}
|
|
|
|
desc := RuntimeDescription{
|
|
ManifestSource: manifestSourceLabel(resolution.Source),
|
|
DeclaredPolicy: resolution.Policy,
|
|
EffectivePolicy: resolution.Policy,
|
|
DisplayName: BackendDisplayName(resolution.Policy),
|
|
}
|
|
|
|
store, openErr := Open(Options{
|
|
ServiceName: options.ServiceName,
|
|
BackendPolicy: resolution.Policy,
|
|
LookupEnv: options.LookupEnv,
|
|
KWalletAppID: options.KWalletAppID,
|
|
KWalletFolder: options.KWalletFolder,
|
|
BitwardenCommand: options.BitwardenCommand,
|
|
BitwardenDebug: options.BitwardenDebug,
|
|
DisableBitwardenCache: disableBitwardenCacheOption(options.DisableBitwardenCache, resolution.BitwardenCache),
|
|
Shell: options.Shell,
|
|
})
|
|
if openErr != nil {
|
|
desc.Ready = false
|
|
desc.ReadyError = openErr
|
|
return desc, nil
|
|
}
|
|
|
|
desc.Ready = true
|
|
if effective := EffectiveBackendPolicy(store); strings.TrimSpace(string(effective)) != "" {
|
|
desc.EffectivePolicy = effective
|
|
desc.DisplayName = BackendDisplayName(effective)
|
|
}
|
|
if desc.EffectivePolicy == BackendBitwardenCLI {
|
|
if err := verifyBitwardenCLIReady(Options{
|
|
BitwardenCommand: options.BitwardenCommand,
|
|
BitwardenDebug: options.BitwardenDebug,
|
|
LookupEnv: options.LookupEnv,
|
|
Shell: options.Shell,
|
|
}); err != nil {
|
|
desc.Ready = false
|
|
desc.ReadyError = err
|
|
}
|
|
}
|
|
|
|
return desc, nil
|
|
}
|
|
|
|
func PreflightFromManifest(options PreflightOptions) (PreflightReport, error) {
|
|
desc, err := DescribeRuntime(options)
|
|
if err != nil {
|
|
return PreflightReport{}, err
|
|
}
|
|
|
|
if desc.Ready {
|
|
return PreflightReport{
|
|
Status: PreflightStatusReady,
|
|
Summary: "secret backend is ready",
|
|
Runtime: desc,
|
|
}, nil
|
|
}
|
|
|
|
summary, remediation := summarizePreflightFailure(desc.ReadyError)
|
|
return PreflightReport{
|
|
Status: PreflightStatusFail,
|
|
Summary: summary,
|
|
Remediation: remediation,
|
|
Runtime: desc,
|
|
}, nil
|
|
}
|
|
|
|
func BackendDisplayName(policy BackendPolicy) string {
|
|
switch policy {
|
|
case BackendBitwardenCLI:
|
|
return "Bitwarden CLI"
|
|
case BackendEnvOnly:
|
|
return "Environment variables"
|
|
case BackendKWalletOnly:
|
|
return "KWallet"
|
|
case BackendAuto:
|
|
return "automatic backend selection"
|
|
case BackendKeyringAny:
|
|
return BackendName()
|
|
default:
|
|
trimmed := strings.TrimSpace(string(policy))
|
|
if trimmed == "" {
|
|
return "unknown backend"
|
|
}
|
|
return trimmed
|
|
}
|
|
}
|
|
|
|
func FormatBackendStatus(desc RuntimeDescription) string {
|
|
source := manifestSourceLabel(desc.ManifestSource)
|
|
display := strings.TrimSpace(desc.DisplayName)
|
|
if display == "" {
|
|
display = BackendDisplayName(desc.EffectivePolicy)
|
|
}
|
|
|
|
effective := desc.EffectivePolicy
|
|
if strings.TrimSpace(string(effective)) == "" {
|
|
effective = desc.DeclaredPolicy
|
|
}
|
|
|
|
parts := []string{
|
|
fmt.Sprintf("declared=%s", normalizeStatusPolicy(desc.DeclaredPolicy)),
|
|
fmt.Sprintf("effective=%s", normalizeStatusPolicy(effective)),
|
|
fmt.Sprintf("display=%s", display),
|
|
fmt.Sprintf("ready=%t", desc.Ready),
|
|
fmt.Sprintf("source=%s", source),
|
|
}
|
|
if desc.ReadyError != nil {
|
|
parts = append(parts, fmt.Sprintf("error=%s", strings.TrimSpace(desc.ReadyError.Error())))
|
|
}
|
|
|
|
return strings.Join(parts, " ")
|
|
}
|
|
|
|
func summarizePreflightFailure(err error) (string, string) {
|
|
if err == nil {
|
|
return "secret backend is unavailable", ""
|
|
}
|
|
|
|
switch {
|
|
case errors.Is(err, ErrBWNotLoggedIn):
|
|
return "bitwarden login is required", strings.TrimSpace(err.Error())
|
|
case errors.Is(err, ErrBWLocked):
|
|
return "bitwarden vault is locked or BW_SESSION is missing", strings.TrimSpace(err.Error())
|
|
case errors.Is(err, ErrBWUnavailable):
|
|
return "bitwarden CLI is unavailable", strings.TrimSpace(err.Error())
|
|
case errors.Is(err, ErrBackendUnavailable):
|
|
return "secret backend is unavailable", strings.TrimSpace(err.Error())
|
|
default:
|
|
return "secret backend preflight failed", strings.TrimSpace(err.Error())
|
|
}
|
|
}
|
|
|
|
func manifestSourceLabel(source string) string {
|
|
trimmed := strings.TrimSpace(source)
|
|
if trimmed == "" {
|
|
return DefaultManifestSource
|
|
}
|
|
return trimmed
|
|
}
|
|
|
|
func normalizeStatusPolicy(policy BackendPolicy) string {
|
|
trimmed := strings.TrimSpace(string(policy))
|
|
if trimmed == "" {
|
|
return string(BackendAuto)
|
|
}
|
|
return trimmed
|
|
}
|