Commit 04360fa3 authored by rithu leena john's avatar rithu leena john Committed by GitHub

Merge pull request #695 from rithujohn191/add-list-password

api: add call to list passwords
parents e6b54250 ee9738d6
This diff is collapsed.
...@@ -80,6 +80,14 @@ message DeletePasswordResp { ...@@ -80,6 +80,14 @@ message DeletePasswordResp {
bool not_found = 1; bool not_found = 1;
} }
// ListPasswordReq is a request to enumerate passwords.
message ListPasswordReq {}
// ListPasswordResp returs a list of passwords.
message ListPasswordResp {
repeated Password passwords = 1;
}
// VersionReq is a request to fetch version info. // VersionReq is a request to fetch version info.
message VersionReq {} message VersionReq {}
...@@ -104,6 +112,8 @@ service Dex { ...@@ -104,6 +112,8 @@ service Dex {
rpc UpdatePassword(UpdatePasswordReq) returns (UpdatePasswordResp) {}; rpc UpdatePassword(UpdatePasswordReq) returns (UpdatePasswordResp) {};
// DeletePassword deletes the password. // DeletePassword deletes the password.
rpc DeletePassword(DeletePasswordReq) returns (DeletePasswordResp) {}; rpc DeletePassword(DeletePasswordReq) returns (DeletePasswordResp) {};
// ListPassword lists all password entries.
rpc ListPasswords(ListPasswordReq) returns (ListPasswordResp) {};
// GetVersion returns version information of the server. // GetVersion returns version information of the server.
rpc GetVersion(VersionReq) returns (VersionResp) {}; rpc GetVersion(VersionReq) returns (VersionResp) {};
} }
...@@ -171,3 +171,26 @@ func (d dexAPI) GetVersion(ctx context.Context, req *api.VersionReq) (*api.Versi ...@@ -171,3 +171,26 @@ func (d dexAPI) GetVersion(ctx context.Context, req *api.VersionReq) (*api.Versi
Api: apiVersion, Api: apiVersion,
}, nil }, nil
} }
func (d dexAPI) ListPasswords(ctx context.Context, req *api.ListPasswordReq) (*api.ListPasswordResp, error) {
passwordList, err := d.s.ListPasswords()
if err != nil {
log.Printf("api: failed to list passwords: %v", err)
return nil, fmt.Errorf("list passwords: %v", err)
}
var passwords []*api.Password
for _, password := range passwordList {
p := api.Password{
Email: password.Email,
Username: password.Username,
UserId: password.UserID,
}
passwords = append(passwords, &p)
}
return &api.ListPasswordResp{
Passwords: passwords,
}, nil
}
...@@ -5,6 +5,7 @@ package conformance ...@@ -5,6 +5,7 @@ package conformance
import ( import (
"reflect" "reflect"
"sort"
"testing" "testing"
"time" "time"
...@@ -244,6 +245,12 @@ func testRefreshTokenCRUD(t *testing.T, s storage.Storage) { ...@@ -244,6 +245,12 @@ func testRefreshTokenCRUD(t *testing.T, s storage.Storage) {
} }
} }
type byEmail []storage.Password
func (n byEmail) Len() int { return len(n) }
func (n byEmail) Less(i, j int) bool { return n[i].Email < n[j].Email }
func (n byEmail) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func testPasswordCRUD(t *testing.T, s storage.Storage) { func testPasswordCRUD(t *testing.T, s storage.Storage) {
// Use bcrypt.MinCost to keep the tests short. // Use bcrypt.MinCost to keep the tests short.
passwordHash, err := bcrypt.GenerateFromPassword([]byte("secret"), bcrypt.MinCost) passwordHash, err := bcrypt.GenerateFromPassword([]byte("secret"), bcrypt.MinCost)
...@@ -285,6 +292,24 @@ func testPasswordCRUD(t *testing.T, s storage.Storage) { ...@@ -285,6 +292,24 @@ 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
}
sort.Sort(byEmail(want))
sort.Sort(byEmail(passwords))
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 +317,7 @@ func testPasswordCRUD(t *testing.T, s storage.Storage) { ...@@ -292,6 +317,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) { ...@@ -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 { ...@@ -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 ...@@ -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) { ...@@ -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) ( ...@@ -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 { ...@@ -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) { ...@@ -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 { ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment