mirror of
https://github.com/gopasspw/gopass.git
synced 2025-12-12 20:35:49 +01:00
docs: Add GoDoc to pkg and improve markdown files (#3251)
This change adds GoDoc comments to many of the public symbols in the `pkg/` directory. It also includes various improvements to the documentation in `README.md` and other markdown files in the `docs/` directory. This is a partial documentation effort, as requested by the user, to get a pull request submitted quickly. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
c0f278095a
commit
86720090b6
@@ -14,7 +14,7 @@
|
||||
|
||||
> The slightly more awesome standard UNIX password manager for teams.
|
||||
|
||||
Manage your credentials with ease. In a globally distributed team, on multiple devices or fully offline on an air gapped machine.
|
||||
Manage your credentials with ease. In a globally distributed team, on multiple devices or fully offline on an air-gapped machine.
|
||||
|
||||
- **Works everywhere** - The same user experience on Linux, MacOS, *BSD or Windows
|
||||
- **Built for teams** - Built from our experience working in distributed development teams
|
||||
@@ -24,6 +24,7 @@ Manage your credentials with ease. In a globally distributed team, on multiple d
|
||||
|
||||
Gopass is a drop-in replacement for pass, the standard UNIX password manager.
|
||||
By default your credentials are encrypted with GPG and versioned in git. This can be customized easily.
|
||||
Other backends for encryption (e.g. age) and storage (e.g. fossil) are also available.
|
||||
The primary interface is the command line, making it an excellent choice for CLI fans, CI/CD systems or
|
||||
anything you can hook it up with. Gopass can also integrate with your browser so you can largely avoid
|
||||
the command line - if you want.
|
||||
@@ -55,7 +56,7 @@ sudo port install gopass
|
||||
|
||||
### Debian (Ubuntu, Debian, Raspbian, ...)
|
||||
|
||||
**Warning**: Do not install the `gopass` package for the official repositories. That is a completely different project that has no relation to us.
|
||||
**Warning**: Do not install the `gopass` package from the official repositories. That is a completely different project that has no relation to us.
|
||||
|
||||
```shell
|
||||
curl https://packages.gopass.pw/repos/gopass/gopass-archive-keyring.gpg | sudo tee /usr/share/keyrings/gopass-archive-keyring.gpg >/dev/null
|
||||
|
||||
@@ -9,7 +9,7 @@ Some configuration options are only available through setting environment variab
|
||||
| `CHECKPOINT_DISABLE` | `bool` | Set to any non-empty value to disable calling the GitHub API when running `gopass version`. |
|
||||
| `GOPASS_AGE_PASSWORD` | `string` | Set to any value (including the empty string) to use as a password for the age identity file containing your secret age identities. |
|
||||
| `GOPASS_AUTOSYNC_INTERVAL` | `int` | Set this to the number of days between autosync runs. |
|
||||
| `GOPASS_CHARACTER_SET` | `bool` | Set to any non-empty value to restrict the characters used in generated passwords |
|
||||
| `GOPASS_CHARACTER_SET` | `bool` | Set to any non-empty value to restrict the character set used in generated passwords. |
|
||||
| `GOPASS_CLIPBOARD_CLEAR_CMD` | `string` | Use an external command to remove a password from the clipboard. See [GPaste](usecases/gpaste.md) for an example |
|
||||
| `GOPASS_CLIPBOARD_COPY_CMD` | `string` | Use an external command to copy a password to the clipboard. See [GPaste](usecases/gpaste.md) for an example |
|
||||
| `GOPASS_CONFIG_NO_MIGRATE` | `bool` | Do not attempt to migrate old gopass configs and option names |
|
||||
@@ -83,13 +83,13 @@ This is a list of available options:
|
||||
|
||||
| **Option** | **Type** | Description | _Default_ |
|
||||
| ------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
|
||||
| `age.agent-enabled` | `bool` | Enable the persistent Agent for caching of age identities. This will remove the need to repedately enter the passphrase. EXPERIMENTAL. |
|
||||
| `age.agent-enabled` | `bool` | Enable the persistent Agent for caching of age identities. This will remove the need to repeatedly enter the passphrase. EXPERIMENTAL. |
|
||||
| `age.agent-timeout` | `int` | Automatically lock the agent after this many seconds of inactivity. | `0` |
|
||||
| `age.ssh-key-path` | `string` | Path to a custom SSH key file or directory containing SSH keys. If set, this will be used to load SSH identities for age. If not set, the default SSH directory is used. | `~/.ssh` |
|
||||
| `age.usekeychain` | `bool` | Use the OS keychain to cache age passphrases. | `false` |
|
||||
| `audit.concurrency` | `int` | Number of concurrent audit workers. | `` |
|
||||
| `audit.hibp-dump-file` | `string` | Specify to a HIBPv2 Dump file (sorted) if you want `audit` to check password hashes against this file. | `None` |
|
||||
| `audit.hibp-use-api` | `bool` | Set to true if you want `gopass audit` to check your secrets against the public HIBPv2 API. Use with caution. This will leak a few bit of entropy. | `false` |
|
||||
| `audit.hibp-dump-file` | `string` | Specify a HIBPv2 Dump file (sorted) if you want `audit` to check password hashes against this file. | `None` |
|
||||
| `audit.hibp-use-api` | `bool` | Set to true if you want `gopass audit` to check your secrets against the public HIBPv2 API. Use with caution. This will leak a few bits of entropy. | `false` |
|
||||
| `autosync.interval` | `string` | AutoSync interval, for example `2d`, `4h`, `2m` (for days, hours, minutes). A plain number without suffix is taken as days. | `3` |
|
||||
| `core.autoimport` | `bool` | Import missing keys stored in the pass repository without asking. | `false` |
|
||||
| `core.autopush` | `bool` | Always do a `git push` after a commit to the store. Makes sure your local changes are always available on your git remote. | `true` |
|
||||
@@ -111,15 +111,15 @@ This is a list of available options:
|
||||
| `domain-alias.<from>.insteadOf` | `string` | Alias from domain to the string value of this entry. Currently not supported at the local config level. | `` |
|
||||
| `edit.auto-create` | `bool` | Automatically create new secrets when editing. | `false` |
|
||||
| `edit.editor` | `string` | This setting controls which editor is used when opening a file with `gopass edit`. Currently not supported at the local config level. It takes precedence over the `$EDITOR` environment variable. This setting can contain flags. | `None` |
|
||||
| `edit.post-hook` | `string` | This hook is run right after editing a record with `gopass edit`. |
|
||||
| `edit.pre-hook` | `string` | This hook is run right before editing a record with `gopass edit`. |
|
||||
| `edit.post-hook` | `string` | This hook is run right after editing a record with `gopass edit`. | `None` |
|
||||
| `edit.pre-hook` | `string` | This hook is run right before editing a record with `gopass edit`. | `None` |
|
||||
| `generate.autoclip` | `bool` | Always copy the password created with `gopass generate`. | `false` |
|
||||
| `generate.generator` | `string` | Default password generator. `xkcd`, `memorable`, `external` or ``. | `` |
|
||||
| `generate.length` | `int` | Default length for generated password. | `24` |
|
||||
| `generate.strict` | `bool` | Use strict mode for generated password. | `false` |
|
||||
| `generate.symbols` | `bool` | Include symbols in generated password. | `false` |
|
||||
| `mounts.path` | `string` | Path to the root store. | `$XDG_DATA_HOME/gopass/stores/root` |
|
||||
| `notify.disable-icon` | `bool` | Do not show notification icon (not available on every platform). |
|
||||
| `notify.disable-icon` | `bool` | Do not show notification icon (not available on every platform). | `None` |
|
||||
| `otp.autoclip` | `bool` | Automatically clip in `gopass otp` by default, while still displaying the codes and timers. | `false` |
|
||||
| `otp.onlyclip` | `bool` | Automatically clip in `gopass otp` by default, without displaying the OTP codes. This takes precedence over `otp.autoclip`. Requires using `gopass otp --clip=false` to force display the codes. | `false` |
|
||||
| `recipients.check` | `bool` | Check recipients hash. The global config option takes precedence over local ones here for security reasons. | `false` |
|
||||
@@ -134,7 +134,7 @@ This is a list of available options:
|
||||
| `pwgen.xkcd-lang` | `string` | `xkcd` password generator language. | `en` |
|
||||
| `pwgen.xkcd-capitalize` | `bool` | Capitalize the first character of each word. Default is `false`, except when the separator is empty. | `false` |
|
||||
| `pwgen.xkcd-numbers` | `bool` | Add random numbers after each word. | `false` |
|
||||
| `pwgen.xkcd-len` | `bool` | The number of words to generated. | `4` |
|
||||
| `pwgen.xkcd-len` | `bool` | The number of words to be generated. | `4` |
|
||||
|
||||
Furthermore, the following table list the legacy options (starting with v1.15.9) and their new names, their migration should be automatic
|
||||
unless you've set them at the system level or using Env variables, in which case you'll need to migrate them manually:
|
||||
|
||||
@@ -273,7 +273,7 @@ go install github.com/gopasspw/gopass@latest
|
||||
Various editors may store temporary files outside of the secure working directory when editing secrets.
|
||||
We advise you to check and disable this behavior for your editor of choice.
|
||||
|
||||
Here are a few useuful example settings:
|
||||
Here are a few useful example settings:
|
||||
|
||||
```vim
|
||||
" neovim on Linux
|
||||
@@ -319,7 +319,7 @@ grep -q "source <(gopass completion bash)" ~/.bashrc || echo "source <(gopass co
|
||||
```
|
||||
|
||||
|
||||
**MacOS**: The version of bash shipped with MacOS may [require a workaround](https://stackoverflow.com/questions/32596123/why-source-command-doesnt-work-with-process-substitution-in-bash-3-2) to enable auto completion. If the instructions above do not work try the following one:
|
||||
**MacOS**: The version of bash shipped with MacOS may [require a workaround](https://stackoverflow.com/questions/32596123/why-source-command-doesnt-work-with-process-substitution-in-bash-3-2) to enable auto completion. If the instructions above do not work try the following:
|
||||
|
||||
```bash
|
||||
source /dev/stdin <<<"$(gopass completion bash)"
|
||||
@@ -341,7 +341,7 @@ rm -i ${ZDOTDIR:-${HOME:?No ZDOTDIR or HOME}}/.zcompdump && compinit
|
||||
Then exit and re-run zsh if the last command failed.
|
||||
|
||||
Notice that it is important to directly redirect Gopass' output to a file,
|
||||
using pipes or echo mess up the output. Also notice that the generated `_gopass` file is
|
||||
using pipes or echo mess up the output.
|
||||
a completion file that is supposed to be handled by zsh and to be installed in the zsh
|
||||
completions directory, as defined by either the standard `/usr/share/zsh/site-functions/` path,
|
||||
or by a user-specified `fpath` folder. It is not meant to used with `source`.
|
||||
@@ -379,7 +379,7 @@ gopass ls --flat | dmenu | xargs --no-run-if-empty gopass show -c
|
||||
gopass ls --flat | dmenu | xargs --no-run-if-empty gopass show -o | xdotool type --clearmodifiers --file -
|
||||
# First pipe the selected name to gopass, and type the value from the key "username" with xdotool.
|
||||
gopass ls --flat | dmenu | xargs --no-run-if-empty -- bash -c 'gopass show -f $0 username' | head -n 1 | xdotool type --clearmodifiers --file -
|
||||
# Oterwise type the name of the entry using xdotool, in case you are not including a username key in your entries
|
||||
# Otherwise type the name of the entry using xdotool, in case you are not including a username key in your entries
|
||||
gopass ls --flat | dmenu | sed 's!.*/!!' | tr -d '\n' | xdotool type --clearmodifiers --file -
|
||||
```
|
||||
|
||||
@@ -410,8 +410,8 @@ For more detailed instructions, please read: [gopass-jsonapi/README](https://git
|
||||
|
||||
This is the recommended way to use `gopass`.
|
||||
|
||||
NOTE: We do recommend to use a private Git repository. A public one will keep
|
||||
your credentials secure but it will leak metadata.
|
||||
NOTE: We recommend using a private Git repository. A public one will keep
|
||||
your credentials secure, but it will leak metadata.
|
||||
|
||||
To use `gopass` with `git` either create a new git repository or clone an existing
|
||||
password store.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// AliasesPrint prints all configured aliases.
|
||||
// AliasesPrint prints all configured aliases for password generation rules.
|
||||
func (s *Action) AliasesPrint(c *cli.Context) error {
|
||||
out.Printf(c.Context, "Configured aliases:")
|
||||
aliases := pwrules.AllAliases(c.Context)
|
||||
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
var binstdin = os.Stdin
|
||||
|
||||
// Cat prints to or reads from STDIN/STDOUT.
|
||||
// If the content is piped to stdin, it is written to the secret.
|
||||
// Otherwise, the secret content is printed to stdout.
|
||||
func (s *Action) Cat(c *cli.Context) error {
|
||||
ctx := ctxutil.WithGlobalFlags(c)
|
||||
name := c.Args().First()
|
||||
@@ -112,7 +114,7 @@ func secFromBytes(dst, src string, in []byte) (gopass.Secret, error) {
|
||||
return sec, nil
|
||||
}
|
||||
|
||||
// BinaryCopy copies either from the filesystem to the store or from the store.
|
||||
// BinaryCopy copies either from the filesystem to the store or from the store
|
||||
// to the filesystem.
|
||||
func (s *Action) BinaryCopy(c *cli.Context) error {
|
||||
ctx := ctxutil.WithGlobalFlags(c)
|
||||
@@ -355,6 +357,7 @@ func (s *Action) binaryGet(ctx context.Context, name string) ([]byte, error) {
|
||||
}
|
||||
|
||||
// Sum decodes binary content and computes the SHA256 checksum.
|
||||
// It prints the checksum to stdout.
|
||||
func (s *Action) Sum(c *cli.Context) error {
|
||||
ctx := ctxutil.WithGlobalFlags(c)
|
||||
name := c.Args().First()
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
)
|
||||
|
||||
// Clone will fetch and mount a new password store from a git repo.
|
||||
// It can also be used to clone a new password store to a submount.
|
||||
func (s *Action) Clone(c *cli.Context) error {
|
||||
ctx := ctxutil.WithGlobalFlags(c)
|
||||
if c.IsSet("crypto") {
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// ShowFlags returns the flags for the show command. Exported to re-use in main
|
||||
// for the default command.
|
||||
// ShowFlags returns the flags for the show command.
|
||||
// Exported to re-use in main for the default command.
|
||||
func ShowFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
@@ -68,6 +68,7 @@ func ShowFlags() []cli.Flag {
|
||||
}
|
||||
|
||||
// GetCommands returns the cli commands exported by this module.
|
||||
// It also includes any commands provided by the crypto and storage backends.
|
||||
func (s *Action) GetCommands() []*cli.Command {
|
||||
cmds := []*cli.Command{
|
||||
{
|
||||
|
||||
@@ -35,7 +35,7 @@ func bashEscape(s string) string {
|
||||
})
|
||||
}
|
||||
|
||||
// Complete prints a list of all password names to os.Stdout.
|
||||
// Complete prints a list of all password names to os.Stdout, for bash completion.
|
||||
func (s *Action) Complete(c *cli.Context) {
|
||||
ctx := ctxutil.WithGlobalFlags(c)
|
||||
_, err := s.Store.IsInitialized(ctx) // important to make sure the structs are not nil.
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
)
|
||||
|
||||
// Config handles changes to the gopass configuration.
|
||||
// It can be used to print the whole config, a single key, or to set a new
|
||||
// value for a given key.
|
||||
func (s *Action) Config(c *cli.Context) error {
|
||||
ctx := ctxutil.WithGlobalFlags(c)
|
||||
store := c.String("store")
|
||||
@@ -126,7 +128,7 @@ func (s *Action) configKeys() []string {
|
||||
return s.cfg.Keys("")
|
||||
}
|
||||
|
||||
// ConfigComplete will print the list of valid config keys.
|
||||
// ConfigComplete will print the list of valid config keys for bash completion.
|
||||
func (s *Action) ConfigComplete(c *cli.Context) {
|
||||
for _, k := range s.configKeys() {
|
||||
fmt.Fprintln(stdout, k)
|
||||
|
||||
@@ -38,7 +38,7 @@ func WithAlsoClip(ctx context.Context, clip bool) context.Context {
|
||||
return context.WithValue(ctx, ctxKeyAlsoClip, clip)
|
||||
}
|
||||
|
||||
// IsAlsoClip returns the value for alsoclip of the dfeault (false).
|
||||
// IsAlsoClip returns the value for alsoclip or the default (false).
|
||||
func IsAlsoClip(ctx context.Context) bool {
|
||||
bv, ok := ctx.Value(ctxKeyAlsoClip).(bool)
|
||||
if !ok {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/gopasspw/gopass/pkg/debug"
|
||||
)
|
||||
|
||||
// DefaultAppdir is the default appdir for gopass.
|
||||
var DefaultAppdir = New("gopass")
|
||||
|
||||
// Appdir is a helper struct to generate paths for config, cache and data dirs.
|
||||
@@ -17,7 +18,9 @@ type Appdir struct {
|
||||
name string
|
||||
}
|
||||
|
||||
// New returns a new Appdir.
|
||||
// New returns a new Appdir for the given application name.
|
||||
// The name is used to construct the paths to the application's
|
||||
// directories.
|
||||
func New(name string) *Appdir {
|
||||
return &Appdir{
|
||||
name: name,
|
||||
@@ -29,22 +32,26 @@ func (a *Appdir) Name() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
// UserConfig returns the users config dir.
|
||||
// UserConfig returns the user's config dir for gopass.
|
||||
// See a.UserConfig() for more details.
|
||||
func UserConfig() string {
|
||||
return DefaultAppdir.UserConfig()
|
||||
}
|
||||
|
||||
// UserCache returns the users cache dir.
|
||||
// UserCache returns the user's cache dir for gopass.
|
||||
// See a.UserCache() for more details.
|
||||
func UserCache() string {
|
||||
return DefaultAppdir.UserCache()
|
||||
}
|
||||
|
||||
// UserData returns the users data dir.
|
||||
// UserData returns the user's data dir for gopass.
|
||||
// See a.UserData() for more details.
|
||||
func UserData() string {
|
||||
return DefaultAppdir.UserData()
|
||||
}
|
||||
|
||||
// UserHome returns the users home dir.
|
||||
// UserHome returns the user's home dir.
|
||||
// It respects the GOPASS_HOMEDIR environment variable.
|
||||
func UserHome() string {
|
||||
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
|
||||
return hd
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// UserConfig returns the users config dir
|
||||
// UserConfig returns the user's config directory.
|
||||
// It uses the APPDATA environment variable on Windows.
|
||||
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
|
||||
func (a *Appdir) UserConfig() string {
|
||||
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
|
||||
return filepath.Join(hd, ".config", a.name)
|
||||
@@ -14,7 +16,9 @@ func (a *Appdir) UserConfig() string {
|
||||
return filepath.Join(os.Getenv("APPDATA"), a.name)
|
||||
}
|
||||
|
||||
// UserCache returns the users cache dir
|
||||
// UserCache returns the user's cache directory.
|
||||
// It uses the LOCALAPPDATA environment variable on Windows.
|
||||
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
|
||||
func (a *Appdir) UserCache() string {
|
||||
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
|
||||
return filepath.Join(hd, ".cache", a.name)
|
||||
@@ -23,7 +27,9 @@ func (a *Appdir) UserCache() string {
|
||||
return filepath.Join(os.Getenv("LOCALAPPDATA"), a.name)
|
||||
}
|
||||
|
||||
// UserData returns the users data dir
|
||||
// UserData returns the user's data directory.
|
||||
// It uses the LOCALAPPDATA environment variable on Windows.
|
||||
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
|
||||
func (a *Appdir) UserData() string {
|
||||
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
|
||||
return filepath.Join(hd, ".local", "share", a.name)
|
||||
|
||||
@@ -10,7 +10,10 @@ import (
|
||||
"github.com/gopasspw/gopass/pkg/debug"
|
||||
)
|
||||
|
||||
// UserConfig returns the users config dir.
|
||||
// UserConfig returns the user's config directory.
|
||||
// It follows the XDG Base Directory Specification.
|
||||
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
|
||||
// See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
func (a *Appdir) UserConfig() string {
|
||||
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
|
||||
debug.V(3).Log("GOPASS_HOMEDIR is set to %s", hd)
|
||||
@@ -26,7 +29,10 @@ func (a *Appdir) UserConfig() string {
|
||||
return filepath.Join(base, a.name)
|
||||
}
|
||||
|
||||
// UserCache returns the users cache dir.
|
||||
// UserCache returns the user's cache directory.
|
||||
// It follows the XDG Base Directory Specification.
|
||||
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
|
||||
// See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
func (a *Appdir) UserCache() string {
|
||||
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
|
||||
return filepath.Join(hd, ".cache", a.name)
|
||||
@@ -40,7 +46,10 @@ func (a *Appdir) UserCache() string {
|
||||
return filepath.Join(base, a.name)
|
||||
}
|
||||
|
||||
// UserData returns the users data dir.
|
||||
// UserData returns the user's data directory.
|
||||
// It follows the XDG Base Directory Specification.
|
||||
// The GOPASS_HOMEDIR environment variable can be used to override the base path.
|
||||
// See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
func (a *Appdir) UserData() string {
|
||||
if hd := os.Getenv("GOPASS_HOMEDIR"); hd != "" {
|
||||
return filepath.Join(hd, ".local", "share", a.name)
|
||||
|
||||
@@ -24,7 +24,8 @@ var (
|
||||
)
|
||||
|
||||
// CopyTo copies the given data to the clipboard and enqueues automatic
|
||||
// clearing of the clipboard.
|
||||
// clearing of the clipboard. The name of the secret is passed for logging
|
||||
// and notifications. The timeout is in seconds.
|
||||
func CopyTo(ctx context.Context, name string, content []byte, timeout int) error {
|
||||
debug.Log("Copying to clipboard: %s for %ds", name, timeout)
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ const (
|
||||
var ErrNoCallback = fmt.Errorf("no callback")
|
||||
|
||||
// WithGlobalFlags parses any global flags from the cli context and returns
|
||||
// a regular context.
|
||||
// a regular context. It handles the --yes flag and sets the appropriate
|
||||
// context value.
|
||||
func WithGlobalFlags(c *cli.Context) context.Context {
|
||||
if c.Bool("yes") {
|
||||
return WithAlwaysYes(c.Context, true)
|
||||
@@ -49,10 +50,11 @@ func WithGlobalFlags(c *cli.Context) context.Context {
|
||||
return c.Context
|
||||
}
|
||||
|
||||
// ProgressCallback is a callback for updateing progress.
|
||||
// ProgressCallback is a callback for updating progress.
|
||||
type ProgressCallback func()
|
||||
|
||||
// WithTerminal returns a context with an explicit value for terminal.
|
||||
// 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 context.WithValue(ctx, ctxKeyTerminal, isTerm)
|
||||
}
|
||||
@@ -74,7 +76,8 @@ func IsTerminal(ctx context.Context) bool {
|
||||
return bv
|
||||
}
|
||||
|
||||
// WithInteractive returns a context with an explicit value for interactive.
|
||||
// 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 context.WithValue(ctx, ctxKeyInteractive, isInteractive)
|
||||
}
|
||||
@@ -121,6 +124,7 @@ func IsStdin(ctx context.Context) bool {
|
||||
}
|
||||
|
||||
// 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 context.WithValue(ctx, ctxKeyShowParsing, bv)
|
||||
}
|
||||
@@ -143,6 +147,7 @@ func IsShowParsing(ctx context.Context) bool {
|
||||
}
|
||||
|
||||
// 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 context.WithValue(ctx, ctxKeyGitCommit, bv)
|
||||
}
|
||||
@@ -159,7 +164,8 @@ func IsGitCommit(ctx context.Context) bool {
|
||||
return is(ctx, ctxKeyGitCommit, true)
|
||||
}
|
||||
|
||||
// IsFollowRef returns the value of follow-ref or the default (true).
|
||||
// IsFollowRef returns the value of follow-ref or the default (false).
|
||||
// If true, symlinks will be followed.
|
||||
func IsFollowRef(ctx context.Context) bool {
|
||||
return is(ctx, ctxFollowRef, false)
|
||||
}
|
||||
@@ -177,6 +183,7 @@ func WithFollowRef(ctx context.Context, bv bool) context.Context {
|
||||
}
|
||||
|
||||
// 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 context.WithValue(ctx, ctxKeyAlwaysYes, bv)
|
||||
}
|
||||
@@ -221,7 +228,7 @@ func GetProgressCallback(ctx context.Context) ProgressCallback {
|
||||
return cb
|
||||
}
|
||||
|
||||
// WithAlias returns an context with the alias set.
|
||||
// WithAlias returns a context with the alias set.
|
||||
func WithAlias(ctx context.Context, alias string) context.Context {
|
||||
return context.WithValue(ctx, ctxKeyAlias, alias)
|
||||
}
|
||||
@@ -242,6 +249,7 @@ func GetAlias(ctx context.Context) string {
|
||||
}
|
||||
|
||||
// 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 context.WithValue(ctx, ctxKeyGitInit, bv)
|
||||
}
|
||||
@@ -251,12 +259,13 @@ func HasGitInit(ctx context.Context) bool {
|
||||
return hasBool(ctx, ctxKeyGitInit)
|
||||
}
|
||||
|
||||
// IsGitInit returns the value of the git init flag or ture if none was set.
|
||||
// IsGitInit returns the value of the git init flag or true if none was set.
|
||||
func IsGitInit(ctx context.Context) bool {
|
||||
return is(ctx, ctxKeyGitInit, 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 context.WithValue(ctx, ctxKeyForce, bv)
|
||||
}
|
||||
@@ -335,7 +344,7 @@ func GetCommitMessage(ctx context.Context) string {
|
||||
return ht.head
|
||||
}
|
||||
|
||||
// GetCommitMessageFull returns the set commit message (head+body, of either are defined) or an empty string.
|
||||
// GetCommitMessageFull returns the set commit message (head+body, if either are defined) or an empty string.
|
||||
func GetCommitMessageFull(ctx context.Context) string {
|
||||
ht, ok := ctx.Value(ctxKeyCommitMessage).(*HeadedText)
|
||||
if !ok {
|
||||
@@ -346,6 +355,7 @@ func GetCommitMessageFull(ctx context.Context) string {
|
||||
}
|
||||
|
||||
// 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 context.WithValue(ctx, ctxKeyNoNetwork, bv)
|
||||
}
|
||||
@@ -391,6 +401,7 @@ func GetEmail(ctx context.Context) string {
|
||||
}
|
||||
|
||||
// WithImportFunc will return a context with the import callback set.
|
||||
// The callback is used to ask the user for confirmation before importing a key.
|
||||
func WithImportFunc(ctx context.Context, imf store.ImportCallback) context.Context {
|
||||
return context.WithValue(ctx, ctxKeyImportFunc, imf)
|
||||
}
|
||||
@@ -435,6 +446,7 @@ func HasPasswordCallback(ctx context.Context) bool {
|
||||
}
|
||||
|
||||
// GetPasswordCallback returns the password callback or a default (which always fails).
|
||||
// The default callback returns ErrNoCallback.
|
||||
func GetPasswordCallback(ctx context.Context) PasswordCallback {
|
||||
pwcb, ok := ctx.Value(ctxKeyPasswordCallback).(PasswordCallback)
|
||||
if !ok || pwcb == nil {
|
||||
@@ -462,6 +474,7 @@ func HasPasswordPurgeCallback(ctx context.Context) bool {
|
||||
}
|
||||
|
||||
// GetPasswordPurgeCallback returns the password purge callback or a default (which is a no-op).
|
||||
// The default callback does nothing.
|
||||
func GetPasswordPurgeCallback(ctx context.Context) PasswordPurgeCallback {
|
||||
ppcb, ok := ctx.Value(ctxKeyPasswordPurgeCallback).(PasswordPurgeCallback)
|
||||
if !ok || ppcb == nil {
|
||||
@@ -473,6 +486,7 @@ func GetPasswordPurgeCallback(ctx context.Context) PasswordPurgeCallback {
|
||||
|
||||
// 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 {
|
||||
return context.WithValue(ctx, ctxKeyCommitTimestamp, ts)
|
||||
}
|
||||
@@ -496,6 +510,7 @@ func GetCommitTimestamp(ctx context.Context) time.Time {
|
||||
}
|
||||
|
||||
// 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 context.WithValue(ctx, ctxKeyHidden, hidden)
|
||||
}
|
||||
|
||||
@@ -32,19 +32,23 @@ func is(ctx context.Context, key contextKey, def bool) bool {
|
||||
return bv
|
||||
}
|
||||
|
||||
// HeadedText is a helper struct for storing a commit message with a subject and body.
|
||||
type HeadedText struct {
|
||||
head string
|
||||
body *strings.Builder
|
||||
}
|
||||
|
||||
// SetHead sets the head of the text.
|
||||
func (h *HeadedText) SetHead(s string) {
|
||||
h.head = s
|
||||
}
|
||||
|
||||
// GetHead returns the head of the text.
|
||||
func (h *HeadedText) GetHead() string {
|
||||
return h.head
|
||||
}
|
||||
|
||||
// AddToBody adds a string to the body of the text.
|
||||
func (h *HeadedText) AddToBody(s string) {
|
||||
if h.body == nil {
|
||||
var realBody strings.Builder
|
||||
@@ -56,10 +60,12 @@ func (h *HeadedText) AddToBody(s string) {
|
||||
(*h.body).WriteString("\n" + s)
|
||||
}
|
||||
|
||||
// ClearBody clears the body of the text.
|
||||
func (h *HeadedText) ClearBody() {
|
||||
h.body = nil
|
||||
}
|
||||
|
||||
// GetBody returns the body of the text.
|
||||
func (h *HeadedText) GetBody() string {
|
||||
if h.body == nil {
|
||||
return ""
|
||||
@@ -68,12 +74,14 @@ func (h *HeadedText) GetBody() string {
|
||||
return (*h.body).String()
|
||||
}
|
||||
|
||||
// HasBody returns true if the body of the text is not empty.
|
||||
func (h *HeadedText) HasBody() bool {
|
||||
ok := h.body != nil
|
||||
|
||||
return ok && (*h.body).Len() > 0
|
||||
}
|
||||
|
||||
// GetText returns the full text, including the head and body.
|
||||
func (h *HeadedText) GetText() string {
|
||||
body := h.GetBody()
|
||||
if body == "" && h.head == "" {
|
||||
|
||||
@@ -34,6 +34,7 @@ var opts struct {
|
||||
type v int
|
||||
|
||||
// V returns a logger at the given verbosity level.
|
||||
// The higher the number, the more verbose the logging.
|
||||
func V(n int) v {
|
||||
return v(n)
|
||||
}
|
||||
@@ -211,12 +212,16 @@ func checkFilter(filter map[string]bool, key string) bool {
|
||||
// Log logs a statement to Stderr (unless filtered) and the
|
||||
// debug log file (if enabled), but only if the verbosity
|
||||
// level is greater or equal to the given level.
|
||||
//
|
||||
// This is a no-op if the verbosity level is not high enough.
|
||||
func (n v) Log(f string, args ...any) {
|
||||
logFn(int(n), 0, f, args...)
|
||||
}
|
||||
|
||||
// Log logs a statement to Stderr (unless filtered) and the
|
||||
// debug log file (if enabled).
|
||||
//
|
||||
// This is a no-op if debugging is not enabled.
|
||||
func Log(f string, args ...any) {
|
||||
logFn(0, 0, f, args...)
|
||||
}
|
||||
@@ -224,6 +229,8 @@ func Log(f string, args ...any) {
|
||||
// LogN logs a statement to Stderr (unless filtered) and the
|
||||
// debug log file (if enabled). The offset will be applied to
|
||||
// the runtime position.
|
||||
//
|
||||
// This is a no-op if debugging is not enabled.
|
||||
func LogN(offset int, f string, args ...any) {
|
||||
logFn(0, offset, f, args...)
|
||||
}
|
||||
@@ -289,6 +296,7 @@ func doLog(verbosity, offset int, f string, args ...any) {
|
||||
}
|
||||
|
||||
// IsEnabled returns true if debug logging was enabled.
|
||||
// This is useful to avoid expensive computations if debugging is not enabled.
|
||||
func IsEnabled() bool {
|
||||
return enabled
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import (
|
||||
|
||||
var biFunc func() (*rdebug.BuildInfo, bool) = rdebug.ReadBuildInfo
|
||||
|
||||
// ModuleVersion the version of the named import.
|
||||
// ModuleVersion returns the version of the named module.
|
||||
// It reads the build info and parses the version of the requested module.
|
||||
func ModuleVersion(m string) semver.Version {
|
||||
bi, ok := biFunc()
|
||||
if !ok || bi == nil {
|
||||
|
||||
@@ -19,8 +19,8 @@ import (
|
||||
|
||||
var reCleanFilename = regexp.MustCompile(`[^\w\d@.-]`)
|
||||
|
||||
// CleanFilename strips all possibly suspicious characters from a filename
|
||||
// WARNING: NOT suiteable for pathnames as slashes will be stripped as well!
|
||||
// CleanFilename strips all possibly suspicious characters from a filename.
|
||||
// WARNING: NOT suitable for pathnames as slashes will be stripped as well!
|
||||
func CleanFilename(in string) string {
|
||||
return strings.Trim(reCleanFilename.ReplaceAllString(in, "_"), "_ ")
|
||||
}
|
||||
@@ -40,6 +40,7 @@ func ExpandHomedir(path string) string {
|
||||
}
|
||||
|
||||
// CleanPath resolves common aliases in a path and cleans it as much as possible.
|
||||
// It expands the tilde to the user's home directory and resolves relative paths.
|
||||
func CleanPath(path string) string {
|
||||
// Replace ~ with GOPASS_HOMEDIR if set (mainly for testing and experiments),
|
||||
// otherwise replace ~ with user's homedir if set. We expect any reference
|
||||
@@ -62,7 +63,6 @@ func CleanPath(path string) string {
|
||||
}
|
||||
|
||||
// IsDir checks if a certain path exists and is a directory.
|
||||
// https://stackoverflow.com/questions/10510691/how-to-check-whether-a-file-or-directory-denoted-by-a-path-exists-in-golang
|
||||
func IsDir(path string) bool {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
@@ -141,7 +141,8 @@ func IsEmptyDir(path string) (bool, error) {
|
||||
return empty, nil
|
||||
}
|
||||
|
||||
// Shred overwrite the given file any number of times.
|
||||
// Shred overwrites the given file with random data and deletes it.
|
||||
// The file is overwritten `runs` times. The last run is with zeros.
|
||||
func Shred(path string, runs int) error {
|
||||
fh, err := os.OpenFile(path, os.O_WRONLY, 0o600)
|
||||
if err != nil {
|
||||
@@ -278,7 +279,7 @@ func CopyFile(from, to string) error {
|
||||
}
|
||||
|
||||
// CopyFileForce copies a file from src to dst. Permissions will be preserved. The destination
|
||||
// if removed before copying to avoid permission issues.
|
||||
// is removed before copying to avoid permission issues.
|
||||
func CopyFileForce(from, to string) error {
|
||||
if IsFile(to) {
|
||||
if err := os.Remove(to); err != nil {
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Umask extracts the umask from env.
|
||||
// Umask extracts the umask from the environment variables.
|
||||
// It checks for GOPASS_UMASK and PASSWORD_STORE_UMASK.
|
||||
// If neither is set, it returns the default umask of 0o77.
|
||||
func Umask() int {
|
||||
for _, en := range []string{"GOPASS_UMASK", "PASSWORD_STORE_UMASK"} {
|
||||
um := os.Getenv(en)
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/gopasspw/gopass/pkg/gopass"
|
||||
)
|
||||
|
||||
// Gopass is a secret store implementation.
|
||||
// Gopass is a secret store implementation. It is the main entry point for the gopass API.
|
||||
type Gopass struct {
|
||||
rs *root.Store
|
||||
}
|
||||
@@ -26,15 +26,17 @@ type Gopass struct {
|
||||
// make sure that *Gopass implements Store.
|
||||
var _ gopass.Store = &Gopass{}
|
||||
|
||||
// ErrNotImplemented is returned when a method is not implemented.
|
||||
// ErrNotImplemented is returned when a method is not yet implemented.
|
||||
var ErrNotImplemented = fmt.Errorf("not yet implemented")
|
||||
|
||||
// ErrNotInitialized is returned when the store is not initialized.
|
||||
// This happens when the gopass CLI has not been used to set up a password store yet.
|
||||
var ErrNotInitialized = fmt.Errorf("password store not initialized. run 'gopass setup' first")
|
||||
|
||||
// New initializes an existing password store. It will attempt to load an existing
|
||||
// configuration or use the built-in defaults. If no password store is found and
|
||||
// the user will need to initialize it with the gopass CLI (`gopass setup`) first.
|
||||
// New initializes an existing password store and returns a new Gopass instance.
|
||||
// It will attempt to load an existing configuration or use the built-in defaults.
|
||||
// If no password store is found the user will need to initialize it with the gopass
|
||||
// CLI (`gopass setup`) first.
|
||||
//
|
||||
// WARNING: This will need to change to accommodate for runtime configuration.
|
||||
func New(ctx context.Context) (*Gopass, error) {
|
||||
@@ -55,19 +57,21 @@ func New(ctx context.Context) (*Gopass, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// List returns a list of all secrets.
|
||||
// List returns a list of all secret names.
|
||||
func (g *Gopass) List(ctx context.Context) ([]string, error) {
|
||||
return g.rs.List(ctx, tree.INF) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
// Get returns a single, encrypted secret. It must be unwrapped before use.
|
||||
// Use "latest" to get the latest revision.
|
||||
// The revision parameter is not yet implemented.
|
||||
func (g *Gopass) Get(ctx context.Context, name, revision string) (gopass.Secret, error) {
|
||||
return g.rs.Get(ctx, name) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
// Set adds a new revision to an existing secret or creates a new one.
|
||||
// Set adds a new revision to an existing secret or creates a new one if it doesn't exist.
|
||||
// Create new secrets with secrets.New().
|
||||
// The secret is passed as a gopass.Byter, which is an interface that can be satisfied by a []byte.
|
||||
func (g *Gopass) Set(ctx context.Context, name string, sec gopass.Byter) error {
|
||||
return g.rs.Set(ctx, name, sec) //nolint:wrapcheck
|
||||
}
|
||||
@@ -82,28 +86,31 @@ func (g *Gopass) RemoveAll(ctx context.Context, prefix string) error {
|
||||
return g.rs.Prune(ctx, prefix) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
// Rename move a prefix to another.
|
||||
// Rename moves a secret from one path to another.
|
||||
func (g *Gopass) Rename(ctx context.Context, src, dest string) error {
|
||||
return g.rs.Move(ctx, src, dest) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
// Sync synchronizes a secret with a remote.
|
||||
// Sync synchronizes the store with a remote.
|
||||
// Not yet implemented.
|
||||
func (g *Gopass) Sync(ctx context.Context) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
// Revisions lists all revisions of this secret.
|
||||
// Not yet implemented.
|
||||
func (g *Gopass) Revisions(ctx context.Context, name string) ([]string, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
// String returns the name of this store.
|
||||
func (g *Gopass) String() string {
|
||||
return "gopass"
|
||||
}
|
||||
|
||||
// Close shuts down all background processes.
|
||||
// Close shuts down all background processes and closes the store.
|
||||
//
|
||||
// MUST be called before existing to make sure any background processing
|
||||
// MUST be called before exiting to make sure any background processing
|
||||
// (e.g. pending commits or pushes) are complete. Failing to do so might
|
||||
// result in an invalid password store state.
|
||||
func (g *Gopass) Close(ctx context.Context) error {
|
||||
|
||||
@@ -30,28 +30,29 @@ type MockAPI struct {
|
||||
}
|
||||
|
||||
// New creates a new gopass API mock.
|
||||
// It uses an in-memory store.
|
||||
func New() *MockAPI {
|
||||
return &MockAPI{
|
||||
store: mockstore.New(""),
|
||||
}
|
||||
}
|
||||
|
||||
// String returns mockapi.
|
||||
// String returns the name of the mock API.
|
||||
func (a *MockAPI) String() string {
|
||||
return "mockapi"
|
||||
}
|
||||
|
||||
// List does nothing.
|
||||
// List returns a list of all secrets in the mock store.
|
||||
func (a *MockAPI) List(ctx context.Context) ([]string, error) {
|
||||
return a.store.List(ctx, "") //nolint:wrapcheck
|
||||
}
|
||||
|
||||
// Get does nothing.
|
||||
// Get returns a secret from the mock store.
|
||||
func (a *MockAPI) Get(ctx context.Context, name, _ string) (gopass.Secret, error) {
|
||||
return a.store.Get(ctx, name) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
// Revisions does nothing.
|
||||
// Revisions returns a list of all revisions of a secret in the mock store.
|
||||
func (a *MockAPI) Revisions(ctx context.Context, name string) ([]string, error) {
|
||||
rs, err := a.store.ListRevisions(ctx, name)
|
||||
if err != nil {
|
||||
@@ -66,22 +67,22 @@ func (a *MockAPI) Revisions(ctx context.Context, name string) ([]string, error)
|
||||
return revs, nil
|
||||
}
|
||||
|
||||
// Set does nothing.
|
||||
// Set sets a secret in the mock store.
|
||||
func (a *MockAPI) Set(ctx context.Context, name string, sec gopass.Byter) error {
|
||||
return a.store.Set(ctx, name, sec) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
// Remove does nothing.
|
||||
// Remove removes a secret from the mock store.
|
||||
func (a *MockAPI) Remove(ctx context.Context, name string) error {
|
||||
return a.store.Delete(ctx, name) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
// RemoveAll does nothing.
|
||||
// RemoveAll removes all secrets with a given prefix from the mock store.
|
||||
func (a *MockAPI) RemoveAll(ctx context.Context, prefix string) error {
|
||||
return a.store.Prune(ctx, prefix) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
// Rename does nothing.
|
||||
// Rename moves a secret in the mock store.
|
||||
func (a *MockAPI) Rename(ctx context.Context, src, dest string) error {
|
||||
return a.store.Move(ctx, src, dest) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ var (
|
||||
)
|
||||
|
||||
// AKV is the new Key-Value implementation that will replace KV.
|
||||
// It stores a password and a set of key-value pairs.
|
||||
// The raw content is stored in a strings.Builder, which is a more
|
||||
// efficient way to build strings.
|
||||
type AKV struct {
|
||||
password string
|
||||
kvp map[string][]string
|
||||
@@ -26,7 +29,7 @@ type AKV struct {
|
||||
fromMime bool
|
||||
}
|
||||
|
||||
// NewAKV creates a new AKV instances.
|
||||
// NewAKV creates a new, empty AKV instance.
|
||||
func NewAKV() *AKV {
|
||||
a := &AKV{
|
||||
kvp: make(map[string][]string),
|
||||
@@ -36,7 +39,7 @@ func NewAKV() *AKV {
|
||||
return a
|
||||
}
|
||||
|
||||
// NewAKVWithData returns a new KV secret populated with data.
|
||||
// NewAKVWithData returns a new AKV secret populated with data.
|
||||
func NewAKVWithData(pw string, kvps map[string][]string, body string, converted bool) *AKV {
|
||||
kv := NewAKV()
|
||||
kv.SetPassword(pw)
|
||||
@@ -54,17 +57,17 @@ func NewAKVWithData(pw string, kvps map[string][]string, body string, converted
|
||||
return kv
|
||||
}
|
||||
|
||||
// Bytes returns the raw string as bytes.
|
||||
// Bytes returns the raw content of the secret as a byte slice.
|
||||
func (a *AKV) Bytes() []byte {
|
||||
return []byte(a.raw.String())
|
||||
}
|
||||
|
||||
// Keys returns all the parsed keys.
|
||||
// Keys returns a sorted list of all keys in the secret.
|
||||
func (a *AKV) Keys() []string {
|
||||
return set.SortedKeys(a.kvp)
|
||||
}
|
||||
|
||||
// Get returns the value of the requested key, if found.
|
||||
// Get returns the first value for a given key, if found.
|
||||
func (a *AKV) Get(key string) (string, bool) {
|
||||
if v, found := a.kvp[key]; found {
|
||||
return v[0], true
|
||||
@@ -73,16 +76,15 @@ func (a *AKV) Get(key string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Values returns all values for that key.
|
||||
// Values returns all values for a given key.
|
||||
func (a *AKV) Values(key string) ([]string, bool) {
|
||||
v, found := a.kvp[key]
|
||||
|
||||
return v, found
|
||||
}
|
||||
|
||||
// Ref returns reference in case of having password of the
|
||||
// gopass://ref
|
||||
// which references another secret in the store.
|
||||
// Ref returns a reference to another secret if the password is a gopass URI.
|
||||
// A gopass URI is a string that starts with "gopass://".
|
||||
func (a *AKV) Ref() (string, bool) {
|
||||
if strings.HasPrefix(a.password, gopassRef) {
|
||||
return strings.TrimPrefix(a.password, gopassRef), true
|
||||
@@ -91,7 +93,8 @@ func (a *AKV) Ref() (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Set writes a single key.
|
||||
// Set sets the value for a given key, replacing the first instance if it exists.
|
||||
// If the key does not exist, it is added to the end of the secret.
|
||||
func (a *AKV) Set(key string, value any) error {
|
||||
// if it's new key we can just append it at the end
|
||||
if _, found := a.kvp[key]; !found {
|
||||
@@ -155,7 +158,7 @@ func (a *AKV) Set(key string, value any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add appends data to a given key.
|
||||
// Add adds a new value for a given key.
|
||||
func (a *AKV) Add(key string, value any) error {
|
||||
sv := fmt.Sprintf("%s", value)
|
||||
a.kvp[key] = append(a.kvp[key], sv)
|
||||
@@ -169,7 +172,7 @@ func (a *AKV) Add(key string, value any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Del removes a given key and all of its values.
|
||||
// Del removes a key and all of its values.
|
||||
func (a *AKV) Del(key string) bool {
|
||||
_, found := a.kvp[key]
|
||||
if !found {
|
||||
@@ -218,12 +221,12 @@ func (a *AKV) Del(key string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Password returns the password.
|
||||
// Password returns the password of the secret.
|
||||
func (a *AKV) Password() string {
|
||||
return a.password
|
||||
}
|
||||
|
||||
// SetPassword updates the password.
|
||||
// SetPassword updates the password of the secret.
|
||||
func (a *AKV) SetPassword(p string) {
|
||||
s := newScanner(strings.NewReader(a.raw.String()), a.raw.Len())
|
||||
a.raw = strings.Builder{}
|
||||
@@ -247,7 +250,7 @@ func (a *AKV) SetPassword(p string) {
|
||||
}
|
||||
}
|
||||
|
||||
// ParseAKV tries to parse an AKV secret.
|
||||
// ParseAKV parses a byte slice and returns a new AKV instance.
|
||||
func ParseAKV(in []byte) *AKV {
|
||||
a := NewAKV()
|
||||
a.raw = strings.Builder{}
|
||||
@@ -291,7 +294,8 @@ func ParseAKV(in []byte) *AKV {
|
||||
return a
|
||||
}
|
||||
|
||||
// Body returns the body.
|
||||
// Body returns the body of the secret, which is the content of the secret
|
||||
// without the password and the key-value pairs.
|
||||
func (a *AKV) Body() string {
|
||||
out := strings.Builder{}
|
||||
|
||||
@@ -346,7 +350,7 @@ func newScanner(in io.Reader, inSize int) *bufio.Scanner {
|
||||
return s
|
||||
}
|
||||
|
||||
// Write appends the buffer to the secret's body.
|
||||
// Write appends data to the secret's body.
|
||||
func (a *AKV) Write(buf []byte) (int, error) {
|
||||
var w io.Writer
|
||||
w = &a.raw
|
||||
@@ -365,26 +369,27 @@ func (a *AKV) Write(buf []byte) (int, error) {
|
||||
return w.Write(buf)
|
||||
}
|
||||
|
||||
// FromMime returns whether this secret was converted from a Mime secret of not.
|
||||
// FromMime returns whether this secret was converted from a MIME secret.
|
||||
func (a *AKV) FromMime() bool {
|
||||
return a.fromMime
|
||||
}
|
||||
|
||||
// SafeStr always returnes "(elided)".
|
||||
// SafeStr always returns "(elided)" to avoid leaking sensitive information.
|
||||
func (a *AKV) SafeStr() string {
|
||||
return "(elided)"
|
||||
}
|
||||
|
||||
// pwWriter is a io.Writer that will extract the first line of the input stream and
|
||||
// then write it to the password field of the provided callback. The first line can stretch
|
||||
// multiple chunks but once the first line has been completed any writes to this
|
||||
// writer will be silently discarded.
|
||||
// pwWriter is an io.Writer that extracts the first line of the input stream
|
||||
// and writes it to the password field of the provided callback. The first
|
||||
// line can stretch multiple chunks, but once the first line has been
|
||||
// completed, any subsequent writes to this writer will be silently discarded.
|
||||
type pwWriter struct {
|
||||
w io.Writer
|
||||
cb func(string)
|
||||
buf strings.Builder
|
||||
}
|
||||
|
||||
// Write implements the io.Writer interface for pwWriter.
|
||||
func (p *pwWriter) Write(buf []byte) (int, error) {
|
||||
n, err := p.w.Write(buf)
|
||||
if p.cb == nil {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package secrets
|
||||
|
||||
// PermanentError signal that parsing should not attempt other formats.
|
||||
// PermanentError signals that parsing should not attempt other formats.
|
||||
type PermanentError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error returns the underlying error.
|
||||
func (p *PermanentError) Error() string {
|
||||
return p.Err.Error()
|
||||
}
|
||||
|
||||
@@ -2,5 +2,6 @@ package secrets
|
||||
|
||||
const (
|
||||
// Ident is the header of the deprecated Gopass MIME secret.
|
||||
// It is used to identify a secret as a gopass secret.
|
||||
Ident = "GOPASS-SECRET-1.0"
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
)
|
||||
|
||||
// New creates a new secret.
|
||||
// It returns a new AKV secret.
|
||||
func New() gopass.Secret { //nolint:ireturn
|
||||
return NewAKV()
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// ErrUnknown is returned when the secret is not recognized.
|
||||
// This is a sentinel error.
|
||||
var ErrUnknown = fmt.Errorf("unknown secrets type")
|
||||
|
||||
// parseLegacyMIME is a fallback parser for the transient MIME format.
|
||||
|
||||
@@ -11,8 +11,11 @@ import (
|
||||
"github.com/gopasspw/gopass/pkg/gopass/secrets"
|
||||
)
|
||||
|
||||
// Parse tries to parse a secret. It will start with the most specific
|
||||
// secrets type.
|
||||
// Parse tries to parse a secret from a byte slice. It attempts to parse the
|
||||
// secret in the following order: legacy MIME, YAML, and finally AKV.
|
||||
// If parsing as legacy MIME or YAML fails, it falls back to the next format.
|
||||
// If a permanent error is encountered while parsing as legacy MIME, it returns
|
||||
// the error immediately.
|
||||
//
|
||||
//nolint:ireturn
|
||||
func Parse(in []byte) (gopass.Secret, error) {
|
||||
@@ -49,7 +52,8 @@ func Parse(in []byte) (gopass.Secret, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// MustParse parses a secret or panics. Should only be used for tests.
|
||||
// MustParse parses a secret from a string or panics if an error occurs.
|
||||
// This function should only be used for testing purposes.
|
||||
func MustParse(in string) gopass.Secret {
|
||||
sec, err := Parse([]byte(in))
|
||||
if err != nil {
|
||||
|
||||
@@ -31,7 +31,7 @@ var ErrNotSupported = fmt.Errorf("not supported")
|
||||
// is neither trivial nor intuitive for users manually editing secrets (e.g.
|
||||
// unquoted phone numbers being parsed as octal and such).
|
||||
//
|
||||
// Format
|
||||
// Format:
|
||||
// ------
|
||||
// Line | Description
|
||||
//
|
||||
@@ -45,12 +45,13 @@ type YAML struct {
|
||||
body string
|
||||
}
|
||||
|
||||
// Keys returns all keys.
|
||||
// Keys returns all keys from the YAML data.
|
||||
func (y *YAML) Keys() []string {
|
||||
return set.SortedKeys(y.data)
|
||||
}
|
||||
|
||||
// Get returns the first value of a single key.
|
||||
// It supports yamlpath expressions.
|
||||
func (y *YAML) Get(key string) (string, bool) {
|
||||
if y.data == nil {
|
||||
y.data = make(map[string]any)
|
||||
@@ -67,7 +68,8 @@ func (y *YAML) Get(key string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Values returns Get since as per YAML specification keys must be unique.
|
||||
// Values returns all values for a given key.
|
||||
// Since YAML keys are unique, this is equivalent to Get.
|
||||
func (y *YAML) Values(key string) ([]string, bool) {
|
||||
data, found := y.Get(key)
|
||||
|
||||
@@ -85,9 +87,8 @@ func (y *YAML) Set(key string, value any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ref returns reference in case of having password of the
|
||||
// gopass://ref
|
||||
// which references another secret in the store.
|
||||
// Ref returns a reference in case the password is of the form
|
||||
// gopass://ref, which references another secret in the store.
|
||||
func (y *YAML) Ref() (string, bool) {
|
||||
if strings.HasPrefix(y.password, gopassRef) {
|
||||
return strings.TrimPrefix(y.password, gopassRef), true
|
||||
@@ -96,12 +97,12 @@ func (y *YAML) Ref() (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Add doesn't work since as per YAML specification keys must be unique.
|
||||
// Add is not supported for YAML secrets, as keys must be unique.
|
||||
func (y *YAML) Add(key string, value any) error {
|
||||
return ErrNotSupported
|
||||
}
|
||||
|
||||
// Del removes a single key.
|
||||
// Del removes a single key from the YAML data.
|
||||
func (y *YAML) Del(key string) bool {
|
||||
_, found := y.data[key]
|
||||
|
||||
@@ -110,7 +111,7 @@ func (y *YAML) Del(key string) bool {
|
||||
return found
|
||||
}
|
||||
|
||||
// ParseYAML will try to parse a YAML secret.
|
||||
// ParseYAML will try to parse a YAML secret from a byte slice.
|
||||
func ParseYAML(in []byte) (*YAML, error) {
|
||||
y := &YAML{
|
||||
data: make(map[string]any, 10),
|
||||
@@ -145,7 +146,7 @@ func ParseYAML(in []byte) (*YAML, error) {
|
||||
return y, nil
|
||||
}
|
||||
|
||||
// Body returns the body.
|
||||
// Body returns the body of the secret.
|
||||
func (y *YAML) Body() string {
|
||||
return y.body
|
||||
}
|
||||
@@ -194,7 +195,7 @@ func parseBody(r *bufio.Reader) (string, error) {
|
||||
return "", ErrNoYAML
|
||||
}
|
||||
|
||||
// Bytes serialized this secret.
|
||||
// Bytes serializes this secret to a byte slice.
|
||||
func (y *YAML) Bytes() []byte {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@@ -232,7 +233,7 @@ func (y *YAML) Write(buf []byte) (int, error) {
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
// SafeStr always returnes "(elided)".
|
||||
// SafeStr always returns "(elided)".
|
||||
func (y *YAML) SafeStr() string {
|
||||
return "(elided)"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package gopass provides the interfaces for the gopass password store.
|
||||
package gopass
|
||||
|
||||
import (
|
||||
@@ -5,60 +6,64 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Byter is a minimal secrets write interface.
|
||||
// Byter is a minimal secrets write interface. It is used for writing secrets to the store.
|
||||
type Byter interface {
|
||||
Bytes() []byte
|
||||
}
|
||||
|
||||
// Secret is a secret type.
|
||||
// Secret is a secret type that represents a single secret in the store.
|
||||
type Secret interface {
|
||||
Byter
|
||||
|
||||
// Keys returns a list of all keys in the secret's metadata.
|
||||
Keys() []string
|
||||
// Get returns a single value for that key, use Password() to get the password value.
|
||||
// Get returns a single value for a given key from the secret's metadata.
|
||||
Get(key string) (string, bool)
|
||||
// Values returns all values for that key, use Password() to get the password value.
|
||||
// Values returns all values for a given key from the secret's metadata.
|
||||
Values(key string) ([]string, bool)
|
||||
// Set sets a single header value, use SetPassword() to set the password value.
|
||||
// Set sets a single value for a given key in the secret's metadata.
|
||||
Set(key string, value any) error
|
||||
// Add appends the value to that key, use SetPassword() to set the password value.
|
||||
// Add adds a new value for a given key in the secret's metadata.
|
||||
Add(key string, value any) error
|
||||
// Del removes a single header value
|
||||
// Del removes a key from the secret's metadata.
|
||||
Del(key string) bool
|
||||
|
||||
// Ref returns reference in case of having password of the
|
||||
// gopass://ref
|
||||
// which references another secret in the store.
|
||||
// Ref returns a reference to another secret if the secret is a reference.
|
||||
// A reference is a secret that contains a gopass:// URI.
|
||||
Ref() (string, bool)
|
||||
|
||||
// GetBody returns everything except the header. Use Bytes to get everything.
|
||||
// Body returns the body of the secret, which is everything except the password and metadata.
|
||||
Body() string
|
||||
// Password returns the password of the secret.
|
||||
Password() string
|
||||
// SetPassword sets the password of the secret.
|
||||
SetPassword(string)
|
||||
}
|
||||
|
||||
// Store is a secret store.
|
||||
// Store is a secret store that provides methods for interacting with the password store.
|
||||
type Store interface {
|
||||
fmt.Stringer
|
||||
|
||||
// List all secrets.
|
||||
// List lists all secrets in the store.
|
||||
List(context.Context) ([]string, error)
|
||||
// Get an decrypted secret. Revision defaults to "latest".
|
||||
// Get returns a decrypted secret from the store.
|
||||
// The revision parameter defaults to "latest".
|
||||
Get(ctx context.Context, name, revision string) (Secret, error)
|
||||
// Set (add) a new revision of an secret
|
||||
// Set creates or updates a secret in the store.
|
||||
Set(ctx context.Context, name string, sec Byter) error
|
||||
// Revisions return a list of revisions for a secret.
|
||||
// Revisions returns a list of all revisions of a secret.
|
||||
Revisions(ctx context.Context, name string) ([]string, error)
|
||||
// Remove a single secret.
|
||||
// Remove removes a single secret from the store.
|
||||
Remove(ctx context.Context, name string) error
|
||||
// RemoveAll secrets with a common prefix.
|
||||
// RemoveAll removes all secrets with a common prefix from the store.
|
||||
RemoveAll(ctx context.Context, prefix string) error
|
||||
// Rename a path (secret of prefix) without decrypting.
|
||||
// Rename renames a secret or a folder of secrets.
|
||||
Rename(ctx context.Context, src, dest string) error
|
||||
// Sync with a remote (if configured)
|
||||
// NOTE: We will always auto-sync when mutating the store. Use this to
|
||||
// manually pull in changes.
|
||||
// Sync synchronizes the store with a remote.
|
||||
// NOTE: The store is automatically synchronized when mutated.
|
||||
// This method can be used to manually trigger a synchronization.
|
||||
Sync(ctx context.Context) error
|
||||
// Clean up any resources. MUST be called before the process exists.
|
||||
// Close closes the store and cleans up any resources.
|
||||
// It MUST be called before the program exits.
|
||||
Close(ctx context.Context) error
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ import (
|
||||
"github.com/pquerna/otp"
|
||||
)
|
||||
|
||||
// Calculate will compute a OTP code from a given secret.
|
||||
// Calculate will compute an OTP code from a given secret.
|
||||
// It will look for a field named "otpauth" or "totp" or "hotp".
|
||||
// If none is found it will fall back to the password.
|
||||
//
|
||||
//nolint:ireturn
|
||||
func Calculate(name string, sec gopass.Secret) (*otp.Key, error) {
|
||||
@@ -89,7 +91,7 @@ func parseOTP(typ string, secKey string) (*otp.Key, error) {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// WriteQRFile writes the given OTP code as a QR image to disk.
|
||||
// WriteQRFile writes the given OTP key as a QR image to disk.
|
||||
func WriteQRFile(key *otp.Key, file string) error {
|
||||
// Convert TOTP key into a QR code encoded as a PNG image.
|
||||
var buf bytes.Buffer
|
||||
|
||||
@@ -14,7 +14,8 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CredentialFlags for the credential parameters: https://www.w3.org/TR/webauthn-2/#flags
|
||||
// CredentialFlags for the credential parameters.
|
||||
// See: https://www.w3.org/TR/webauthn-2/#flags
|
||||
type CredentialFlags struct {
|
||||
UserPresent bool
|
||||
UserVerified bool
|
||||
@@ -33,14 +34,16 @@ type Credential struct {
|
||||
Flags CredentialFlags
|
||||
}
|
||||
|
||||
// ClientData for signature: https://www.w3.org/TR/webauthn-1/#sec-client-data
|
||||
// ClientData for signature.
|
||||
// See: https://www.w3.org/TR/webauthn-1/#sec-client-data
|
||||
type ClientData struct {
|
||||
Challenge string `json:"challenge"`
|
||||
Origin string `json:"origin"`
|
||||
CredType string `json:"type"`
|
||||
}
|
||||
|
||||
// Response from a GetAssertion: https://www.w3.org/TR/webauthn-1/#authenticatorGetAssertion-return-values
|
||||
// Response from a GetAssertion.
|
||||
// See: https://www.w3.org/TR/webauthn-1/#authenticatorGetAssertion-return-values
|
||||
type Response struct {
|
||||
AuthenticatorData []byte `json:"authdata"`
|
||||
ClientDataJSON []byte `json:"client_data_json"`
|
||||
@@ -70,7 +73,8 @@ func authDataFlags(options CredentialFlags) uint8 {
|
||||
return flags
|
||||
}
|
||||
|
||||
// CreateCredential is an implementation of the authenticatorMakeCredential Operation: https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred
|
||||
// CreateCredential is an implementation of the authenticatorMakeCredential Operation.
|
||||
// See: https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred
|
||||
func CreateCredential(rp string, user string, flags CredentialFlags) (*Credential, error) {
|
||||
rawID := make([]byte, 32)
|
||||
_, err := rand.Read(rawID)
|
||||
@@ -90,7 +94,8 @@ func CreateCredential(rp string, user string, flags CredentialFlags) (*Credentia
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAssertion is an implementation of the authenticatorGetAssertion Operation: https://www.w3.org/TR/webauthn-2/#authenticatorgetassertion
|
||||
// GetAssertion is an implementation of the authenticatorGetAssertion Operation.
|
||||
// See: https://www.w3.org/TR/webauthn-2/#authenticatorgetassertion
|
||||
func (cred *Credential) GetAssertion(challenge string, origin string) (*Response, error) {
|
||||
credType := "webauthn.get"
|
||||
clientData := ClientData{
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/gopasspw/gopass/pkg/termio"
|
||||
)
|
||||
|
||||
// Client is pinentry CLI drop-in.
|
||||
// Client is a pinentry CLI drop-in.
|
||||
type Client struct {
|
||||
repeat bool
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func (c *Client) Option(string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPINContext prompts for the pin in the termnial and returns the output.
|
||||
// GetPINContext prompts for the pin in the terminal and returns the output.
|
||||
// The context is only used for tests.
|
||||
func (c *Client) GetPINContext(ctx context.Context) (string, error) {
|
||||
pw, err := termio.AskForPassword(ctx, "your PIN", c.repeat)
|
||||
@@ -46,7 +46,7 @@ func (c *Client) GetPINContext(ctx context.Context) (string, error) {
|
||||
return pw, nil
|
||||
}
|
||||
|
||||
// GetPIN prompts for the pin in the termnial and returns the output.
|
||||
// GetPIN prompts for the pin in the terminal and returns the output.
|
||||
func (c *Client) GetPIN() (string, error) {
|
||||
return c.GetPINContext(context.TODO())
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
package protect
|
||||
|
||||
// ProtectEnabled lets us know if we have protection or not.
|
||||
// It is false on all systems except OpenBSD.
|
||||
var ProtectEnabled = false
|
||||
|
||||
// Pledge on any other system than OpenBSD doesn't do anything.
|
||||
|
||||
@@ -5,7 +5,8 @@ package protect
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// ProtectEnabled lets us know if we have protection or not
|
||||
// ProtectEnabled lets us know if we have protection or not.
|
||||
// It is true on OpenBSD.
|
||||
var ProtectEnabled = true
|
||||
|
||||
// Pledge on OpenBSD lets us "promise" to only run a subset of
|
||||
|
||||
@@ -17,6 +17,9 @@ var ErrCrypticInvalid = fmt.Errorf("password does not satisfy all validators")
|
||||
|
||||
// Cryptic is a generator for hard-to-remember passwords as required by (too)
|
||||
// many sites. Prefer memorable or xkcd-style passwords, if possible.
|
||||
//
|
||||
// The generator can be configured with a character set, a length, a maximum
|
||||
// number of tries, and a list of validators.
|
||||
type Cryptic struct {
|
||||
Chars string
|
||||
Length int
|
||||
@@ -25,6 +28,8 @@ type Cryptic struct {
|
||||
}
|
||||
|
||||
// NewCryptic creates a new generator with sane defaults.
|
||||
// The default length is 16, and the default character set is digits, upper and lower case letters.
|
||||
// If symbols is true, the symbol character set is added.
|
||||
func NewCryptic(length int, symbols bool) *Cryptic {
|
||||
if length < 1 {
|
||||
length = 16
|
||||
@@ -45,6 +50,7 @@ func NewCryptic(length int, symbols bool) *Cryptic {
|
||||
|
||||
// NewCrypticForDomain tries to look up password rules for the given domain
|
||||
// or uses the default generator.
|
||||
// It will adjust the length and character set of the generator based on the rules.
|
||||
func NewCrypticForDomain(ctx context.Context, length int, domain string) *Cryptic {
|
||||
c := NewCryptic(length, true)
|
||||
r, found := pwrules.LookupRule(ctx, domain)
|
||||
@@ -149,6 +155,7 @@ func uniqueChars(in string) string {
|
||||
|
||||
// NewCrypticWithAllClasses returns a password generator that generates passwords
|
||||
// containing all available character classes.
|
||||
// This is useful for password policies that require a mix of character types.
|
||||
func NewCrypticWithAllClasses(length int, symbols bool) *Cryptic {
|
||||
c := NewCryptic(length, symbols)
|
||||
c.Validators = append(c.Validators, func(pw string) error {
|
||||
@@ -162,7 +169,7 @@ func NewCrypticWithAllClasses(length int, symbols bool) *Cryptic {
|
||||
return c
|
||||
}
|
||||
|
||||
// NewCrypticWithCrunchy returns a password generators that only returns a
|
||||
// NewCrypticWithCrunchy returns a password generator that only returns a
|
||||
// password if it's successfully validated with crunchy.
|
||||
func NewCrypticWithCrunchy(length int, symbols bool) *Cryptic {
|
||||
c := NewCryptic(length, symbols)
|
||||
@@ -174,6 +181,8 @@ func NewCrypticWithCrunchy(length int, symbols bool) *Cryptic {
|
||||
}
|
||||
|
||||
// Password returns a single password from the generator.
|
||||
// It will try to generate a password that satisfies all validators.
|
||||
// If it fails after MaxTries, it will return an empty string.
|
||||
func (c *Cryptic) Password() string {
|
||||
round := 0
|
||||
maxFn := func() bool {
|
||||
|
||||
@@ -18,7 +18,8 @@ var (
|
||||
)
|
||||
|
||||
// GenerateExternal will invoke an external password generator,
|
||||
// if set, and return it's output.
|
||||
// if set, and return its output.
|
||||
// The external generator is configured via the GOPASS_EXTERNAL_PWGEN environment variable.
|
||||
func GenerateExternal(pwlen int) (string, error) {
|
||||
c := os.Getenv("GOPASS_EXTERNAL_PWGEN")
|
||||
if c == "" {
|
||||
|
||||
@@ -4,6 +4,9 @@ import "strings"
|
||||
|
||||
// GenerateMemorablePassword will generate a memorable password
|
||||
// with a minimum length.
|
||||
// It will use a wordlist to generate the password.
|
||||
// If symbols is true, it will add symbols to the password.
|
||||
// If capitals is true, it will capitalize some words.
|
||||
func GenerateMemorablePassword(minLength int, symbols bool, capitals bool) string {
|
||||
var sb strings.Builder
|
||||
|
||||
|
||||
@@ -16,11 +16,16 @@ var ErrMaxTries = fmt.Errorf("maximum tries exceeded")
|
||||
|
||||
// Character classes.
|
||||
const (
|
||||
// Digits is the class of digits.
|
||||
Digits = "0123456789"
|
||||
Upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
Lower = "abcdefghijklmnopqrstuvwxyz"
|
||||
Syms = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
|
||||
Ambiq = "0ODQ1IlB8G6S5Z2"
|
||||
// Upper is the class of upper case letters.
|
||||
Upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
// Lower is the class of lower case letters.
|
||||
Lower = "abcdefghijklmnopqrstuvwxyz"
|
||||
// Syms is the class of symbols.
|
||||
Syms = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
|
||||
// Ambiq is the class of ambiguous characters.
|
||||
Ambiq = "0ODQ1IlB8G6S5Z2"
|
||||
// CharAlpha is the class of letters.
|
||||
CharAlpha = Upper + Lower
|
||||
// CharAlphaNum is the class of alpha-numeric characters.
|
||||
@@ -29,7 +34,7 @@ const (
|
||||
CharAll = Digits + Upper + Lower + Syms
|
||||
)
|
||||
|
||||
// GeneratePassword generates a random, hard to remember password.
|
||||
// GeneratePassword generates a random, hard-to-remember password.
|
||||
func GeneratePassword(length int, symbols bool) string {
|
||||
chars := Digits + Upper + Lower
|
||||
if symbols {
|
||||
@@ -45,6 +50,7 @@ func GeneratePassword(length int, symbols bool) string {
|
||||
|
||||
// GeneratePasswordCharset generates a random password from a given
|
||||
// set of characters.
|
||||
// It does not perform any checks on the generated password.
|
||||
func GeneratePasswordCharset(length int, chars string) string {
|
||||
c := NewCryptic(length, false)
|
||||
c.Chars = chars
|
||||
@@ -56,6 +62,8 @@ func GeneratePasswordCharset(length int, chars string) string {
|
||||
// contains all character classes instead of only enabling them.
|
||||
// This is especially useful for broken (corporate) password policies
|
||||
// that mandate the use of certain character classes for no good reason.
|
||||
// It will try to generate a password that contains at least one character from each of the
|
||||
// enabled character classes (digits, upper, lower, symbols).
|
||||
func GeneratePasswordWithAllClasses(length int, symbols bool) (string, error) {
|
||||
c := NewCrypticWithAllClasses(length, symbols)
|
||||
if pw := c.Password(); pw != "" {
|
||||
@@ -67,6 +75,7 @@ func GeneratePasswordWithAllClasses(length int, symbols bool) (string, error) {
|
||||
|
||||
// GeneratePasswordCharsetCheck generates a random password from a given
|
||||
// set of characters and validates the generated password with crunchy.
|
||||
// It will try to generate a password that passes the crunchy check.
|
||||
func GeneratePasswordCharsetCheck(length int, chars string) string {
|
||||
c := NewCrypticWithCrunchy(length, false)
|
||||
c.Chars = chars
|
||||
@@ -74,7 +83,7 @@ func GeneratePasswordCharsetCheck(length int, chars string) string {
|
||||
return c.Password()
|
||||
}
|
||||
|
||||
// Prune removes all characters in cutset from the input.
|
||||
// Prune removes all characters in cutset from the input string.
|
||||
func Prune(in string, cutset string) string {
|
||||
out := make([]rune, 0, len(in))
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// LookupAliases looks up known aliases for the given domain.
|
||||
// It will check for both built-in and custom aliases.
|
||||
func LookupAliases(ctx context.Context, domain string) []string {
|
||||
customAliases := loadCustomAliases(ctx)
|
||||
aliases := make([]string, 0, len(genAliases[domain])+len(customAliases[domain]))
|
||||
@@ -21,7 +22,7 @@ func LookupAliases(ctx context.Context, domain string) []string {
|
||||
return aliases
|
||||
}
|
||||
|
||||
// AllAliases returns all aliases.
|
||||
// AllAliases returns all aliases, including built-in and custom aliases.
|
||||
func AllAliases(ctx context.Context) map[string][]string {
|
||||
customAliases := loadCustomAliases(ctx)
|
||||
all := make(map[string][]string, len(genAliases)+len(customAliases))
|
||||
|
||||
@@ -16,7 +16,7 @@ func init() {
|
||||
}
|
||||
|
||||
// LookupChangeURL looks up a change URL, either directly or through
|
||||
// one of it's know aliases.
|
||||
// one of its known aliases.
|
||||
func LookupChangeURL(ctx context.Context, domain string) string {
|
||||
if u, found := changeURLs[domain]; found {
|
||||
return u
|
||||
|
||||
@@ -14,12 +14,12 @@ import (
|
||||
|
||||
var reChars = regexp.MustCompile(`(allowed|required):\s*\[(.*)\](?:;|,)`)
|
||||
|
||||
// AllRules returns all rules.
|
||||
// AllRules returns all password rules.
|
||||
func AllRules() map[string]Rule {
|
||||
return genRules
|
||||
}
|
||||
|
||||
// LookupRule looks up a rule either directly or through one of it's know
|
||||
// LookupRule looks up a rule either directly or through one of its known
|
||||
// aliases.
|
||||
func LookupRule(ctx context.Context, domain string) (Rule, bool) {
|
||||
r, found := genRules[domain]
|
||||
@@ -36,7 +36,8 @@ func LookupRule(ctx context.Context, domain string) (Rule, bool) {
|
||||
return Rule{}, false
|
||||
}
|
||||
|
||||
// Rule is a password rule as defined by Apple at https://developer.apple.com/password-rules/
|
||||
// Rule is a password rule as defined by Apple.
|
||||
// See: https://developer.apple.com/password-rules/
|
||||
type Rule struct {
|
||||
Minlen int
|
||||
Maxlen int
|
||||
|
||||
@@ -15,7 +15,7 @@ func Random() string {
|
||||
return password
|
||||
}
|
||||
|
||||
// RandomLength returns a random passphrase combined from the desired number.
|
||||
// RandomLength returns a random passphrase combined from the desired number
|
||||
// of words. Words are drawn from lang.
|
||||
func RandomLength(length int, lang string) (string, error) {
|
||||
return RandomLengthDelim(length, " ", lang, false, false)
|
||||
|
||||
@@ -18,8 +18,8 @@ const (
|
||||
// ErrUnknowColor is returned when the color is unknown.
|
||||
var ErrUnknowColor = fmt.Errorf("unknown color")
|
||||
|
||||
// QRCode returns a string containing an ANSI encoded
|
||||
// QR Code.
|
||||
// QRCode returns a string containing an ANSI encoded QR Code.
|
||||
// This can be printed to a terminal that supports ANSI escape codes.
|
||||
func QRCode(content string) (string, error) {
|
||||
q, err := qrcode.New(content, qrcode.Medium)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package set
|
||||
|
||||
// Filter filters all r's from the input list.
|
||||
// Filter filters all elements in r from the input list.
|
||||
func Filter[K comparable](in []K, r ...K) []K {
|
||||
rs := Map(r)
|
||||
var out []K
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package set
|
||||
|
||||
// Map takes a slice of a given type and create a boolean map with keys
|
||||
// Map takes a slice of a given type and creates a boolean map with keys
|
||||
// of that type.
|
||||
func Map[K comparable](in []K) map[K]bool {
|
||||
m := make(map[K]bool, len(in))
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// Set is a generic set type.
|
||||
// Set is a generic set type, implemented as a map of keys to booleans.
|
||||
type Set[K constraints.Ordered] map[K]bool
|
||||
|
||||
// New initializes a new Set with the given elements.
|
||||
@@ -78,7 +78,7 @@ func (s Set[K]) Equals(s2 Set[K]) bool {
|
||||
return len(s) == len(s2) && s.IsSubset(s2)
|
||||
}
|
||||
|
||||
// Contains returs true if the set contains the presented
|
||||
// Contains returns true if the set contains the given
|
||||
// element.
|
||||
func (s Set[K]) Contains(e K) bool {
|
||||
for k := range s {
|
||||
@@ -194,7 +194,7 @@ func (s *Set[K]) Add(elems ...K) bool {
|
||||
return len(*s) != il
|
||||
}
|
||||
|
||||
// Remove deletes the given element from the set.
|
||||
// Remove deletes the given elements from the set.
|
||||
func (s Set[K]) Remove(s2 Set[K]) bool {
|
||||
if s.Empty() {
|
||||
return false
|
||||
@@ -220,8 +220,8 @@ func (s Set[K]) Discard(elems ...K) bool {
|
||||
return len(s) != il
|
||||
}
|
||||
|
||||
// Map returns a new set by applied the function f
|
||||
// to all it's elements.
|
||||
// Map returns a new set by applying the function f
|
||||
// to all its elements.
|
||||
func (s Set[K]) Map(f func(K) K) Set[K] {
|
||||
out := make(Set[K], len(s))
|
||||
for k := range s {
|
||||
@@ -231,7 +231,7 @@ func (s Set[K]) Map(f func(K) K) Set[K] {
|
||||
return out
|
||||
}
|
||||
|
||||
// Each applies the function f to all it's elements.
|
||||
// Each applies the function f to all its elements.
|
||||
func (s Set[K]) Each(f func(K)) {
|
||||
for k := range s {
|
||||
f(k)
|
||||
@@ -239,7 +239,7 @@ func (s Set[K]) Each(f func(K)) {
|
||||
}
|
||||
|
||||
// Select returns a new set with all the elements for
|
||||
// that f returns true.
|
||||
// which f returns true.
|
||||
func (s Set[K]) Select(f func(K) bool) Set[K] {
|
||||
out := make(Set[K], len(s))
|
||||
for k := range s {
|
||||
@@ -252,7 +252,7 @@ func (s Set[K]) Select(f func(K) bool) Set[K] {
|
||||
}
|
||||
|
||||
// Partition returns two new sets: the first contains all
|
||||
// the elements for which f returns true. The seconds the others.
|
||||
// the elements for which f returns true. The second contains the others.
|
||||
func (s Set[K]) Partition(f func(K) bool) (Set[K], Set[K]) {
|
||||
yes := make(Set[K], len(s))
|
||||
no := make(Set[K], len(s))
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
// SortedKeys returns the sorted keys of the map.
|
||||
// The keys are sorted in ascending order.
|
||||
func SortedKeys[K constraints.Ordered, V any](m map[K]V) []K {
|
||||
// sort
|
||||
keys := maps.Keys(m)
|
||||
@@ -16,6 +17,7 @@ func SortedKeys[K constraints.Ordered, V any](m map[K]V) []K {
|
||||
}
|
||||
|
||||
// Sorted returns a sorted set of the input.
|
||||
// Duplicates are removed.
|
||||
func Sorted[K constraints.Ordered](l []K) []K {
|
||||
return SortedFiltered(l, func(k K) bool {
|
||||
return true
|
||||
@@ -23,6 +25,7 @@ func Sorted[K constraints.Ordered](l []K) []K {
|
||||
}
|
||||
|
||||
// SortedFiltered returns a sorted set of the input, filtered by the predicate.
|
||||
// Duplicates are removed.
|
||||
func SortedFiltered[K constraints.Ordered](l []K, want func(K) bool) []K {
|
||||
if len(l) == 0 {
|
||||
return l
|
||||
|
||||
@@ -15,7 +15,7 @@ var ErrNotInit = fmt.Errorf("not initialized")
|
||||
// globalPrefix is prefixed to all temporary dirs.
|
||||
var globalPrefix string
|
||||
|
||||
// File is a temporary file.
|
||||
// File is a temporary file that is stored on a ramdisk if possible.
|
||||
type File struct {
|
||||
dir string
|
||||
dev string
|
||||
@@ -23,6 +23,8 @@ type File struct {
|
||||
}
|
||||
|
||||
// New returns a new tempfile wrapper.
|
||||
// It will create a temporary directory and a file inside it.
|
||||
// If possible, it will mount a ramdisk to the temporary directory.
|
||||
func New(ctx context.Context, prefix string) (*File, error) {
|
||||
td, err := os.MkdirTemp(tempdirBase(), globalPrefix+prefix)
|
||||
if err != nil {
|
||||
|
||||
@@ -95,7 +95,7 @@ func AskForBool(ctx context.Context, text string, def bool) (bool, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// AskForInt asks for an valid interger once. If the input
|
||||
// AskForInt asks for a valid integer once. If the input
|
||||
// can not be converted to an int it returns an error.
|
||||
func AskForInt(ctx context.Context, text string, def int) (int, error) {
|
||||
if ctxutil.IsAlwaysYes(ctx) {
|
||||
@@ -121,6 +121,7 @@ func AskForInt(ctx context.Context, text string, def int) (int, error) {
|
||||
|
||||
// AskForConfirmation asks a yes/no question until the user
|
||||
// replies yes or no.
|
||||
// It will retry until a valid answer is given or the user aborts.
|
||||
func AskForConfirmation(ctx context.Context, text string) bool {
|
||||
if ctxutil.IsAlwaysYes(ctx) {
|
||||
return true
|
||||
@@ -141,6 +142,7 @@ func AskForConfirmation(ctx context.Context, text string) bool {
|
||||
}
|
||||
|
||||
// AskForKeyImport asks for permissions to import the named key.
|
||||
// It will ask the user for confirmation.
|
||||
func AskForKeyImport(ctx context.Context, key string, names []string) bool {
|
||||
if ctxutil.IsAlwaysYes(ctx) {
|
||||
return true
|
||||
@@ -159,6 +161,7 @@ func AskForKeyImport(ctx context.Context, key string, names []string) bool {
|
||||
}
|
||||
|
||||
// AskForPassword prompts for a password, optionally prompting twice until both match.
|
||||
// It will retry until the passwords match or the user aborts.
|
||||
func AskForPassword(ctx context.Context, name string, repeat bool) (string, error) {
|
||||
if ctxutil.IsAlwaysYes(ctx) {
|
||||
return "", nil
|
||||
|
||||
@@ -25,7 +25,7 @@ func HasPassPromptFunc(ctx context.Context) bool {
|
||||
return ok && ppf != nil
|
||||
}
|
||||
|
||||
// GetPassPromptFunc will return the password prompt func or a default one
|
||||
// GetPassPromptFunc will return the password prompt func or a default one.
|
||||
// Note: will never return nil.
|
||||
func GetPassPromptFunc(ctx context.Context) PassPromptFunc {
|
||||
ppf, ok := ctx.Value(ctxKeyPassPromptFunc).(PassPromptFunc)
|
||||
@@ -37,12 +37,14 @@ func GetPassPromptFunc(ctx context.Context) PassPromptFunc {
|
||||
}
|
||||
|
||||
// WithWorkdir returns a context with the working directory option set.
|
||||
// The working directory is used to resolve relative paths.
|
||||
func WithWorkdir(ctx context.Context, dir string) context.Context {
|
||||
return context.WithValue(ctx, ctxKeyWorkdir, dir)
|
||||
}
|
||||
|
||||
// GetWorkdir returns the working directory from the context or an empty
|
||||
// string if it is not set.
|
||||
// The working directory is used to resolve relative paths.
|
||||
func GetWorkdir(ctx context.Context) string {
|
||||
sv, ok := ctx.Value(ctxKeyWorkdir).(string)
|
||||
if !ok {
|
||||
|
||||
@@ -25,6 +25,8 @@ var (
|
||||
)
|
||||
|
||||
// DetectName tries to guess the name of the logged in user.
|
||||
// It checks the context, the command line flags, environment variables,
|
||||
// and the git config.
|
||||
func DetectName(ctx context.Context, c *cli.Context) string {
|
||||
cand := make([]string, 0, 10)
|
||||
cand = append(cand, ctxutil.GetUsername(ctx))
|
||||
@@ -50,6 +52,8 @@ func DetectName(ctx context.Context, c *cli.Context) string {
|
||||
}
|
||||
|
||||
// DetectEmail tries to guess the email of the logged in user.
|
||||
// It checks the context, the command line flags, environment variables,
|
||||
// and the git config.
|
||||
func DetectEmail(ctx context.Context, c *cli.Context) string {
|
||||
cand := make([]string, 0, 10)
|
||||
cand = append(cand, ctxutil.GetEmail(ctx))
|
||||
|
||||
@@ -20,6 +20,7 @@ const (
|
||||
var now = time.Now
|
||||
|
||||
// ProgressBar is a gopass progress bar.
|
||||
// It is used to display progress for long-running operations.
|
||||
type ProgressBar struct {
|
||||
// keep both int64 fields at the top to ensure correct
|
||||
// 8-byte alignment on 32 bit systems. See https://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||
|
||||
Reference in New Issue
Block a user