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