mirror of
https://github.com/gopasspw/gopass.git
synced 2025-12-08 19:24:54 +00:00
These backends are not ready, yet. Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
224 lines
6.5 KiB
Go
224 lines
6.5 KiB
Go
package action
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/gopasspw/gopass/internal/action/exit"
|
|
"github.com/gopasspw/gopass/internal/backend"
|
|
"github.com/gopasspw/gopass/internal/config"
|
|
"github.com/gopasspw/gopass/internal/cui"
|
|
"github.com/gopasspw/gopass/internal/out"
|
|
"github.com/gopasspw/gopass/pkg/ctxutil"
|
|
"github.com/gopasspw/gopass/pkg/debug"
|
|
"github.com/gopasspw/gopass/pkg/fsutil"
|
|
"github.com/gopasspw/gopass/pkg/termio"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
const logo = `
|
|
__ _ _ _ _ _ ___ ___
|
|
/'_ '\ /'_'\ ( '_'\ /'_' )/',__)/',__)
|
|
( (_) |( (_) )| (_) )( (_| |\__, \\__, \
|
|
'\__ |'\___/'| ,__/''\__,_)(____/(____/
|
|
( )_) | | |
|
|
\___/' (_)
|
|
`
|
|
|
|
// IsInitialized returns an error if the store is not properly
|
|
// prepared.
|
|
func (s *Action) IsInitialized(c *cli.Context) error {
|
|
ctx := ctxutil.WithGlobalFlags(c)
|
|
inited, err := s.Store.IsInitialized(ctx)
|
|
if err != nil {
|
|
return exit.Error(exit.Unknown, err, "Failed to initialize store: %s", err)
|
|
}
|
|
|
|
if inited {
|
|
debug.Log("Store is fully initialized and ready to go\n\nAll systems go. 🚀\n")
|
|
name := c.Args().First()
|
|
// setting the mount point here is not enough when we're using the REPL mode
|
|
ctx = config.WithMount(ctx, s.Store.MountPoint(name))
|
|
s.printReminder(ctx)
|
|
if c.Command.Name != "sync" && !c.Bool("nosync") {
|
|
_ = s.autoSync(ctx)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
debug.Log("Store needs to be initialized.\n\nAbort. Abort. Abort. 🚫\n")
|
|
if !ctxutil.IsInteractive(ctx) {
|
|
return exit.Error(exit.NotInitialized, nil, "password-store is not initialized. Try '%s init'", s.Name)
|
|
}
|
|
|
|
out.Printf(ctx, logo)
|
|
out.Printf(ctx, "🌟 Welcome to gopass!")
|
|
out.Noticef(ctx, "No existing configuration found.")
|
|
|
|
contSetup, err := termio.AskForBool(ctx, "❓ Do you want to continue to setup?", false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if contSetup {
|
|
return s.Setup(c)
|
|
}
|
|
|
|
out.Printf(ctx, "☝ Please run 'gopass setup'")
|
|
|
|
return exit.Error(exit.NotInitialized, err, "not initialized")
|
|
}
|
|
|
|
// Init a new password store with a first gpg id.
|
|
func (s *Action) Init(c *cli.Context) error {
|
|
ctx := ctxutil.WithGlobalFlags(c)
|
|
path := c.String("path")
|
|
alias := c.String("store")
|
|
|
|
ctx, err := initParseContext(ctx, c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out.Printf(ctx, "🍭 Initializing a new password store ...")
|
|
|
|
if name := termio.DetectName(c.Context, c); name != "" {
|
|
ctx = ctxutil.WithUsername(ctx, name)
|
|
}
|
|
|
|
if email := termio.DetectEmail(c.Context, c); email != "" {
|
|
ctx = ctxutil.WithEmail(ctx, email)
|
|
}
|
|
|
|
inited, err := s.Store.IsInitialized(ctx)
|
|
if err != nil {
|
|
return exit.Error(exit.Unknown, err, "Failed to initialized store: %s", err)
|
|
}
|
|
|
|
if inited {
|
|
out.Errorf(ctx, "Store is already initialized!")
|
|
}
|
|
|
|
if err := s.init(ctx, alias, path, c.Args().Slice()...); err != nil {
|
|
return exit.Error(exit.Unknown, err, "Failed to initialize store: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func initParseContext(ctx context.Context, c *cli.Context) (context.Context, error) {
|
|
if c.IsSet("crypto") {
|
|
var err error
|
|
ctx, err = backend.WithCryptoBackendString(ctx, c.String("crypto"))
|
|
if err != nil {
|
|
return ctx, exit.Error(exit.Unknown, err, "Failed to set crypto backend: %s", err)
|
|
}
|
|
}
|
|
|
|
if c.IsSet("storage") {
|
|
var err error
|
|
ctx, err = backend.WithStorageBackendString(ctx, c.String("storage"))
|
|
if err != nil {
|
|
return ctx, exit.Error(exit.Unknown, err, "Failed to set storage backend: %s", err)
|
|
}
|
|
}
|
|
|
|
if !backend.HasCryptoBackend(ctx) {
|
|
debug.Log("Using default Crypto Backend (GPGCLI)")
|
|
ctx = backend.WithCryptoBackend(ctx, backend.GPGCLI)
|
|
}
|
|
|
|
if !backend.HasStorageBackend(ctx) {
|
|
debug.Log("Using default storage backend (GitFS)")
|
|
ctx = backend.WithStorageBackend(ctx, backend.GitFS)
|
|
}
|
|
|
|
sb := backend.GetStorageBackend(ctx)
|
|
if sb == backend.CryptFS {
|
|
out.Warning(ctx, "⚠ CryptFS is an experimental backend. Use at your own risk! ⚠")
|
|
}
|
|
if sb == backend.JJFS {
|
|
out.Warning(ctx, "⚠ JJFS is an experimental backend. Use at your own risk! ⚠")
|
|
}
|
|
|
|
return ctx, nil
|
|
}
|
|
|
|
func (s *Action) init(ctx context.Context, alias, path string, keys ...string) error {
|
|
if path == "" {
|
|
if alias != "" {
|
|
path = config.PwStoreDir(alias)
|
|
} else {
|
|
path = s.Store.Path()
|
|
}
|
|
}
|
|
path = fsutil.CleanPath(path)
|
|
|
|
debug.Log("Initializing Store %q in %q for %+v", alias, path, keys)
|
|
|
|
out.Printf(ctx, "🔑 Searching for usable private Keys ...")
|
|
debug.Log("Checking private keys for: %+v", keys)
|
|
crypto := s.getCryptoFor(ctx, alias)
|
|
|
|
// private key selection doesn't matter for plain. save one question.
|
|
// TODO should ask the backend
|
|
if crypto.Name() == "plain" {
|
|
keys, _ = crypto.ListIdentities(ctx)
|
|
}
|
|
|
|
if len(keys) < 1 {
|
|
if crypto.Name() != "age" {
|
|
out.Notice(ctx, "Hint: Use 'gopass init <subkey> to use subkeys!'")
|
|
}
|
|
nk, err := cui.AskForPrivateKey(ctx, crypto, "🎮 Please select a private key for encrypting secrets:")
|
|
if err != nil {
|
|
out.Noticef(ctx, "Hint: Use 'gopass setup --crypto %s' to be guided through an initial setup instead of 'gopass init'", crypto.Name())
|
|
|
|
return fmt.Errorf("failed to read user input: %w", err)
|
|
}
|
|
keys = []string{nk}
|
|
}
|
|
|
|
debug.Log("Initializing sub store - Alias: %q - Path: %q - Keys: %+v", alias, path, keys)
|
|
if err := s.Store.Init(ctx, alias, path, keys...); err != nil {
|
|
return fmt.Errorf("failed to init store %q at %q: %w", alias, path, err)
|
|
}
|
|
|
|
if alias != "" && path != "" {
|
|
debug.Log("Mounting sub store %q -> %q", alias, path)
|
|
if err := s.Store.AddMount(ctx, alias, path); err != nil {
|
|
return fmt.Errorf("failed to add mount %q: %w", alias, err)
|
|
}
|
|
}
|
|
|
|
if backend.HasStorageBackend(ctx) {
|
|
bn := backend.StorageBackendName(backend.GetStorageBackend(ctx))
|
|
debug.Log("Initializing RCS (%s) ...", bn)
|
|
if err := s.rcsInit(ctx, alias, ctxutil.GetUsername(ctx), ctxutil.GetEmail(ctx)); err != nil {
|
|
debug.Log("Stacktrace: %+v\n", err)
|
|
out.Errorf(ctx, "❌ Failed to init Version Control (%s): %s", bn, err)
|
|
}
|
|
debug.Log("RCS initialized as %s", s.Store.Storage(ctx, alias).Name())
|
|
} else {
|
|
debug.Log("not initializing RCS backend ...")
|
|
}
|
|
|
|
out.Printf(ctx, "🏁 Password store %s initialized for:", path)
|
|
s.printRecipients(ctx, alias)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Action) printRecipients(ctx context.Context, alias string) {
|
|
crypto := s.Store.Crypto(ctx, alias)
|
|
for _, recipient := range s.Store.ListRecipients(ctx, alias) {
|
|
if kl, err := crypto.FindRecipients(ctx, recipient); err == nil && len(kl) > 0 {
|
|
recipient = crypto.FormatKey(ctx, kl[0], "")
|
|
}
|
|
out.Printf(ctx, "📩 "+recipient)
|
|
}
|
|
}
|
|
|
|
func (s *Action) getCryptoFor(ctx context.Context, name string) backend.Crypto {
|
|
return s.Store.Crypto(ctx, name)
|
|
}
|