1package leaf 2 3import ( 4 "context" 5 "fmt" 6 "path/filepath" 7 "sort" 8 "strings" 9 10 "github.com/gopasspw/gopass/internal/backend" 11 "github.com/gopasspw/gopass/pkg/debug" 12) 13 14// Store is password store 15type Store struct { 16 alias string 17 path string 18 crypto backend.Crypto 19 storage backend.Storage 20} 21 22// Init initializes this sub store 23func Init(ctx context.Context, alias, path string) (*Store, error) { 24 debug.Log("Initializing %s at %s", alias, path) 25 s := &Store{ 26 alias: alias, 27 path: path, 28 } 29 30 st, err := backend.InitStorage(ctx, backend.GetStorageBackend(ctx), path) 31 if err != nil { 32 return nil, err 33 } 34 s.storage = st 35 debug.Log("Storage initialized") 36 37 crypto, err := backend.NewCrypto(ctx, backend.GetCryptoBackend(ctx)) 38 if err != nil { 39 return nil, err 40 } 41 s.crypto = crypto 42 debug.Log("Crypto initialized") 43 44 return s, nil 45} 46 47// New creates a new store 48func New(ctx context.Context, alias, path string) (*Store, error) { 49 debug.Log("Instantiating %s at %s", alias, path) 50 51 s := &Store{ 52 alias: alias, 53 path: path, 54 } 55 56 // init storage and rcs backend 57 if err := s.initStorageBackend(ctx); err != nil { 58 return nil, fmt.Errorf("failed to init storage backend: %w", err) 59 } 60 debug.Log("Storage initialized") 61 62 // init crypto backend 63 if err := s.initCryptoBackend(ctx); err != nil { 64 return nil, fmt.Errorf("failed to init crypto backend: %w", err) 65 } 66 debug.Log("Crypto initialized") 67 68 debug.Log("Instantiated %s at %s - storage: %+#v - crypto: %+#v", alias, path, s.storage, s.crypto) 69 return s, nil 70} 71 72// idFile returns the path to the recipient list for this store 73// it walks up from the given filename until it finds a directory containing 74// a gpg id file or it leaves the scope of storage. 75func (s *Store) idFile(ctx context.Context, name string) string { 76 if s.crypto == nil { 77 return "" 78 } 79 fn := name 80 var cnt uint8 81 for { 82 cnt++ 83 if cnt > 100 { 84 break 85 } 86 if fn == "" || fn == Sep { 87 break 88 } 89 gfn := filepath.Join(fn, s.crypto.IDFile()) 90 if s.storage.Exists(ctx, gfn) { 91 return gfn 92 } 93 fn = filepath.Dir(fn) 94 } 95 return s.crypto.IDFile() 96} 97 98// idFiles returns the path to all id files in this store. 99func (s *Store) idFiles(ctx context.Context) []string { 100 if s.crypto == nil { 101 return nil 102 } 103 files, err := s.Storage().List(ctx, "") 104 if err != nil { 105 return nil 106 } 107 fileSet := make(map[string]struct{}, len(files)) 108 for _, file := range files { 109 if strings.HasPrefix(filepath.Base(file), ".") { 110 continue 111 } 112 idf := s.idFile(ctx, file) 113 if s.storage.Exists(ctx, idf) { 114 fileSet[idf] = struct{}{} 115 } 116 } 117 out := make([]string, 0, len(fileSet)) 118 for file := range fileSet { 119 out = append(out, file) 120 } 121 sort.Strings(out) 122 return out 123} 124 125// Equals returns true if this.storage has the same on-disk path as the other 126func (s *Store) Equals(other *Store) bool { 127 if other == nil { 128 return false 129 } 130 return s.Path() == other.Path() 131} 132 133// IsDir returns true if the entry is folder inside the store 134func (s *Store) IsDir(ctx context.Context, name string) bool { 135 return s.storage.IsDir(ctx, name) 136} 137 138// Exists checks the existence of a single entry 139func (s *Store) Exists(ctx context.Context, name string) bool { 140 return s.storage.Exists(ctx, s.passfile(name)) 141} 142 143func (s *Store) useableKeys(ctx context.Context, name string) ([]string, error) { 144 rs, err := s.GetRecipients(ctx, name) 145 if err != nil { 146 return nil, fmt.Errorf("failed to get recipients: %w", err) 147 } 148 149 if !IsCheckRecipients(ctx) { 150 return rs, nil 151 } 152 153 kl, err := s.crypto.FindRecipients(ctx, rs...) 154 if err != nil { 155 return rs, err 156 } 157 158 return kl, nil 159} 160 161// passfile returns the name of gpg file on disk, for the given key/name 162func (s *Store) passfile(name string) string { 163 return strings.TrimPrefix(name+"."+s.crypto.Ext(), "/") 164} 165 166// String implement fmt.Stringer 167func (s *Store) String() string { 168 return fmt.Sprintf("Store(Alias: %s, Path: %s)", s.alias, s.path) 169} 170 171// Path returns the value of path 172func (s *Store) Path() string { 173 return s.path 174} 175 176// Alias returns the value of alias 177func (s *Store) Alias() string { 178 return s.alias 179} 180 181// Storage returns the storage backend used by this.storage 182func (s *Store) Storage() backend.Storage { 183 return s.storage 184} 185 186// Valid returns true if this store is not nil 187func (s *Store) Valid() bool { 188 return s != nil 189} 190