Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

Support for Getting All DIDs for a Given Method #116

Merged
merged 1 commit into from
Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions pkg/server/router/did.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,39 @@ func (dr DIDRouter) GetDIDByMethod(ctx context.Context, w http.ResponseWriter, _
resp := GetDIDByMethodResponse{DID: gotDID.DID}
return framework.Respond(ctx, w, resp, http.StatusOK)
}

type GetDIDsByMethodResponse struct {
DIDs []didsdk.DIDDocument `json:"dids,omitempty"`
}

// GetDIDsByMethod godoc
// @Summary Get DIDs
// @Description Get DIDs by method
// @Tags DecentralizedIdentityAPI
// @Accept json
// @Produce json
// @Param method path string true "Method"
// @Success 200 {object} GetDIDsByMethodResponse
// @Failure 400 {string} string "Bad request"
// @Router /v1/dids/{method} [get]
func (dr DIDRouter) GetDIDsByMethod(ctx context.Context, w http.ResponseWriter, _ *http.Request) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good chance, can you run mage spec ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah read this after I merged

method := framework.GetParam(ctx, MethodParam)
if method == nil {
errMsg := "get DIDs by method request missing method parameter"
logrus.Error(errMsg)
return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest)
}

// TODO(gabe) check if the method is supported, to tell whether this is a bad req or internal error
// TODO(gabe) differentiate between internal errors and not found DIDs
getDIDsRequest := did.GetDIDsRequest{Method: did.Method(*method)}
gotDIDs, err := dr.service.GetDIDsByMethod(getDIDsRequest)
if err != nil {
errMsg := fmt.Sprintf("could not get DIDs for method: %s", *method)
logrus.WithError(err).Error(errMsg)
return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest)
}

resp := GetDIDsByMethodResponse{DIDs: gotDIDs.DIDs}
return framework.Respond(ctx, w, resp, http.StatusOK)
}
21 changes: 21 additions & 0 deletions pkg/server/router/did_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,26 @@ func TestDIDRouter(t *testing.T) {

// make sure it's the same value
assert.Equal(tt, createDIDResponse.DID.ID, getDIDResponse.DID.ID)

// create a second DID
createDIDResponse2, err := didService.CreateDIDByMethod(did.CreateDIDRequest{Method: did.KeyMethod, KeyType: crypto.Ed25519})
assert.NoError(tt, err)
assert.NotEmpty(tt, createDIDResponse2)

// get all DIDs back
getDIDsResponse, err := didService.GetDIDsByMethod(did.GetDIDsRequest{Method: did.KeyMethod})
assert.NoError(tt, err)
assert.NotEmpty(tt, getDIDsResponse)
assert.Len(tt, getDIDsResponse.DIDs, 2)

knownDIDs := map[string]bool{createDIDResponse.DID.ID: true, createDIDResponse2.DID.ID: true}
for _, did := range getDIDsResponse.DIDs {
if _, ok := knownDIDs[did.ID]; !ok {
tt.Error("got unknown DID")
} else {
delete(knownDIDs, did.ID)
}
}
assert.Len(tt, knownDIDs, 0)
})
}
1 change: 1 addition & 0 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ func (s *SSIServer) DecentralizedIdentityAPI(service svcframework.Service) (err

s.Handle(http.MethodGet, handlerPath, didRouter.GetDIDMethods)
s.Handle(http.MethodPut, path.Join(handlerPath, "/:method"), didRouter.CreateDIDByMethod)
s.Handle(http.MethodGet, path.Join(handlerPath, "/:method"), didRouter.GetDIDsByMethod)
s.Handle(http.MethodGet, path.Join(handlerPath, "/:method/:id"), didRouter.GetDIDByMethod)
return
}
Expand Down
106 changes: 102 additions & 4 deletions pkg/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,21 +146,25 @@ func TestDIDAPI(t *testing.T) {
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "invalid create DID request")

// reset recorder between calls
w.Flush()

// with body, bad key type
createDIDRequest := router.CreateDIDByMethodRequest{KeyType: "bad"}
requestReader := newRequestValue(tt, createDIDRequest)
req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
w = httptest.NewRecorder()

