From 19c22807a7acfac9d09d0c631bbdadfcec61fa00 Mon Sep 17 00:00:00 2001 From: rithu john Date: Wed, 16 Nov 2016 14:57:27 -0800 Subject: [PATCH] api: adding ListPasswords() method to the storage interface. --- storage/conformance/conformance.go | 17 +++++++++++++++ storage/kubernetes/storage.go | 19 +++++++++++++++++ storage/kubernetes/types.go | 7 +++++++ storage/memory/memory.go | 9 ++++++++ storage/sql/crud.go | 33 +++++++++++++++++++++++++++--- storage/static.go | 10 ++++++++- storage/storage.go | 1 + 7 files changed, 92 insertions(+), 4 deletions(-) diff --git a/storage/conformance/conformance.go b/storage/conformance/conformance.go index caa77c14..af7ff2b3 100644 --- a/storage/conformance/conformance.go +++ b/storage/conformance/conformance.go @@ -285,6 +285,22 @@ func testPasswordCRUD(t *testing.T, s storage.Storage) { password.Username = "jane doe" getAndCompare("jane@example.com", password) + var passwordList []storage.Password + passwordList = append(passwordList, password) + + listAndCompare := func(want []storage.Password) { + passwords, err := s.ListPasswords() + if err != nil { + t.Errorf("list password: %v", err) + return + } + if diff := pretty.Compare(want, passwords); diff != "" { + t.Errorf("password list retrieved from storage did not match: %s", diff) + } + } + + listAndCompare(passwordList) + if err := s.DeletePassword(password.Email); err != nil { t.Fatalf("failed to delete password: %v", err) } @@ -292,6 +308,7 @@ func testPasswordCRUD(t *testing.T, s storage.Storage) { if _, err := s.GetPassword(password.Email); err != storage.ErrNotFound { t.Errorf("after deleting password expected storage.ErrNotFound, got %v", err) } + } func testKeysCRUD(t *testing.T, s storage.Storage) { diff --git a/storage/kubernetes/storage.go b/storage/kubernetes/storage.go index 08df0161..54712652 100644 --- a/storage/kubernetes/storage.go +++ b/storage/kubernetes/storage.go @@ -260,6 +260,25 @@ func (cli *client) ListRefreshTokens() ([]storage.RefreshToken, error) { return nil, errors.New("not implemented") } +func (cli *client) ListPasswords() (passwords []storage.Password, err error) { + var passwordList PasswordList + if err = cli.list(resourcePassword, &passwordList); err != nil { + return passwords, fmt.Errorf("failed to list passwords: %v", err) + } + + for _, password := range passwordList.Passwords { + p := storage.Password{ + Email: password.Email, + Hash: password.Hash, + Username: password.Username, + UserID: password.UserID, + } + passwords = append(passwords, p) + } + + return +} + func (cli *client) DeleteAuthRequest(id string) error { return cli.delete(resourceAuthRequest, id) } diff --git a/storage/kubernetes/types.go b/storage/kubernetes/types.go index 900a8187..61c2c295 100644 --- a/storage/kubernetes/types.go +++ b/storage/kubernetes/types.go @@ -259,6 +259,13 @@ type Password struct { UserID string `json:"userID,omitempty"` } +// PasswordList is a list of Passwords. +type PasswordList struct { + k8sapi.TypeMeta `json:",inline"` + k8sapi.ListMeta `json:"metadata,omitempty"` + Passwords []Password `json:"items"` +} + func (cli *client) fromStoragePassword(p storage.Password) Password { email := strings.ToLower(p.Email) return Password{ diff --git a/storage/memory/memory.go b/storage/memory/memory.go index df88b442..e8f2ce9a 100644 --- a/storage/memory/memory.go +++ b/storage/memory/memory.go @@ -192,6 +192,15 @@ func (s *memStorage) ListRefreshTokens() (tokens []storage.RefreshToken, err err return } +func (s *memStorage) ListPasswords() (passwords []storage.Password, err error) { + s.tx(func() { + for _, password := range s.passwords { + passwords = append(passwords, password) + } + }) + return +} + func (s *memStorage) DeletePassword(email string) (err error) { email = strings.ToLower(email) s.tx(func() { diff --git a/storage/sql/crud.go b/storage/sql/crud.go index 1b444670..1bc36ccd 100644 --- a/storage/sql/crud.go +++ b/storage/sql/crud.go @@ -532,12 +532,39 @@ func (c *conn) GetPassword(email string) (storage.Password, error) { } func getPassword(q querier, email string) (p storage.Password, err error) { - email = strings.ToLower(email) - err = q.QueryRow(` + return scanPassword(q.QueryRow(` select email, hash, username, user_id from password where email = $1; - `, email).Scan( + `, strings.ToLower(email))) +} + +func (c *conn) ListPasswords() ([]storage.Password, error) { + rows, err := c.Query(` + select + email, hash, username, user_id + from password; + `) + if err != nil { + return nil, err + } + + var passwords []storage.Password + for rows.Next() { + p, err := scanPassword(rows) + if err != nil { + return nil, err + } + passwords = append(passwords, p) + } + if err := rows.Err(); err != nil { + return nil, err + } + return passwords, nil +} + +func scanPassword(s scanner) (p storage.Password, err error) { + err = s.Scan( &p.Email, &p.Hash, &p.Username, &p.UserID, ) if err != nil { diff --git a/storage/static.go b/storage/static.go index 8274c5f8..4076a613 100644 --- a/storage/static.go +++ b/storage/static.go @@ -60,6 +60,8 @@ func (s staticClientsStorage) UpdateClient(id string, updater func(old Client) ( type staticPasswordsStorage struct { Storage + // A read-only set of passwords. + passwords []Password passwordsByEmail map[string]Password } @@ -71,7 +73,7 @@ func WithStaticPasswords(s Storage, staticPasswords []Password) Storage { p.Email = strings.ToLower(p.Email) passwordsByEmail[p.Email] = p } - return staticPasswordsStorage{s, passwordsByEmail} + return staticPasswordsStorage{s, staticPasswords, passwordsByEmail} } func (s staticPasswordsStorage) GetPassword(email string) (Password, error) { @@ -81,6 +83,12 @@ func (s staticPasswordsStorage) GetPassword(email string) (Password, error) { return Password{}, ErrNotFound } +func (s staticPasswordsStorage) ListPasswords() ([]Password, error) { + passwords := make([]Password, len(s.passwords)) + copy(passwords, s.passwords) + return passwords, nil +} + func (s staticPasswordsStorage) CreatePassword(p Password) error { return errors.New("static passwords: read-only cannot create password") } diff --git a/storage/storage.go b/storage/storage.go index 0d5b1f7f..cd480326 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -70,6 +70,7 @@ type Storage interface { ListClients() ([]Client, error) ListRefreshTokens() ([]RefreshToken, error) + ListPasswords() ([]Password, error) // Delete methods MUST be atomic. DeleteAuthRequest(id string) error