2026-04-14 14:40:50 +00:00
|
|
|
package secretstore
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
|
2026-05-05 10:23:14 +00:00
|
|
|
"forge.lclr.dev/AI/mcp-framework/manifest"
|
2026-04-14 14:40:50 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type ManifestLoader func(startDir string) (manifest.File, string, error)
|
|
|
|
|
|
|
|
|
|
type ExecutableResolver func() (string, error)
|
|
|
|
|
|
|
|
|
|
type OpenFromManifestOptions struct {
|
2026-05-02 12:59:04 +00:00
|
|
|
ServiceName string
|
|
|
|
|
LookupEnv func(string) (string, bool)
|
|
|
|
|
KWalletAppID string
|
|
|
|
|
KWalletFolder string
|
|
|
|
|
BitwardenCommand string
|
|
|
|
|
BitwardenDebug bool
|
|
|
|
|
DisableBitwardenCache bool
|
|
|
|
|
Shell string
|
|
|
|
|
ManifestLoader ManifestLoader
|
|
|
|
|
ExecutableResolver ExecutableResolver
|
2026-04-14 14:40:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func OpenFromManifest(options OpenFromManifestOptions) (Store, error) {
|
2026-04-20 08:56:15 +00:00
|
|
|
manifestPolicy, err := resolveManifestPolicy(options)
|
2026-04-14 14:40:50 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Open(Options{
|
2026-05-02 12:59:04 +00:00
|
|
|
ServiceName: options.ServiceName,
|
|
|
|
|
BackendPolicy: manifestPolicy.Policy,
|
|
|
|
|
LookupEnv: options.LookupEnv,
|
|
|
|
|
KWalletAppID: options.KWalletAppID,
|
|
|
|
|
KWalletFolder: options.KWalletFolder,
|
|
|
|
|
BitwardenCommand: strings.TrimSpace(options.BitwardenCommand),
|
|
|
|
|
BitwardenDebug: options.BitwardenDebug,
|
|
|
|
|
DisableBitwardenCache: disableBitwardenCacheOption(options.DisableBitwardenCache, manifestPolicy.BitwardenCache),
|
|
|
|
|
Shell: strings.TrimSpace(options.Shell),
|
2026-04-14 14:40:50 +00:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func resolveManifestBackendPolicy(options OpenFromManifestOptions) (BackendPolicy, error) {
|
2026-04-20 08:56:15 +00:00
|
|
|
resolution, err := resolveManifestPolicy(options)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
return resolution.Policy, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type manifestPolicyResolution struct {
|
2026-05-02 12:59:04 +00:00
|
|
|
Policy BackendPolicy
|
|
|
|
|
Source string
|
|
|
|
|
BitwardenCache bool
|
2026-04-20 08:56:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func resolveManifestPolicy(options OpenFromManifestOptions) (manifestPolicyResolution, error) {
|
2026-04-14 14:40:50 +00:00
|
|
|
manifestLoader := options.ManifestLoader
|
|
|
|
|
if manifestLoader == nil {
|
|
|
|
|
manifestLoader = manifest.LoadDefault
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
executableResolver := options.ExecutableResolver
|
|
|
|
|
if executableResolver == nil {
|
|
|
|
|
executableResolver = os.Executable
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
executablePath, err := executableResolver()
|
|
|
|
|
if err != nil {
|
2026-04-20 08:56:15 +00:00
|
|
|
return manifestPolicyResolution{}, fmt.Errorf("resolve executable path for manifest lookup: %w", err)
|
2026-04-14 14:40:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
startDir := filepath.Dir(strings.TrimSpace(executablePath))
|
|
|
|
|
if strings.TrimSpace(startDir) == "" {
|
|
|
|
|
startDir = "."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file, manifestPath, err := manifestLoader(startDir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, os.ErrNotExist) {
|
2026-04-20 08:56:15 +00:00
|
|
|
return manifestPolicyResolution{
|
2026-05-02 12:59:04 +00:00
|
|
|
Policy: BackendAuto,
|
|
|
|
|
Source: "",
|
|
|
|
|
BitwardenCache: true,
|
2026-04-20 08:56:15 +00:00
|
|
|
}, nil
|
2026-04-14 14:40:50 +00:00
|
|
|
}
|
2026-04-20 08:56:15 +00:00
|
|
|
return manifestPolicyResolution{}, fmt.Errorf("load runtime manifest from %q: %w", startDir, err)
|
2026-04-14 14:40:50 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-02 12:59:04 +00:00
|
|
|
bitwardenCache := true
|
|
|
|
|
if file.SecretStore.BitwardenCache != nil {
|
|
|
|
|
bitwardenCache = *file.SecretStore.BitwardenCache
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-14 14:40:50 +00:00
|
|
|
if strings.TrimSpace(file.SecretStore.BackendPolicy) == "" {
|
2026-04-20 08:56:15 +00:00
|
|
|
return manifestPolicyResolution{
|
2026-05-02 12:59:04 +00:00
|
|
|
Policy: BackendAuto,
|
|
|
|
|
Source: strings.TrimSpace(manifestPath),
|
|
|
|
|
BitwardenCache: bitwardenCache,
|
2026-04-20 08:56:15 +00:00
|
|
|
}, nil
|
2026-04-14 14:40:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
policy, err := normalizeBackendPolicy(BackendPolicy(file.SecretStore.BackendPolicy))
|
|
|
|
|
if err != nil {
|
2026-04-20 08:56:15 +00:00
|
|
|
return manifestPolicyResolution{}, fmt.Errorf(
|
|
|
|
|
"invalid secret_store.backend_policy in manifest %q: %w",
|
|
|
|
|
strings.TrimSpace(manifestPath),
|
|
|
|
|
err,
|
|
|
|
|
)
|
2026-04-14 14:40:50 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-20 08:56:15 +00:00
|
|
|
return manifestPolicyResolution{
|
2026-05-02 12:59:04 +00:00
|
|
|
Policy: policy,
|
|
|
|
|
Source: strings.TrimSpace(manifestPath),
|
|
|
|
|
BitwardenCache: bitwardenCache,
|
2026-04-20 08:56:15 +00:00
|
|
|
}, nil
|
2026-04-14 14:40:50 +00:00
|
|
|
}
|
2026-05-02 12:59:04 +00:00
|
|
|
|
|
|
|
|
func disableBitwardenCacheOption(runtimeDisabled bool, manifestEnabled bool) bool {
|
|
|
|
|
return runtimeDisabled || !manifestEnabled
|
|
|
|
|
}
|