err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "could not create DID for method<key> with key type: bad")

// reset recorder between calls
w.Flush()

// with body, good key type
createDIDRequest = router.CreateDIDByMethodRequest{KeyType: crypto.Ed25519}
requestReader = newRequestValue(tt, createDIDRequest)
req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
w = httptest.NewRecorder()

err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
assert.NoError(tt, err)
Expand Down Expand Up @@ -197,6 +201,9 @@ func TestDIDAPI(t *testing.T) {
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "could not get DID for method<bad>")

// reset recorder between calls
w.Flush()

// good method, bad id
badParams1 := map[string]string{
"method": "key",
Expand All @@ -206,12 +213,13 @@ func TestDIDAPI(t *testing.T) {
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "could not get DID for method<key> with id: worse")

// reset recorder between calls
w.Flush()
// store a DID
createDIDRequest := router.CreateDIDByMethodRequest{KeyType: crypto.Ed25519}
requestReader := newRequestValue(tt, createDIDRequest)
params := map[string]string{"method": "key"}
req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)
w = httptest.NewRecorder()

err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
assert.NoError(tt, err)
Expand All @@ -220,11 +228,13 @@ func TestDIDAPI(t *testing.T) {
err = json.NewDecoder(w.Body).Decode(&createdDID)
assert.NoError(tt, err)

// reset recorder between calls
w.Flush()

// get it back
createdID := createdDID.DID.ID
getDIDPath := fmt.Sprintf("https://ssi-service.com/v1/dids/key/%s", createdID)
req = httptest.NewRequest(http.MethodGet, getDIDPath, nil)
w = httptest.NewRecorder()

// good params
goodParams := map[string]string{
Expand All @@ -239,6 +249,94 @@ func TestDIDAPI(t *testing.T) {
assert.NoError(tt, err)
assert.Equal(tt, createdID, resp.DID.ID)
})

t.Run("Test Get DIDs By Method", func(tt *testing.T) {
bolt, err := storage.NewBoltDB()

// remove the db file after the test
tt.Cleanup(func() {
_ = bolt.Close()
_ = os.Remove(storage.DBFile)
})

_, keyStore := testKeyStore(tt, bolt)
didService := testDIDRouter(tt, bolt, keyStore)

// get DIDs by method
req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/bad", nil)
w := httptest.NewRecorder()

// bad params
badParams := map[string]string{
"method": "bad",
}
err = didService.GetDIDsByMethod(newRequestContextWithParams(badParams), w, req)
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "could not get DIDs for method: bad")

// good method
goodParams := map[string]string{
"method": "key",
}
err = didService.GetDIDsByMethod(newRequestContextWithParams(goodParams), w, req)
assert.NoError(tt, err)
var gotDIDs router.GetDIDByMethodResponse
err = json.NewDecoder(w.Body).Decode(&gotDIDs)
assert.NoError(tt, err)
assert.Empty(tt, gotDIDs)

// reset recorder between calls
w.Flush()

// store two DIDs
createDIDRequest := router.CreateDIDByMethodRequest{KeyType: crypto.Ed25519}
requestReader := newRequestValue(tt, createDIDRequest)
params := map[string]string{"method": "key"}
req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)

err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
assert.NoError(tt, err)

var createdDID router.CreateDIDByMethodResponse
err = json.NewDecoder(w.Body).Decode(&createdDID)
assert.NoError(tt, err)

// reset recorder between calls
w.Flush()

requestReader = newRequestValue(tt, createDIDRequest)
req = httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/dids/key", requestReader)

err = didService.CreateDIDByMethod(newRequestContextWithParams(params), w, req)
assert.NoError(tt, err)

var createdDID2 router.CreateDIDByMethodResponse
err = json.NewDecoder(w.Body).Decode(&createdDID2)
assert.NoError(tt, err)

// reset recorder between calls
w.Flush()

// get all dids for method

req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/key", requestReader)
err = didService.GetDIDsByMethod(newRequestContextWithParams(params), w, req)
assert.NoError(tt, err)

var gotDIDsResponse router.GetDIDsByMethodResponse
err = json.NewDecoder(w.Body).Decode(&gotDIDsResponse)
assert.NoError(tt, err)

knownDIDs := map[string]bool{createdDID.DID.ID: true, createdDID2.DID.ID: true}
for _, did := range gotDIDsResponse.DIDs {
if _, ok := knownDIDs[did.ID]; !ok {
tt.Error("got unknown DID")
} else {
delete(knownDIDs, did.ID)
}
}
assert.Len(tt, knownDIDs, 0)
})
}

