-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathkeyring.go
203 lines (178 loc) · 5.59 KB
/
keyring.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package keyring
import (
"errors"
"github.com/launchrctl/launchr"
)
const defaultFileYaml = "keyring.yaml"
var (
ErrNotFound = errors.New("item not found") // ErrNotFound if an item was not found
ErrEmptyFields = errors.New("item can't be empty") // ErrEmptyFields if fields are empty
ErrEmptyPass = errors.New("passphrase can't be empty") // ErrEmptyPass if a passphrase is empty
ErrKeyringMalformed = errors.New("the keyring is malformed") // ErrKeyringMalformed when keyring can't be read.
)
// SecretItem is an interface that represents an item saved in a storage.
// It is used in the DataStore interface for adding and manipulating items.
type SecretItem interface {
isEmpty() bool
}
// CredentialsItem stores credentials.
type CredentialsItem struct {
URL string `yaml:"url"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
func (i CredentialsItem) isEmpty() bool {
return i.URL == "" || i.Username == "" || i.Password == ""
}
// KeyValueItem stores key-value pair.
type KeyValueItem struct {
Key string `yaml:"key"`
Value string `yaml:"value"`
}
func (i KeyValueItem) isEmpty() bool {
return i.Key == "" || i.Value == ""
}
// DataStore provides password storage functionality.
type DataStore interface {
// GetForURL returns a credentials item by a URL.
// Error is returned if either the keyring could not be unlocked
// Error ErrNotFound if the credentials were not found.
GetForURL(url string) (CredentialsItem, error)
// GetForKey returns a key-value item by a key.
// Error is returned if either the keyring could not be unlocked
// Error ErrNotFound if the key was not found.
GetForKey(key string) (KeyValueItem, error)
// AddItem adds a new credential item.
// Error is returned if the vault couldn't be unlocked.
// Error ErrEmptyFields is returned if item is empty.
AddItem(SecretItem) error
// RemoveByURL deletes an item by url.
// Error is returned if the vault couldn't be unlocked.
// Error ErrNotFound if the credentials were not found.
RemoveByURL(url string) error
// RemoveByKey deletes an item by key.
// Error is returned if the vault couldn't be unlocked.
// Error ErrNotFound if the credentials were not found.
RemoveByKey(key string) error
// CleanStorage cleanups storage (credentials or key-value).
// Error is returned if the vault couldn't be unlocked.
CleanStorage(item SecretItem) error
// Exists checks if keyring exists in persistent storage.
Exists() bool
// Save saves the keyring to the persistent storage.
Save() error
// Destroy removes the keyring from the persistent storage.
Destroy() error
}
// Keyring is a [launchr.Service] providing password store functionality.
type Keyring interface {
launchr.Service
DataStore
ResetStorage()
}
type keyringService struct {
fname string
store DataStore
cfg launchr.Config
}
func newKeyringService(cfg launchr.Config) Keyring {
return &keyringService{
fname: cfg.Path(defaultFileYaml),
cfg: cfg,
}
}
// ServiceInfo implements [launchr.Service] interface.
func (k *keyringService) ServiceInfo() launchr.ServiceInfo {
return launchr.ServiceInfo{}
}
// ResetStorage cleans store for subsequent reload.
func (k *keyringService) ResetStorage() {
k.store = nil
}
func (k *keyringService) defaultStore() (DataStore, error) {
if k.store != nil {
return k.store, nil
}
var askPass AskPass
if passphrase != "" {
askPass = AskPassConstFlow(passphrase)
} else {
askPass = AskPassWithTerminal{}
}
// @todo parse header to know if it's encrypted or not.
// @todo do not encrypt if the passphrase is not provided.
k.store = &dataStoreYaml{file: newAgeFile(k.fname, askPass)}
return k.store, nil
}
// GetForURL implements DataStore interface. Uses service default store.
func (k *keyringService) GetForURL(url string) (CredentialsItem, error) {
s, err := k.defaultStore()
if err != nil {
return CredentialsItem{}, err
}
return s.GetForURL(url)
}
// GetForKey implements DataStore interface. Uses service default store.
func (k *keyringService) GetForKey(key string) (KeyValueItem, error) {
s, err := k.defaultStore()
if err != nil {
return KeyValueItem{}, err
}
return s.GetForKey(key)
}
// AddItem implements DataStore interface. Uses service default store.
func (k *keyringService) AddItem(item SecretItem) error {
s, err := k.defaultStore()
if err != nil {
return err
}
return s.AddItem(item)
}
// RemoveByURL implements DataStore interface. Uses service default store.
func (k *keyringService) RemoveByURL(url string) error {
s, err := k.defaultStore()
if err != nil {
return err
}
return s.RemoveByURL(url)
}
// RemoveByKey implements DataStore interface. Uses service default store.
func (k *keyringService) RemoveByKey(key string) error {
s, err := k.defaultStore()
if err != nil {
return err
}
return s.RemoveByKey(key)
}
// CleanStorage implements DataStore interface. Uses service default store.
func (k *keyringService) CleanStorage(item SecretItem) error {
s, err := k.defaultStore()
if err != nil {
return err
}
return s.CleanStorage(item)
}
// Exists implements DataStore, checks if keyring exists in persistent storage.
func (k *keyringService) Exists() bool {
s, err := k.defaultStore()
if err != nil {
return false
}
return s.Exists()
}
// Save implements DataStore interface. Uses service default store.
func (k *keyringService) Save() error {
s, err := k.defaultStore()
if err != nil {
return err
}
return s.Save()
}
// Destroy implements DataStore interface. Uses service default store.
func (k *keyringService) Destroy() error {
s, err := k.defaultStore()
if err != nil {
return err
}
return s.Destroy()
}