Merge pull request #649 from rithujohn191/gRPC-endpoints

api: add gRPC endpoints for creating, updating and deleting passwords
This commit is contained in:
Eric Chiang
2016-11-01 14:20:31 -07:00
committed by GitHub
5 changed files with 444 additions and 27 deletions

View File

@@ -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
}

View File

@@ -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)
}
}