func TestSchemaAPI(t *testing.T) {
Expand Down
11 changes: 11 additions & 0 deletions pkg/service/did/did.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Service struct {
type MethodHandler interface {
CreateDID(request CreateDIDRequest) (*CreateDIDResponse, error)
GetDID(request GetDIDRequest) (*GetDIDResponse, error)
GetDIDs(method Method) (*GetDIDsResponse, error)
}

func NewDIDService(config config.DIDServiceConfig, s storage.ServiceStorage, keyStore *keystore.Service) (*Service, error) {
Expand Down Expand Up @@ -116,6 +117,16 @@ func (s *Service) GetDIDByMethod(request GetDIDRequest) (*GetDIDResponse, error)
return handler.GetDID(request)
}

func (s *Service) GetDIDsByMethod(request GetDIDsRequest) (*GetDIDsResponse, error) {
method := request.Method
handler, err := s.getHandler(method)
if err != nil {
errMsg := fmt.Sprintf("could not get handler for method<%s>", method)
return nil, util.LoggingErrorMsg(err, errMsg)
}
return handler.GetDIDs(method)
}

func (s *Service) getHandler(method Method) (MethodHandler, error) {
handler, ok := s.handlers[method]
if !ok {
Expand Down
15 changes: 15 additions & 0 deletions pkg/service/did/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@ func (h *keyDIDHandler) GetDID(request GetDIDRequest) (*GetDIDResponse, error) {
return &GetDIDResponse{DID: gotDID.DID}, nil
}

func (h *keyDIDHandler) GetDIDs(method Method) (*GetDIDsResponse, error) {

logrus.Debugf("getting DIDs for method: %s", method)

gotDIDs, err := h.storage.GetDIDs(string(method))
if err != nil {
return nil, fmt.Errorf("error getting DIDs for method: %s", method)
}
var dids []did.DIDDocument
for _, did := range gotDIDs {
dids = append(dids, did.DID)
}
return &GetDIDsResponse{DIDs: dids}, nil
}

func privateKeyToBase58(privKey interface{}) (string, error) {
if haveBytes, ok := privKey.([]byte); ok {
return base58.Encode(haveBytes), nil
Expand Down
9 changes: 9 additions & 0 deletions pkg/service/did/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,12 @@ type GetDIDRequest struct {
type GetDIDResponse struct {
DID didsdk.DIDDocument `json:"did"`
}

type GetDIDsRequest struct {
Method Method `json:"method" validate:"required"`
}

// GetDIDsResponse is the JSON-serializable response for getting all DIDs for a given method
type GetDIDsResponse struct {
DIDs []didsdk.DIDDocument `json:"dids"`
}
5 changes: 3 additions & 2 deletions pkg/service/did/storage/bolt.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/goccy/go-json"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"

"github.com/tbd54566975/ssi-service/internal/util"
"github.com/tbd54566975/ssi-service/pkg/storage"
Expand Down Expand Up @@ -79,8 +80,8 @@ func (b BoltDIDStorage) GetDIDs(method string) ([]StoredDID, error) {
return nil, util.LoggingErrorMsg(err, couldNotGetDIDsErr)
}
if len(gotDIDs) == 0 {
err := fmt.Errorf("no DIDs found for method: %s", method)
return nil, util.LoggingErrorMsg(err, "could not get stored DIDs")
logrus.Infof("no DIDs found for method: %s", method)
return nil, nil
}
var stored []StoredDID
for _, didBytes := range gotDIDs {
Expand Down