api: adding ListPasswords() method to the storage interface.
This commit is contained in:
parent
57178fd5f3
commit
19c22807a7
7 changed files with 92 additions and 4 deletions
|
@ -285,6 +285,22 @@ func testPasswordCRUD(t *testing.T, s storage.Storage) {
|
||||||
password.Username = "jane doe"
|
password.Username = "jane doe"
|
||||||
getAndCompare("jane@example.com", password)
|
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 {
|
if err := s.DeletePassword(password.Email); err != nil {
|
||||||
t.Fatalf("failed to delete password: %v", err)
|
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 {
|
if _, err := s.GetPassword(password.Email); err != storage.ErrNotFound {
|
||||||
t.Errorf("after deleting password expected storage.ErrNotFound, got %v", err)
|
t.Errorf("after deleting password expected storage.ErrNotFound, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testKeysCRUD(t *testing.T, s storage.Storage) {
|
func testKeysCRUD(t *testing.T, s storage.Storage) {
|
||||||
|
|
|
@ -260,6 +260,25 @@ func (cli *client) ListRefreshTokens() ([]storage.RefreshToken, error) {
|
||||||
return nil, errors.New("not implemented")
|
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 {
|
func (cli *client) DeleteAuthRequest(id string) error {
|
||||||
return cli.delete(resourceAuthRequest, id)
|
return cli.delete(resourceAuthRequest, id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,6 +259,13 @@ type Password struct {
|
||||||
UserID string `json:"userID,omitempty"`
|
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 {
|
func (cli *client) fromStoragePassword(p storage.Password) Password {
|
||||||
email := strings.ToLower(p.Email)
|
email := strings.ToLower(p.Email)
|
||||||
return Password{
|
return Password{
|
||||||
|
|
|
@ -192,6 +192,15 @@ func (s *memStorage) ListRefreshTokens() (tokens []storage.RefreshToken, err err
|
||||||
return
|
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) {
|
func (s *memStorage) DeletePassword(email string) (err error) {
|
||||||
email = strings.ToLower(email)
|
email = strings.ToLower(email)
|
||||||
s.tx(func() {
|
s.tx(func() {
|
||||||
|
|
|
@ -532,12 +532,39 @@ func (c *conn) GetPassword(email string) (storage.Password, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPassword(q querier, email string) (p storage.Password, err error) {
|
func getPassword(q querier, email string) (p storage.Password, err error) {
|
||||||
email = strings.ToLower(email)
|
return scanPassword(q.QueryRow(`
|
||||||
err = q.QueryRow(`
|
|
||||||
select
|
select
|
||||||
email, hash, username, user_id
|
email, hash, username, user_id
|
||||||
from password where email = $1;
|
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,
|
&p.Email, &p.Hash, &p.Username, &p.UserID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -60,6 +60,8 @@ func (s staticClientsStorage) UpdateClient(id string, updater func(old Client) (
|
||||||
type staticPasswordsStorage struct {
|
type staticPasswordsStorage struct {
|
||||||
Storage
|
Storage
|
||||||
|
|
||||||
|
// A read-only set of passwords.
|
||||||
|
passwords []Password
|
||||||
passwordsByEmail map[string]Password
|
passwordsByEmail map[string]Password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +73,7 @@ func WithStaticPasswords(s Storage, staticPasswords []Password) Storage {
|
||||||
p.Email = strings.ToLower(p.Email)
|
p.Email = strings.ToLower(p.Email)
|
||||||
passwordsByEmail[p.Email] = p
|
passwordsByEmail[p.Email] = p
|
||||||
}
|
}
|
||||||
return staticPasswordsStorage{s, passwordsByEmail}
|
return staticPasswordsStorage{s, staticPasswords, passwordsByEmail}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s staticPasswordsStorage) GetPassword(email string) (Password, error) {
|
func (s staticPasswordsStorage) GetPassword(email string) (Password, error) {
|
||||||
|
@ -81,6 +83,12 @@ func (s staticPasswordsStorage) GetPassword(email string) (Password, error) {
|
||||||
return Password{}, ErrNotFound
|
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 {
|
func (s staticPasswordsStorage) CreatePassword(p Password) error {
|
||||||
return errors.New("static passwords: read-only cannot create password")
|
return errors.New("static passwords: read-only cannot create password")
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ type Storage interface {
|
||||||
|
|
||||||
ListClients() ([]Client, error)
|
ListClients() ([]Client, error)
|
||||||
ListRefreshTokens() ([]RefreshToken, error)
|
ListRefreshTokens() ([]RefreshToken, error)
|
||||||
|
ListPasswords() ([]Password, error)
|
||||||
|
|
||||||
// Delete methods MUST be atomic.
|
// Delete methods MUST be atomic.
|
||||||
DeleteAuthRequest(id string) error
|
DeleteAuthRequest(id string) error
|
||||||
|
|
Reference in a new issue