Merge pull request #649 from rithujohn191/gRPC-endpoints
api: add gRPC endpoints for creating, updating and deleting passwords
This commit is contained in:
101
server/api.go
101
server/api.go
@@ -2,8 +2,10 @@ package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/dex/api"
|
||||
@@ -43,7 +45,7 @@ func (d dexAPI) CreateClient(ctx context.Context, req *api.CreateClientReq) (*ap
|
||||
if err := d.s.CreateClient(c); err != nil {
|
||||
log.Printf("api: failed to create client: %v", err)
|
||||
// TODO(ericchiang): Surface "already exists" errors.
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("create client: %v", err)
|
||||
}
|
||||
|
||||
return &api.CreateClientResp{
|
||||
@@ -58,7 +60,102 @@ func (d dexAPI) DeleteClient(ctx context.Context, req *api.DeleteClientReq) (*ap
|
||||
return &api.DeleteClientResp{NotFound: true}, nil
|
||||
}
|
||||
log.Printf("api: failed to delete client: %v", err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("delete client: %v", err)
|
||||
}
|
||||
return &api.DeleteClientResp{}, nil
|
||||
}
|
||||
|
||||
// checkCost returns an error if the hash provided does not meet minimum cost requirement
|
||||
func checkCost(hash []byte) error {
|
||||
actual, err := bcrypt.Cost(hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing bcrypt hash: %v", err)
|
||||
}
|
||||
if actual < bcrypt.DefaultCost {
|
||||
return fmt.Errorf("given hash cost = %d, does not meet minimum cost requirement = %d", actual, bcrypt.DefaultCost)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d dexAPI) CreatePassword(ctx context.Context, req *api.CreatePasswordReq) (*api.CreatePasswordResp, error) {
|
||||
if req.Password == nil {
|
||||
return nil, errors.New("no password supplied")
|
||||
}
|
||||
if req.Password.UserId == "" {
|
||||
return nil, errors.New("no user ID supplied")
|
||||
}
|
||||
if req.Password.Hash != nil {
|
||||
if err := checkCost(req.Password.Hash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("no hash of password supplied")
|
||||
}
|
||||
|
||||
p := storage.Password{
|
||||
Email: req.Password.Email,
|
||||
Hash: req.Password.Hash,
|
||||
Username: req.Password.Username,
|
||||
UserID: req.Password.UserId,
|
||||
}
|
||||
if err := d.s.CreatePassword(p); err != nil {
|
||||
log.Printf("api: failed to create password: %v", err)
|
||||
return nil, fmt.Errorf("create password: %v", err)
|
||||
}
|
||||
|
||||
return &api.CreatePasswordResp{}, nil
|
||||
}
|
||||
|
||||
func (d dexAPI) UpdatePassword(ctx context.Context, req *api.UpdatePasswordReq) (*api.UpdatePasswordResp, error) {
|
||||
if req.Email == "" {
|
||||
return nil, errors.New("no email supplied")
|
||||
}
|
||||
if req.NewHash == nil && req.NewUsername == "" {
|
||||
return nil, errors.New("nothing to update")
|
||||
}
|
||||
|
||||
if req.NewHash != nil {
|
||||
if err := checkCost(req.NewHash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
updater := func(old storage.Password) (storage.Password, error) {
|
||||
if req.NewHash != nil {
|
||||
old.Hash = req.NewHash
|
||||
}
|
||||
|
||||
if req.NewUsername != "" {
|
||||
old.Username = req.NewUsername
|
||||
}
|
||||
|
||||
return old, nil
|
||||
}
|
||||
|
||||
if err := d.s.UpdatePassword(req.Email, updater); err != nil {
|
||||
if err == storage.ErrNotFound {
|
||||
return &api.UpdatePasswordResp{NotFound: true}, nil
|
||||
}
|
||||
log.Printf("api: failed to update password: %v", err)
|
||||
return nil, fmt.Errorf("update password: %v", err)
|
||||
}
|
||||
|
||||
return &api.UpdatePasswordResp{}, nil
|
||||
}
|
||||
|
||||
func (d dexAPI) DeletePassword(ctx context.Context, req *api.DeletePasswordReq) (*api.DeletePasswordResp, error) {
|
||||
if req.Email == "" {
|
||||
return nil, errors.New("no email supplied")
|
||||
}
|
||||
|
||||
err := d.s.DeletePassword(req.Email)
|
||||
if err != nil {
|
||||
if err == storage.ErrNotFound {
|
||||
return &api.DeletePasswordResp{NotFound: true}, nil
|
||||
}
|
||||
log.Printf("api: failed to delete password: %v", err)
|
||||
return nil, fmt.Errorf("delete password: %v", err)
|
||||
}
|
||||
return &api.DeletePasswordResp{}, nil
|
||||
|
||||
}
|
||||
|
@@ -1 +1,59 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/dex/api"
|
||||
"github.com/coreos/dex/storage/memory"
|
||||
)
|
||||
|
||||
// Attempts to create, update and delete a test Password
|
||||
func TestPassword(t *testing.T) {
|
||||
s := memory.New()
|
||||
serv := NewAPI(s)
|
||||
|
||||
ctx := context.Background()
|
||||
p := api.Password{
|
||||
Email: "test@example.com",
|
||||
// bcrypt hash of the value "test1" with cost 10
|
||||
Hash: []byte("$2a$10$XVMN/Fid.Ks4CXgzo8fpR.iU1khOMsP5g9xQeXuBm1wXjRX8pjUtO"),
|
||||
Username: "test",
|
||||
UserId: "test123",
|
||||
}
|
||||
|
||||
createReq := api.CreatePasswordReq{
|
||||
Password: &p,
|
||||
}
|
||||
|
||||
if _, err := serv.CreatePassword(ctx, &createReq); err != nil {
|
||||
t.Fatalf("Unable to create password: %v", err)
|
||||
}
|
||||
|
||||
updateReq := api.UpdatePasswordReq{
|
||||
Email: "test@example.com",
|
||||
NewUsername: "test1",
|
||||
}
|
||||
|
||||
if _, err := serv.UpdatePassword(ctx, &updateReq); err != nil {
|
||||
t.Fatalf("Unable to update password: %v", err)
|
||||
}
|
||||
|
||||
pass, err := s.GetPassword(updateReq.Email)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to retrieve password: %v", err)
|
||||
}
|
||||
|
||||
if pass.Username != updateReq.NewUsername {
|
||||
t.Fatalf("UpdatePassword failed. Expected username %s retrieved %s", updateReq.NewUsername, pass.Username)
|
||||
}
|
||||
|
||||
deleteReq := api.DeletePasswordReq{
|
||||
Email: "test@example.com",
|
||||
}
|
||||
|
||||
if _, err := serv.DeletePassword(ctx, &deleteReq); err != nil {
|
||||
t.Fatalf("Unable to delete password: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user