gopasspw--gopass
492 行
15 KiB
Go
492 行
15 KiB
Go
// Package ctxutil provides a set of functions to manage context values
|
|
// in a gopass application. It allows to set and get values in the context.
|
|
package ctxutil
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/urfave/cli/v3"
|
|
)
|
|
|
|
type contextKey int
|
|
|
|
const (
|
|
// ctxKeyExecConfig holds the consolidated ExecConfig struct.
|
|
ctxKeyExecConfig contextKey = iota
|
|
)
|
|
|
|
// ExecConfig holds all boolean/string/struct configuration flags that are
|
|
// threaded through the call stack. Using a single typed struct instead of
|
|
// individual context keys makes dependencies explicit at the type level and
|
|
// eliminates the boilerplate of per-field With*/Is*/Has*/Get* helpers.
|
|
type ExecConfig struct {
|
|
// Terminal indicates whether output is going to a terminal.
|
|
Terminal *bool
|
|
// Interactive indicates whether user interaction is possible.
|
|
Interactive *bool
|
|
// Stdin indicates whether data is being piped on stdin.
|
|
Stdin *bool
|
|
// GitCommit indicates whether changes should be committed to git.
|
|
GitCommit *bool
|
|
// AlwaysYes indicates that all prompts should be answered with yes.
|
|
AlwaysYes *bool
|
|
// GitInit indicates whether a new git repository should be initialized.
|
|
GitInit *bool
|
|
// Force indicates that operations that would otherwise fail should be forced.
|
|
Force *bool
|
|
// NoNetwork indicates that no network operations should be performed.
|
|
NoNetwork *bool
|
|
// ShowParsing indicates whether parsing errors should be shown.
|
|
ShowParsing *bool
|
|
// Hidden indicates whether output should be hidden.
|
|
Hidden *bool
|
|
// FollowRef indicates whether symlink references should be followed.
|
|
FollowRef *bool
|
|
// Alias is the mount alias for the current store.
|
|
Alias string
|
|
// Username is the configured user name.
|
|
Username string
|
|
// Email is the configured e-mail address.
|
|
Email string
|
|
// CommitMessage is the structured git commit message.
|
|
CommitMessage *HeadedText
|
|
// CommitTimestamp is an optional fixed timestamp for reproducible commits.
|
|
CommitTimestamp *time.Time
|
|
// AgePassphrase is an optional passphrase used for the age identity file.
|
|
// It is set from the GOPASS_AGE_PASSWORD environment variable and is used
|
|
// to provide a password for the age encryption backend without interactive
|
|
// prompts (e.g. in CI/CD pipelines or tests).
|
|
AgePassphrase string
|
|
// SetupRemote is set to the remote URL when a git remote is specified during
|
|
// setup, signalling that the automatic initial commit should be suppressed.
|
|
SetupRemote string
|
|
}
|
|
|
|
// WithExecConfig stores e in ctx and returns the updated context.
|
|
func WithExecConfig(ctx context.Context, e ExecConfig) context.Context {
|
|
return context.WithValue(ctx, ctxKeyExecConfig, e)
|
|
}
|
|
|
|
// GetExecConfig retrieves the ExecConfig from ctx, returning a zero value if
|
|
// none has been set.
|
|
func GetExecConfig(ctx context.Context) ExecConfig {
|
|
if e, ok := ctx.Value(ctxKeyExecConfig).(ExecConfig); ok {
|
|
return e
|
|
}
|
|
|
|
return ExecConfig{}
|
|
}
|
|
|
|
// withBool is a helper that clones the ExecConfig, sets the field pointed to
|
|
// by set, and stores the result back into ctx.
|
|
func withBool(ctx context.Context, set func(*ExecConfig, *bool), v bool) context.Context {
|
|
e := GetExecConfig(ctx)
|
|
set(&e, &v)
|
|
|
|
return WithExecConfig(ctx, e)
|
|
}
|
|
|
|
// WithGlobalFlags parses any global flags from the cli context and returns
|
|
// a regular context. It handles the --yes flag and sets the appropriate
|
|
// context value.
|
|
func WithGlobalFlags(ctx context.Context, cmd *cli.Command) context.Context {
|
|
if cmd.Bool("yes") {
|
|
return WithAlwaysYes(ctx, true)
|
|
}
|
|
|
|
return ctx
|
|
}
|
|
|
|
// ProgressCallback is a callback for updating progress.
|
|
type ProgressCallback func()
|
|
|
|
// WithTerminal returns a context with an explicit value for whether or not we are
|
|
// in a terminal.
|
|
func WithTerminal(ctx context.Context, isTerm bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.Terminal = v }, isTerm)
|
|
}
|
|
|
|
// HasTerminal returns true if a value for Terminal has been set in this context.
|
|
func HasTerminal(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).Terminal != nil
|
|
}
|
|
|
|
// IsTerminal returns the value of terminal or the default (true).
|
|
func IsTerminal(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).Terminal; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// WithInteractive returns a context with an explicit value for whether or not we are
|
|
// in an interactive session.
|
|
func WithInteractive(ctx context.Context, isInteractive bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.Interactive = v }, isInteractive)
|
|
}
|
|
|
|
// HasInteractive returns true if a value for Interactive has been set in this context.
|
|
func HasInteractive(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).Interactive != nil
|
|
}
|
|
|
|
// IsInteractive returns the value of interactive or the default (true).
|
|
func IsInteractive(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).Interactive; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// WithStdin returns a context with the value for Stdin set. If true some input
|
|
// is available on Stdin (e.g. something is being piped into it).
|
|
func WithStdin(ctx context.Context, isStdin bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.Stdin = v }, isStdin)
|
|
}
|
|
|
|
// HasStdin returns true if a value for Stdin has been set in this context.
|
|
func HasStdin(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).Stdin != nil
|
|
}
|
|
|
|
// IsStdin returns the value of stdin, i.e. if it's true some data is being
|
|
// piped to stdin. If not set it returns the default value (false).
|
|
func IsStdin(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).Stdin; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// WithShowParsing returns a context with the value for ShowParsing set.
|
|
// This is used to control whether to show parsing errors.
|
|
func WithShowParsing(ctx context.Context, bv bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.ShowParsing = v }, bv)
|
|
}
|
|
|
|
// HasShowParsing returns true if a value for ShowParsing has been set in this context.
|
|
func HasShowParsing(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).ShowParsing != nil
|
|
}
|
|
|
|
// IsShowParsing returns the value of ShowParsing or the default (true).
|
|
func IsShowParsing(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).ShowParsing; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// WithGitCommit returns a context with the value of git commit set.
|
|
// If true, changes will be committed to git.
|
|
func WithGitCommit(ctx context.Context, bv bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.GitCommit = v }, bv)
|
|
}
|
|
|
|
// HasGitCommit returns true if a value for GitCommit has been set in this context.
|
|
func HasGitCommit(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).GitCommit != nil
|
|
}
|
|
|
|
// IsGitCommit returns the value of git commit or the default (true).
|
|
func IsGitCommit(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).GitCommit; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// IsFollowRef returns the value of follow-ref or the default (false).
|
|
// If true, symlinks will be followed.
|
|
func IsFollowRef(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).FollowRef; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// HasFollowRef returns true if a value for follow-ref has been set in this context.
|
|
func HasFollowRef(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).FollowRef != nil
|
|
}
|
|
|
|
// WithFollowRef returns a context with the value of follow-ref set.
|
|
func WithFollowRef(ctx context.Context, bv bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.FollowRef = v }, bv)
|
|
}
|
|
|
|
// WithAlwaysYes returns a context with the value of always yes set.
|
|
// If true, any prompts will be answered with yes.
|
|
func WithAlwaysYes(ctx context.Context, bv bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.AlwaysYes = v }, bv)
|
|
}
|
|
|
|
// HasAlwaysYes returns true if a value for AlwaysYes has been set in this context.
|
|
func HasAlwaysYes(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).AlwaysYes != nil
|
|
}
|
|
|
|
// IsAlwaysYes returns the value of always yes or the default (false).
|
|
func IsAlwaysYes(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).AlwaysYes; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// WithAlias returns a context with the alias set.
|
|
func WithAlias(ctx context.Context, alias string) context.Context {
|
|
e := GetExecConfig(ctx)
|
|
e.Alias = alias
|
|
|
|
return WithExecConfig(ctx, e)
|
|
}
|
|
|
|
// HasAlias returns true if a value for alias has been set.
|
|
func HasAlias(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).Alias != ""
|
|
}
|
|
|
|
// GetAlias returns an alias if it has been set or an empty string otherwise.
|
|
func GetAlias(ctx context.Context) string {
|
|
return GetExecConfig(ctx).Alias
|
|
}
|
|
|
|
// WithGitInit returns a context with the value for the git init flag set.
|
|
// If true, a git repository will be initialized.
|
|
func WithGitInit(ctx context.Context, bv bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.GitInit = v }, bv)
|
|
}
|
|
|
|
// HasGitInit returns true if the git init flag was set.
|
|
func HasGitInit(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).GitInit != nil
|
|
}
|
|
|
|
// IsGitInit returns the value of the git init flag or true if none was set.
|
|
func IsGitInit(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).GitInit; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// WithForce returns a context with the force flag set.
|
|
// If true, operations that would otherwise fail will be forced to succeed.
|
|
func WithForce(ctx context.Context, bv bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.Force = v }, bv)
|
|
}
|
|
|
|
// HasForce returns true if the context has the force flag set.
|
|
func HasForce(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).Force != nil
|
|
}
|
|
|
|
// IsForce returns the force flag value of the default (false).
|
|
func IsForce(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).Force; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// AddToCommitMessageBody returns a context with something added to the commit's body.
|
|
func AddToCommitMessageBody(ctx context.Context, sv string) context.Context {
|
|
e := GetExecConfig(ctx)
|
|
if e.CommitMessage == nil {
|
|
var ht HeadedText
|
|
e.CommitMessage = &ht
|
|
}
|
|
e.CommitMessage.AddToBody(sv)
|
|
|
|
return WithExecConfig(ctx, e)
|
|
}
|
|
|
|
// HasCommitMessageBody returns true if the commit message body is nonempty.
|
|
func HasCommitMessageBody(ctx context.Context) bool {
|
|
ht := GetExecConfig(ctx).CommitMessage
|
|
if ht == nil {
|
|
return false
|
|
}
|
|
|
|
return ht.HasBody()
|
|
}
|
|
|
|
// GetCommitMessageBody returns the set commit message body or an empty string.
|
|
func GetCommitMessageBody(ctx context.Context) string {
|
|
ht := GetExecConfig(ctx).CommitMessage
|
|
if ht == nil {
|
|
return ""
|
|
}
|
|
|
|
return ht.GetBody()
|
|
}
|
|
|
|
// WithCommitMessage returns a context with a commit message (head) set.
|
|
// (full commit message is the commit message's body is not defined, commit message head otherwise).
|
|
func WithCommitMessage(ctx context.Context, sv string) context.Context {
|
|
e := GetExecConfig(ctx)
|
|
if e.CommitMessage == nil {
|
|
var ht HeadedText
|
|
e.CommitMessage = &ht
|
|
}
|
|
e.CommitMessage.SetHead(sv)
|
|
|
|
return WithExecConfig(ctx, e)
|
|
}
|
|
|
|
// HasCommitMessage returns true if the commit message (head) was set.
|
|
func HasCommitMessage(ctx context.Context) bool {
|
|
ht := GetExecConfig(ctx).CommitMessage
|
|
|
|
return ht != nil && ht.head != "" // not the most intuitive answer, but a backwards-compatible one. for now.
|
|
}
|
|
|
|
// GetCommitMessage returns the set commit message (head) or an empty string.
|
|
func GetCommitMessage(ctx context.Context) string {
|
|
ht := GetExecConfig(ctx).CommitMessage
|
|
if ht == nil {
|
|
return ""
|
|
}
|
|
|
|
return ht.head
|
|
}
|
|
|
|
// GetCommitMessageFull returns the set commit message (head+body, if either are defined) or an empty string.
|
|
func GetCommitMessageFull(ctx context.Context) string {
|
|
ht := GetExecConfig(ctx).CommitMessage
|
|
if ht == nil {
|
|
return ""
|
|
}
|
|
|
|
return ht.GetText()
|
|
}
|
|
|
|
// WithNoNetwork returns a context with the value of no network set.
|
|
// If true, no network operations will be performed.
|
|
func WithNoNetwork(ctx context.Context, bv bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.NoNetwork = v }, bv)
|
|
}
|
|
|
|
// HasNoNetwork returns true if no network was set.
|
|
func HasNoNetwork(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).NoNetwork != nil
|
|
}
|
|
|
|
// IsNoNetwork returns the value of no network or false.
|
|
func IsNoNetwork(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).NoNetwork; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// WithUsername returns a context with the username set in the context.
|
|
func WithUsername(ctx context.Context, sv string) context.Context {
|
|
e := GetExecConfig(ctx)
|
|
e.Username = sv
|
|
|
|
return WithExecConfig(ctx, e)
|
|
}
|
|
|
|
// GetUsername returns the username from the context.
|
|
func GetUsername(ctx context.Context) string {
|
|
return GetExecConfig(ctx).Username
|
|
}
|
|
|
|
// WithEmail returns a context with the email set in the context.
|
|
func WithEmail(ctx context.Context, sv string) context.Context {
|
|
e := GetExecConfig(ctx)
|
|
e.Email = sv
|
|
|
|
return WithExecConfig(ctx, e)
|
|
}
|
|
|
|
// GetEmail returns the email from the context.
|
|
func GetEmail(ctx context.Context) string {
|
|
return GetExecConfig(ctx).Email
|
|
}
|
|
|
|
// WithAgePassphrase returns a context with the age passphrase set.
|
|
// This is used by the age crypto backend to encrypt/decrypt the identity file
|
|
// without interactive prompts (e.g. set from the GOPASS_AGE_PASSWORD env variable).
|
|
func WithAgePassphrase(ctx context.Context, pw string) context.Context {
|
|
e := GetExecConfig(ctx)
|
|
e.AgePassphrase = pw
|
|
|
|
return WithExecConfig(ctx, e)
|
|
}
|
|
|
|
// GetAgePassphrase returns the age passphrase from the context, or an empty
|
|
// string if none has been set.
|
|
func GetAgePassphrase(ctx context.Context) string {
|
|
return GetExecConfig(ctx).AgePassphrase
|
|
}
|
|
|
|
// WithCommitTimestamp returns a context with the value for the commit
|
|
// timestamp set.
|
|
// This is used to allow for reproducible builds.
|
|
func WithCommitTimestamp(ctx context.Context, ts time.Time) context.Context {
|
|
e := GetExecConfig(ctx)
|
|
e.CommitTimestamp = &ts
|
|
|
|
return WithExecConfig(ctx, e)
|
|
}
|
|
|
|
// HasCommitTimestamp returns true if the value for the commit timestamp
|
|
// was set in the context.
|
|
func HasCommitTimestamp(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).CommitTimestamp != nil
|
|
}
|
|
|
|
// GetCommitTimestamp returns the commit timestamp from the context if
|
|
// set or the default (now) otherwise.
|
|
func GetCommitTimestamp(ctx context.Context) time.Time {
|
|
if ts := GetExecConfig(ctx).CommitTimestamp; ts != nil {
|
|
return *ts
|
|
}
|
|
|
|
return time.Now()
|
|
}
|
|
|
|
// WithHidden returns a context with the flag value for hidden set.
|
|
// This is used to hide secrets from the output.
|
|
func WithHidden(ctx context.Context, hidden bool) context.Context {
|
|
return withBool(ctx, func(e *ExecConfig, v *bool) { e.Hidden = v }, hidden)
|
|
}
|
|
|
|
// IsHidden returns true if any output should be hidden in this context.
|
|
func IsHidden(ctx context.Context) bool {
|
|
if v := GetExecConfig(ctx).Hidden; v != nil {
|
|
return *v
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// WithSetupRemote returns a context with the remote URL stored for use during
|
|
// "gopass setup". When non-empty it signals that the automatic initial commit
|
|
// in gitfs.Init() should be suppressed.
|
|
func WithSetupRemote(ctx context.Context, remote string) context.Context {
|
|
e := GetExecConfig(ctx)
|
|
e.SetupRemote = remote
|
|
|
|
return WithExecConfig(ctx, e)
|
|
}
|
|
|
|
// HasSetupRemote returns true if a non-empty setup remote was stored in ctx.
|
|
func HasSetupRemote(ctx context.Context) bool {
|
|
return GetExecConfig(ctx).SetupRemote != ""
|
|
}
|