// Package provider defines the read-only interface every secret backend // implements, and the types the UI works with. // // All backends map onto the same hierarchy: // // Source Azure subscription | Databricks workspace profile // Container Key Vault | secret scope // Object secret, key or certificate | secret package provider import ( "context" "errors" "fmt" "time" ) // Kind identifies a backend. type Kind string const ( KindAzure Kind = "Azure" KindDatabricks Kind = "Databricks" // KindRepo is a git repository's encrypted secret files (SOPS). KindRepo Kind = "Repo" ) // AuthError reports that the user must (re)authenticate with a backend, for // example because their az CLI login has expired. type AuthError struct { Kind Kind // Account names the identity when a provider has several, e.g. // "Azure · admin.alice"; empty otherwise. Account string // Login is the command that signs the user in, e.g. ["az", "login"]. Login []string Err error } func (e *AuthError) Error() string { return fmt.Sprintf("%s: not signed in or session expired: %v", e.Who(), e.Err) } // Who is the account, or the kind if there is just one. func (e *AuthError) Who() string { if e.Account != "" { return e.Account } return string(e.Kind) } func (e *AuthError) Unwrap() error { return e.Err } // ErrUnsupported is returned by optional operations a backend cannot perform, // such as listing versions of a Databricks secret. var ErrUnsupported = errors.New("not supported by this provider") // Source is a top-level account: an Azure subscription or a Databricks // workspace profile. type Source struct { Kind Kind // Provider is the ID of the provider instance the source came from. // The app sets it; it routes requests when several providers share a // Kind, e.g. one per Azure account. Provider string // ProviderLabel is that provider's Label, also set by the app. ProviderLabel string ID string Name string Detail string // e.g. tenant or workspace host URL string // web page for the source, if any // Here marks the source lazysecrets was started in (the current git // repo): listed first, starred or not, and labelled. Here bool } // ObjectType is the kind of object stored in a container. type ObjectType string const ( TypeSecret ObjectType = "Secret" TypeKey ObjectType = "Key" TypeCertificate ObjectType = "Certificate" ) // Container holds objects: a Key Vault or a Databricks secret scope. type Container struct { Source Source ID string Name string Detail string // e.g. vault URL, or "Azure Key Vault-backed: kv-name" // Types lists the object types the container holds; empty means // secrets only. Types []ObjectType URL string // web page for the container, if any } // ObjectTypes returns the container's object types, defaulting to secrets. func (c Container) ObjectTypes() []ObjectType { if len(c.Types) == 0 { return []ObjectType{TypeSecret} } return c.Types } // Attr is a type-specific metadata field, shown in provider order. type Attr struct { Label string Value string } // Object describes a secret, key or certificate without its value. type Object struct { Container Container Type ObjectType ID string Name string Version string Enabled *bool Updated time.Time Expires time.Time ContentType string Tags map[string]string // Attrs holds type-specific metadata, e.g. key type and size, or a // certificate's subject, issuer and thumbprint. Attrs []Attr // Reference is how the object is referenced from config, e.g. // "@Microsoft.KeyVault(SecretUri=...)" or "{{secrets/scope/key}}". Reference string URL string // web page for the object (or its list), if any } // Provider is a read-only secret backend. Every method may block on the // network and must honour ctx cancellation. type Provider interface { Kind() Kind // ID identifies the instance, stable across runs, e.g. "Azure" or // "Azure:user@example.com/tenant". ID() string // Label names the instance in the UI, e.g. "Azure" or "Azure · user". Label() string Sources(ctx context.Context) ([]Source, error) Containers(ctx context.Context, src Source) ([]Container, error) // Objects lists objects of type t, one of c.ObjectTypes(). It returns // metadata only; values are fetched with Value. Objects(ctx context.Context, c Container, t ObjectType) ([]Object, error) // Describe returns o with details that listing doesn't include, such as // a key's type or a certificate's subject. It never fetches secret // values; it may return o unchanged. Describe(ctx context.Context, o Object) (Object, error) // Versions may return ErrUnsupported. Versions(ctx context.Context, o Object) ([]Object, error) // Value returns a secret's value, or the public material of a key (PEM // public key) or certificate (PEM certificate). Private key material is // never returned for keys. Value(ctx context.Context, o Object) (SecretValue, error) } // Current is implemented by providers that know the source lazysecrets // was started in (the current git repo) and can find it faster than // Sources lists everything, so it shows first. type Current interface { Current(ctx context.Context) (Source, bool) } // Locator is implemented by providers that can find where a container lives, // to open links. address is a vault host (kv.vault.azure.net) or a // workspace host. It returns the ID of the source holding it. type Locator interface { Locate(ctx context.Context, address string) (sourceID string, ok bool, err error) } // Files is implemented by providers whose containers are files on disk // (SOPS and .env files in repos), so they can be opened in an editor. type Files interface { // File is where c is, and for o (nil for the whole file) the line its // key is on. File(c Container, o *Object) (File, error) } // File is a container's file, to open. type File struct { Path string Line int // 1-based; 0 for the top Sops string // the sops command to edit it with; empty for a plain file }