Skip to content

Commit 71e7fe3

Browse files
committed
Updates to logins
1 parent a815c46 commit 71e7fe3

File tree

4 files changed

+157
-33
lines changed

4 files changed

+157
-33
lines changed

cmd/api/bitwarden.go

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ import (
1515
. "github.com/djthorpe/go-errors"
1616
)
1717

18+
///////////////////////////////////////////////////////////////////////////////
19+
// TYPES
20+
21+
type bwCipher struct {
22+
Name string
23+
Username string
24+
URI string
25+
Folder string
26+
}
27+
1828
///////////////////////////////////////////////////////////////////////////////
1929
// GLOBALS
2030

@@ -47,9 +57,10 @@ func bwRegister(flags *Flags) {
4757
Description: "Interact with the Bitwarden API",
4858
Parse: bwParse,
4959
Fn: []Fn{
50-
{Name: "login", Description: "Login to Bitwarden", Call: bwLogin},
60+
{Name: "auth", Description: "Authenticate with Bitwarden", Call: bwAuth},
5161
{Name: "sync", Description: "Sync items from Bitwarden", Call: bwSync},
5262
{Name: "folders", Description: "Retrieve folders", Call: bwFolders},
63+
{Name: "logins", Description: "Retrieve login items", Call: bwLogins},
5364
},
5465
})
5566
}
@@ -87,7 +98,7 @@ func bwParse(flags *Flags, opts ...client.ClientOpt) error {
8798
///////////////////////////////////////////////////////////////////////////////
8899
// API METHODS
89100

90-
func bwLogin(w *tablewriter.TableWriter) error {
101+
func bwAuth(w *tablewriter.TableWriter) error {
91102
// Load session or create a new one
92103
session, err := bwReadSession()
93104
if err != nil {
@@ -168,24 +179,15 @@ func bwFolders(w *tablewriter.TableWriter) error {
168179
return err
169180
}
170181

171-
// If no password is set, then read it
172-
password := bwPassword
173-
if password == "" {
174-
stdin := int(os.Stdin.Fd())
175-
if !term.IsTerminal(stdin) {
176-
return ErrBadParameter.With("No password set and not running in terminal")
177-
}
178-
fmt.Fprintf(os.Stdout, "Enter password: ")
179-
if value, err := term.ReadPassword(stdin); err != nil {
182+
// Make an encryption key
183+
if bwPassword == "" {
184+
if v, err := bwReadPasswordFromTerminal(); err != nil {
180185
return err
181186
} else {
182-
password = string(value)
187+
bwPassword = v
183188
}
184-
fmt.Fprintf(os.Stdout, "\n")
185189
}
186-
187-
// Make the decryption key
188-
if err := session.CacheKey(profile.Key, profile.Email, password); err != nil {
190+
if err := session.CacheKey(profile.Key, profile.Email, bwPassword); err != nil {
189191
return err
190192
}
191193

@@ -211,9 +213,80 @@ func bwFolders(w *tablewriter.TableWriter) error {
211213
return nil
212214
}
213215

216+
func bwLogins(w *tablewriter.TableWriter) error {
217+
// Load the profile
218+
profile, err := bwReadProfile()
219+
if err != nil {
220+
return err
221+
}
222+
223+
// Load session or create a new one
224+
session, err := bwReadSession()
225+
if err != nil {
226+
return err
227+
}
228+
229+
// Make an encryption key
230+
if bwPassword == "" {
231+
if v, err := bwReadPasswordFromTerminal(); err != nil {
232+
return err
233+
} else {
234+
bwPassword = v
235+
}
236+
}
237+
if err := session.CacheKey(profile.Key, profile.Email, bwPassword); err != nil {
238+
return err
239+
}
240+
241+
// Read the ciphers
242+
ciphers := schema.Ciphers{}
243+
result := []bwCipher{}
244+
if err := bwRead("ciphers.json", &ciphers); err != nil {
245+
return err
246+
}
247+
// Decrypt the ciphers from the session
248+
for _, cipher := range ciphers {
249+
if cipher.Type != schema.CipherTypeLogin {
250+
continue
251+
}
252+
if decrypted, err := cipher.Decrypt(session); err != nil {
253+
return err
254+
} else {
255+
result = append(result, bwCipher{
256+
Name: decrypted.(*schema.Cipher).Name,
257+
Username: decrypted.(*schema.Cipher).Login.Username,
258+
URI: decrypted.(*schema.Cipher).Login.URI,
259+
Folder: decrypted.(*schema.Cipher).FolderId,
260+
})
261+
}
262+
}
263+
264+
// Output the ciphers
265+
w.Write(result)
266+
267+
// Return success
268+
return nil
269+
}
270+
214271
///////////////////////////////////////////////////////////////////////////////
215272
// OTHER
216273

274+
func bwReadPasswordFromTerminal() (string, error) {
275+
stdin := int(os.Stdin.Fd())
276+
if !term.IsTerminal(stdin) {
277+
return "", ErrBadParameter.With("No password set and not running in terminal")
278+
}
279+
fmt.Fprintf(os.Stdout, "Enter password: ")
280+
defer func() {
281+
fmt.Fprintf(os.Stdout, "\n")
282+
}()
283+
if value, err := term.ReadPassword(stdin); err != nil {
284+
return "", err
285+
} else {
286+
return string(value), nil
287+
}
288+
}
289+
217290
func bwReadProfile() (*schema.Profile, error) {
218291
result := schema.NewProfile()
219292
filename := filepath.Join(bwConfigDir, "profile.json")

pkg/bitwarden/schema/cipher.go

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package schema
22

33
import (
44
"encoding/json"
5+
"fmt"
56
"io"
67
"time"
78
)
@@ -12,34 +13,41 @@ import (
1213
type Ciphers []*Cipher
1314

1415
type Cipher struct {
15-
Id string `json:"id"`
16-
Name string `json:"name"`
17-
Type CipherType `json:"type"`
18-
FolderId *string `json:"folderId,omitempty"`
19-
OrganizationId *string `json:"organizationId,omitempty"`
20-
Favorite bool `json:"favorite"`
21-
Edit bool `json:"edit"`
22-
RevisionDate time.Time `json:"revisionDate"`
23-
CollectionIds []string `json:"collectionIds"`
24-
ViewPassword bool `json:"viewPassword"`
25-
// Login *LoginData `json:"Login,omitempty"`
16+
Id string `json:"id"`
17+
Name string `json:"name"`
18+
Type CipherType `json:"type"`
19+
FolderId string `json:"folderId,omitempty"`
20+
OrganizationId string `json:"organizationId,omitempty"`
21+
Favorite bool `json:"favorite,omitempty"`
22+
Edit bool `json:"edit"`
23+
RevisionDate time.Time `json:"revisionDate"`
24+
CollectionIds []string `json:"collectionIds,omitempty"`
25+
ViewPassword bool `json:"viewPassword"`
26+
Login *CipherLogin `json:"Login,omitempty"`
2627
// Card *CardData `json:"Card,omitempty"`
2728
// SecureNote *SecureNoteData `json:"SecureNote,omitempty"`
2829
// Identity *IdentityData `json:"Identity,omitempty"`
29-
Attachments []string `json:"Attachments"`
30+
Attachments []string `json:"Attachments,omitempty"`
3031
Object string `json:"object"`
3132
}
3233

3334
type CipherType uint
3435

36+
type CipherLogin struct {
37+
Username string `json:"Username,omitempty"` // crypt
38+
Password string `json:"Password,omitempty"` // crypt
39+
URI string `json:"URI,omitempty"` // crypt
40+
}
41+
3542
///////////////////////////////////////////////////////////////////////////////
3643
// CONSTANTS
3744

3845
const (
39-
CipherTypeLogin CipherType = 1
40-
CipherTypeNote CipherType = 2
41-
CipherTypeCard CipherType = 3
42-
CipherTypeIdentity CipherType = 4
46+
_ CipherType = iota
47+
CipherTypeLogin
48+
CipherTypeNote
49+
CipherTypeCard
50+
CipherTypeIdentity
4351
)
4452

4553
///////////////////////////////////////////////////////////////////////////////
@@ -50,6 +58,25 @@ func (c Cipher) String() string {
5058
return string(data)
5159
}
5260

61+
func (t CipherType) String() string {
62+
switch t {
63+
case CipherTypeLogin:
64+
return "Login"
65+
case CipherTypeNote:
66+
return "Note"
67+
case CipherTypeCard:
68+
return "Card"
69+
case CipherTypeIdentity:
70+
return "Identity"
71+
default:
72+
return fmt.Sprint(uint(t))
73+
}
74+
}
75+
76+
func (t CipherType) Marshal() ([]byte, error) {
77+
return []byte(fmt.Sprint(t)), nil
78+
}
79+
5380
///////////////////////////////////////////////////////////////////////////////
5481
// PUBLIC METHODS
5582

@@ -61,6 +88,27 @@ func (c *Ciphers) Write(w io.Writer) error {
6188
return json.NewEncoder(w).Encode(c)
6289
}
6390

91+
// Decrypt a cipher
92+
func (c Cipher) Decrypt(s *Session) (Crypter, error) {
93+
result := &c
94+
if value, err := s.DecryptStr(result.Name); err != nil {
95+
return nil, err
96+
} else {
97+
result.Name = value
98+
}
99+
if value, err := s.DecryptStr(result.Login.Username); err != nil {
100+
return nil, err
101+
} else {
102+
result.Login.Username = value
103+
}
104+
if value, err := s.DecryptStr(result.Login.URI); err != nil {
105+
return nil, err
106+
} else {
107+
result.Login.URI = value
108+
}
109+
return result, nil
110+
}
111+
64112
/*
65113
"collectionIds": [
66114
"86f7c94b-12a0-4eb2-bb0e-aedb007de863"

pkg/bitwarden/schema/folder.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func (f Folder) Decrypt(s *Session) (Crypter, error) {
3939
} else {
4040
result.Name = value
4141
}
42-
return &f, nil
42+
return result, nil
4343
}
4444

4545
///////////////////////////////////////////////////////////////////////////////

pkg/bitwarden/schema/session.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ func (s *Session) CacheKey(key, email, password string) error {
134134

135135
// Decrypt a cipher string, requires a cached key first
136136
func (s *Session) DecryptStr(value string) (string, error) {
137+
if value == "" {
138+
return "", nil
139+
}
137140
if s.cryptKey == nil {
138141
return "", ErrInternalAppError.With("Missing decryption key")
139142
}

0 commit comments

Comments
 (0)