From 8b7a1d0268e3b2b6718c1846ccdc55bc116b6b7d Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Tue, 8 Nov 2022 17:54:04 -0500 Subject: [PATCH 01/27] Initial create, delete, get for submissions --- config/config.go | 11 ++ pkg/server/router/submission.go | 145 ++++++++++++++++++++++ pkg/server/server.go | 12 ++ pkg/service/submission/model.go | 35 ++++++ pkg/service/submission/service.go | 110 ++++++++++++++++ pkg/service/submission/storage/bolt.go | 72 +++++++++++ pkg/service/submission/storage/storage.go | 39 ++++++ 7 files changed, 424 insertions(+) create mode 100644 pkg/server/router/submission.go create mode 100644 pkg/service/submission/model.go create mode 100644 pkg/service/submission/service.go create mode 100644 pkg/service/submission/storage/bolt.go create mode 100644 pkg/service/submission/storage/storage.go diff --git a/config/config.go b/config/config.go index 34a650223..d43d35560 100644 --- a/config/config.go +++ b/config/config.go @@ -134,6 +134,17 @@ func (p *PresentationServiceConfig) IsEmpty() bool { return reflect.DeepEqual(p, &PresentationServiceConfig{}) } +type SubmissionServiceConfig struct { + *BaseServiceConfig +} + +func (d *SubmissionServiceConfig) IsEmpty() bool { + if d == nil { + return true + } + return reflect.DeepEqual(d, &SubmissionServiceConfig{}) +} + // LoadConfig attempts to load a TOML config file from the given path, and coerce it into our object model. // Before loading, defaults are applied on certain properties, which are overwritten if specified in the TOML file. func LoadConfig(path string) (*SSIServiceConfig, error) { diff --git a/pkg/server/router/submission.go b/pkg/server/router/submission.go new file mode 100644 index 000000000..cbf376380 --- /dev/null +++ b/pkg/server/router/submission.go @@ -0,0 +1,145 @@ +package router + +import ( + "context" + "fmt" + "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/tbd54566975/ssi-service/internal/keyaccess" + "github.com/tbd54566975/ssi-service/pkg/server/framework" + svcframework "github.com/tbd54566975/ssi-service/pkg/service/framework" + "github.com/tbd54566975/ssi-service/pkg/service/submission" + "net/http" +) + +type SubmissionRouter struct { + service *submission.Service +} + +func NewSubmissionRouter(s svcframework.Service) (*SubmissionRouter, error) { + if s == nil { + return nil, errors.New("service cannot be nil") + } + service, ok := s.(*submission.Service) + if !ok { + return nil, fmt.Errorf("could not create submission router with service type: %s", s.Type()) + } + return &SubmissionRouter{service: service}, nil +} + +type CreateSubmissionRequest struct { + PresentationJwt keyaccess.JWT `json:"presentationJwt" validate:"required"` +} + +type CreateSubmissionResponse struct { + Status string `json:"status"` + Submission exchange.PresentationSubmission `json:"submission"` +} + +// CreateSubmission godoc +// @Summary Create Submission +// @Description Creates a submission in this server ready to be reviewed. +// @Tags SubmissionAPI +// @Accept json +// @Produce json +// @Param request body CreateSubmissionRequest true "request body" +// @Success 201 {object} CreateSubmissionResponse +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" +// @Router /v1/presentations/submissions [put] +func (sr SubmissionRouter) CreateSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + var request CreateSubmissionRequest + if err := framework.Decode(r, &request); err != nil { + errMsg := "decoding" + logrus.WithError(err).Error(errMsg) + return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) + } + + if err := framework.ValidateRequest(request); err != nil { + errMsg := "validating" + logrus.WithError(err).Error(errMsg) + return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) + } + + // TODO: convert from request.PresentationJwt + var s exchange.PresentationSubmission + + req := submission.CreateSubmissionRequest{ + Submission: s, + } + sub, err := sr.service.CreateSubmission(req) + if err != nil { + errMsg := fmt.Sprintf("could not create submission definition") + logrus.WithError(err).Error(errMsg) + return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError) + } + + resp := CreateSubmissionResponse{Status: "pending", Submission: sub.Submission} + return framework.Respond(ctx, w, resp, http.StatusCreated) +} + +type GetSubmissionResponse struct { + ID string `json:"id"` + Submission exchange.PresentationSubmission `json:"submission"` +} + +// GetSubmission godoc +// @Summary Get Submission +// @Description Get a submission by its ID +// @Tags SubmissionAPI +// @Accept json +// @Produce json +// @Param id path string true "ID" +// @Success 200 {object} GetSubmissionResponse +// @Failure 400 {string} string "Bad request" +// @Router /v1/presentations/submission/{id} [get] +func (sr SubmissionRouter) GetSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + id := framework.GetParam(ctx, IDParam) + if id == nil { + errMsg := "cannot get submission without ID parameter" + logrus.Error(errMsg) + return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest) + } + + def, err := sr.service.GetSubmission(submission.GetSubmissionRequest{ID: *id}) + if err != nil { + errMsg := fmt.Sprintf("could not get submission with id: %s", *id) + logrus.WithError(err).Error(errMsg) + return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) + } + + resp := GetSubmissionResponse{ + ID: def.ID, + Submission: def.Submission, + } + return framework.Respond(ctx, w, resp, http.StatusOK) +} + +// DeleteSubmission godoc +// @Summary Delete Submission +// @Description Delete a submission by its ID +// @Tags SubmissionAPI +// @Accept json +// @Produce json +// @Param id path string true "ID" +// @Success 200 {string} string "OK" +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" +// @Router /v1/presentations/submissions/{id} [delete] +func (sr SubmissionRouter) DeleteSubmission(ctx context.Context, w http.ResponseWriter, _ *http.Request) error { + id := framework.GetParam(ctx, IDParam) + if id == nil { + errMsg := "cannot delete a submission without an ID parameter" + logrus.Error(errMsg) + return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest) + } + + if err := sr.service.DeleteSubmission(submission.DeleteSubmissionRequest{ID: *id}); err != nil { + errMsg := fmt.Sprintf("could not delete submission with id: %s", *id) + logrus.WithError(err).Error(errMsg) + return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError) + } + + return framework.Respond(ctx, w, nil, http.StatusOK) +} diff --git a/pkg/server/server.go b/pkg/server/server.go index ed6697a98..5b5f9bfce 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -28,6 +28,7 @@ const ( CredentialsPrefix = "/credentials" PresentationsPrefix = "/presentations" DefinitionsPrefix = "/definitions" + SubmissionsPrefix = "/presentations/submissions" ManifestsPrefix = "/manifests" ApplicationsPrefix = "/applications" ResponsesPrefix = "/responses" @@ -166,6 +167,17 @@ func (s *SSIServer) PresentationAPI(service svcframework.Service) (err error) { s.Handle(http.MethodPut, handlerPath, pRouter.CreatePresentationDefinition) s.Handle(http.MethodGet, path.Join(handlerPath, "/:id"), pRouter.GetPresentationDefinition) s.Handle(http.MethodDelete, path.Join(handlerPath, "/:id"), pRouter.DeletePresentationDefinition) + + return +} +func (s *SSIServer) SubmissionAPI(service svcframework.Service) (err error) { + sRouter, err := router.NewSubmissionRouter(service) + if err != nil { + return util.LoggingErrorMsg(err, "could not create submission router") + } + + hand := V1Prefix + SubmissionsPrefix + s.Handle(http.MethodPut, hand, sRouter.CreateSubmission) return } diff --git a/pkg/service/submission/model.go b/pkg/service/submission/model.go new file mode 100644 index 000000000..90886ec57 --- /dev/null +++ b/pkg/service/submission/model.go @@ -0,0 +1,35 @@ +package submission + +import ( + "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/TBD54566975/ssi-sdk/util" +) + +const ( + Version1 string = "1.0" +) + +type CreateSubmissionRequest struct { + Submission exchange.PresentationSubmission `json:"submission" validate:"required"` +} + +func (csr CreateSubmissionRequest) IsValid() bool { + return util.IsValidStruct(csr) == nil +} + +type CreateSubmissionResponse struct { + Submission exchange.PresentationSubmission `json:"submission"` +} + +type GetSubmissionRequest struct { + ID string `json:"id" validate:"required"` +} + +type GetSubmissionResponse struct { + ID string `json:"id"` + Submission exchange.PresentationSubmission `json:"submission"` +} + +type DeleteSubmissionRequest struct { + ID string `json:"id" validate:"required"` +} diff --git a/pkg/service/submission/service.go b/pkg/service/submission/service.go new file mode 100644 index 000000000..ecae8b612 --- /dev/null +++ b/pkg/service/submission/service.go @@ -0,0 +1,110 @@ +package submission + +import ( + "fmt" + "github.com/TBD54566975/ssi-sdk/credential/exchange" + sdkutil "github.com/TBD54566975/ssi-sdk/util" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/tbd54566975/ssi-service/config" + "github.com/tbd54566975/ssi-service/internal/util" + "github.com/tbd54566975/ssi-service/pkg/service/framework" + presentationstorage "github.com/tbd54566975/ssi-service/pkg/service/submission/storage" + "github.com/tbd54566975/ssi-service/pkg/storage" +) + +type Service struct { + storage presentationstorage.Storage + // TODO(andres) maybe change this + config config.PresentationServiceConfig +} + +func (s Service) Type() framework.Type { + return framework.Presentation +} + +func (s Service) Status() framework.Status { + ae := sdkutil.NewAppendError() + if s.storage == nil { + ae.AppendString("no storage configured") + } + if !ae.IsEmpty() { + return framework.Status{ + Status: framework.StatusNotReady, + Message: fmt.Sprintf("presentation service is not ready: %s", ae.Error().Error()), + } + } + return framework.Status{Status: framework.StatusReady} +} + +func (s Service) Config() config.PresentationServiceConfig { + return s.config +} + +func NewSubmissionService(config config.PresentationServiceConfig, s storage.ServiceStorage) (*Service, error) { + presentationStorage, err := presentationstorage.NewSubmissionStorage(s) + if err != nil { + errMsg := "could not instantiate storage for the presentation definition service" + return nil, util.LoggingErrorMsg(err, errMsg) + } + service := Service{ + storage: presentationStorage, + config: config, + } + if !service.Status().IsReady() { + return nil, errors.New(service.Status().Message) + } + return &service, nil +} + +// CreateSubmission houses the main service logic for presentation definition creation. It validates the input, and +// produces a presentation definition value that conforms with the Submission specification. +func (s Service) CreateSubmission(request CreateSubmissionRequest) (*CreateSubmissionResponse, error) { + logrus.Debugf("creating presentation definition: %+v", request) + + if !request.IsValid() { + errMsg := fmt.Sprintf("invalid create presentation definition request: %+v", request) + return nil, util.LoggingNewError(errMsg) + } + + if err := exchange.IsValidPresentationSubmission(request.Submission); err != nil { + return nil, util.LoggingErrorMsg(err, "provided value is not a valid presentation definition") + } + + storedSubmission := presentationstorage.StoredSubmission{ID: request.Submission.ID, Submission: request.Submission} + + if err := s.storage.StoreSubmission(storedSubmission); err != nil { + return nil, util.LoggingErrorMsg(err, "could not store presentation") + } + + return &CreateSubmissionResponse{ + Submission: storedSubmission.Submission, + }, nil +} + +func (s Service) GetSubmission(request GetSubmissionRequest) (*GetSubmissionResponse, error) { + logrus.Debugf("getting presentation definition: %s", request.ID) + + storedSubmission, err := s.storage.GetSubmission(request.ID) + if err != nil { + err := errors.Wrapf(err, "error getting presentation definition: %s", request.ID) + return nil, util.LoggingError(err) + } + if storedSubmission == nil { + err := fmt.Errorf("presentation definition with id<%s> could not be found", request.ID) + return nil, util.LoggingError(err) + } + return &GetSubmissionResponse{ID: storedSubmission.ID, Submission: storedSubmission.Submission}, nil +} + +func (s Service) DeleteSubmission(request DeleteSubmissionRequest) error { + logrus.Debugf("deleting presentation definition: %s", request.ID) + + if err := s.storage.DeleteSubmission(request.ID); err != nil { + errMsg := fmt.Sprintf("could not delete presentation definition with id: %s", request.ID) + return util.LoggingErrorMsg(err, errMsg) + } + + return nil +} diff --git a/pkg/service/submission/storage/bolt.go b/pkg/service/submission/storage/bolt.go new file mode 100644 index 000000000..69d53709a --- /dev/null +++ b/pkg/service/submission/storage/bolt.go @@ -0,0 +1,72 @@ +package storage + +import ( + "fmt" + + "github.com/goccy/go-json" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/tbd54566975/ssi-service/pkg/storage" +) + +const ( + namespace = "presentation_submission" +) + +type BoltSubmissionStorage struct { + db *storage.BoltDB +} + +func NewBoltSubmissionStorage(db *storage.BoltDB) (*BoltSubmissionStorage, error) { + if db == nil { + return nil, errors.New("bolt db reference is nil") + } + return &BoltSubmissionStorage{db: db}, nil +} + +func (b BoltSubmissionStorage) StoreSubmission(submission StoredSubmission) error { + id := submission.ID + if id == "" { + err := errors.New("could not store submission definition without an ID") + logrus.WithError(err).Error() + return err + } + jsonBytes, err := json.Marshal(submission) + if err != nil { + errMsg := fmt.Sprintf("could not store submission definition: %s", id) + logrus.WithError(err).Error(errMsg) + return errors.Wrapf(err, errMsg) + } + return b.db.Write(namespace, id, jsonBytes) +} + +func (b BoltSubmissionStorage) GetSubmission(id string) (*StoredSubmission, error) { + jsonBytes, err := b.db.Read(namespace, id) + if err != nil { + errMsg := fmt.Sprintf("could not get submission definition: %s", id) + logrus.WithError(err).Error(errMsg) + return nil, errors.Wrapf(err, errMsg) + } + if len(jsonBytes) == 0 { + err := fmt.Errorf("submission definition not found with id: %s", id) + logrus.WithError(err).Error("could not get submission definition from storage") + return nil, err + } + var stored StoredSubmission + if err := json.Unmarshal(jsonBytes, &stored); err != nil { + errMsg := fmt.Sprintf("could not unmarshal stored submission definition: %s", id) + logrus.WithError(err).Error(errMsg) + return nil, errors.Wrapf(err, errMsg) + } + return &stored, nil +} + +func (b BoltSubmissionStorage) DeleteSubmission(id string) error { + if err := b.db.Delete(namespace, id); err != nil { + errMsg := fmt.Sprintf("could not delete submission definition: %s", id) + logrus.WithError(err).Error(errMsg) + return errors.Wrapf(err, errMsg) + } + return nil +} diff --git a/pkg/service/submission/storage/storage.go b/pkg/service/submission/storage/storage.go new file mode 100644 index 000000000..0ebdeda34 --- /dev/null +++ b/pkg/service/submission/storage/storage.go @@ -0,0 +1,39 @@ +package storage + +import ( + "fmt" + "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/tbd54566975/ssi-service/internal/util" + "github.com/tbd54566975/ssi-service/pkg/storage" +) + +type StoredSubmission struct { + ID string `json:"id"` + Submission exchange.PresentationSubmission `json:"submission"` +} + +type Storage interface { + StoreSubmission(schema StoredSubmission) error + GetSubmission(id string) (*StoredSubmission, error) + DeleteSubmission(id string) error +} + +// NewSubmissionStorage finds the submission storage impl for a given ServiceStorage value +func NewSubmissionStorage(s storage.ServiceStorage) (Storage, error) { + switch s.Type() { + case storage.Bolt: + gotBolt, ok := s.(*storage.BoltDB) + if !ok { + errMsg := fmt.Sprintf("trouble instantiating : %s", s.Type()) + return nil, util.LoggingNewError(errMsg) + } + boltStorage, err := NewBoltSubmissionStorage(gotBolt) + if err != nil { + return nil, util.LoggingErrorMsg(err, "could not instantiate schema bolt storage") + } + return boltStorage, err + default: + errMsg := fmt.Errorf("unsupported storage type: %s", s.Type()) + return nil, util.LoggingError(errMsg) + } +} From 18c6eab0d267f19e953823dd2026753a87026940 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Thu, 10 Nov 2022 17:15:26 -0500 Subject: [PATCH 02/27] Basic tests for the router --- pkg/server/router/submission.go | 22 +++- pkg/server/router/submission_test.go | 134 ++++++++++++++++++++++ pkg/service/submission/model.go | 1 - pkg/service/submission/service.go | 4 +- pkg/service/submission/storage/bolt.go | 2 +- pkg/service/submission/storage/storage.go | 1 - 6 files changed, 154 insertions(+), 10 deletions(-) create mode 100644 pkg/server/router/submission_test.go diff --git a/pkg/server/router/submission.go b/pkg/server/router/submission.go index cbf376380..f4e1fca18 100644 --- a/pkg/server/router/submission.go +++ b/pkg/server/router/submission.go @@ -33,6 +33,7 @@ type CreateSubmissionRequest struct { } type CreateSubmissionResponse struct { + // TODO(andres): return an operation here. Status string `json:"status"` Submission exchange.PresentationSubmission `json:"submission"` } @@ -50,20 +51,30 @@ type CreateSubmissionResponse struct { // @Router /v1/presentations/submissions [put] func (sr SubmissionRouter) CreateSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { var request CreateSubmissionRequest + errMsg := "Invalid create submission request" if err := framework.Decode(r, &request); err != nil { - errMsg := "decoding" logrus.WithError(err).Error(errMsg) return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) } if err := framework.ValidateRequest(request); err != nil { - errMsg := "validating" logrus.WithError(err).Error(errMsg) return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) } // TODO: convert from request.PresentationJwt - var s exchange.PresentationSubmission + s := exchange.PresentationSubmission{ + ID: "dummy value", + DefinitionID: "another dummy", + DescriptorMap: []exchange.SubmissionDescriptor{ + { + ID: "what?", + Format: "jwt_vp", + Path: "ohhh yeah", + PathNested: nil, + }, + }, + } req := submission.CreateSubmissionRequest{ Submission: s, @@ -80,7 +91,6 @@ func (sr SubmissionRouter) CreateSubmission(ctx context.Context, w http.Response } type GetSubmissionResponse struct { - ID string `json:"id"` Submission exchange.PresentationSubmission `json:"submission"` } @@ -108,9 +118,11 @@ func (sr SubmissionRouter) GetSubmission(ctx context.Context, w http.ResponseWri logrus.WithError(err).Error(errMsg) return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) } + if def == nil { + return framework.NewRequestError(fmt.Errorf("submission with id: %s", *id), http.StatusNotFound) + } resp := GetSubmissionResponse{ - ID: def.ID, Submission: def.Submission, } return framework.Respond(ctx, w, resp, http.StatusOK) diff --git a/pkg/server/router/submission_test.go b/pkg/server/router/submission_test.go new file mode 100644 index 000000000..082d6d2f4 --- /dev/null +++ b/pkg/server/router/submission_test.go @@ -0,0 +1,134 @@ +package router + +import ( + "context" + "fmt" + "github.com/dimfeld/httptreemux/v5" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/tbd54566975/ssi-service/config" + "github.com/tbd54566975/ssi-service/pkg/server/framework" + "github.com/tbd54566975/ssi-service/pkg/service/submission" + "github.com/tbd54566975/ssi-service/pkg/storage" + "io" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + "time" +) + +func TestSubmissionRouter_CreateSubmission(t *testing.T) { + router := createRouter(t) + + t.Run("simple jwt return submission object", func(t *testing.T) { + t.Cleanup(func() { + _ = os.Remove(storage.DBFile) + }) + + jsonRequestString := `{"presentationJwt":"hello"}` + r := httptest.NewRequest(http.MethodPut, "http://localhost/v1/presentations/submissions", strings.NewReader(jsonRequestString)) + w := httptest.NewRecorder() + + assert.NoError(t, router.CreateSubmission(newRequestContext(), w, r)) + + readBody, err := io.ReadAll(w.Body) + assert.NoError(t, err) + assert.Equal(t, `{"status":"pending","submission":{"id":"dummy value","definition_id":"another dummy","descriptor_map":[{"id":"what?","format":"jwt_vp","path":"ohhh yeah"}]}}`, fmt.Sprintf("%s", readBody)) + }) + + t.Run("missing jwt returns error", func(t *testing.T) { + jsonRequestString := `{}` + r := httptest.NewRequest(http.MethodPut, "http://localhost/v1/presentations/submissions", strings.NewReader(jsonRequestString)) + w := httptest.NewRecorder() + + assert.Error(t, router.CreateSubmission(newRequestContext(), w, r)) + }) +} + +func createRouter(t *testing.T) *SubmissionRouter { + svcStorage, err := storage.NewStorage(storage.Bolt) + assert.NoError(t, err) + submissionService, err := submission.NewSubmissionService(config.PresentationServiceConfig{}, svcStorage) + assert.NoError(t, err) + router, err := NewSubmissionRouter(submissionService) + assert.NoError(t, err) + return router +} + +func TestSubmissionRouter_GetSubmission(t *testing.T) { + router := createRouter(t) + t.Run("get returns error when not found", func(t *testing.T) { + jsonRequestString := `{"presentationJwt":"hello"}` + r := httptest.NewRequest(http.MethodPut, "http://localhost/v1/presentations/submissions", strings.NewReader(jsonRequestString)) + w := httptest.NewRecorder() + + assert.Error(t, router.GetSubmission(newRequestContextWithParams(map[string]string{"id": "dummy value"}), w, r)) + }) + + t.Run("returns resource after creating it", func(t *testing.T) { + t.Cleanup(func() { + _ = os.Remove(storage.DBFile) + }) + jsonRequestString := `{"presentationJwt":"hello"}` + r := httptest.NewRequest(http.MethodPut, "http://localhost/v1/presentations/submissions", strings.NewReader(jsonRequestString)) + w := httptest.NewRecorder() + assert.NoError(t, router.CreateSubmission(newRequestContext(), w, r)) + + r = httptest.NewRequest(http.MethodGet, "http://localhost/v1/presentations/submissions", strings.NewReader("")) + w = httptest.NewRecorder() + assert.NoError(t, router.GetSubmission(newRequestContextWithParams(map[string]string{"id": "dummy value"}), w, r)) + + readBody, err := io.ReadAll(w.Body) + assert.NoError(t, err) + assert.Equal(t, `{"submission":{"id":"dummy value","definition_id":"another dummy","descriptor_map":[{"id":"what?","format":"jwt_vp","path":"ohhh yeah"}]}}`, fmt.Sprintf("%s", readBody)) + }) +} + +func TestSubmissionRouter_DeleteSubmission(t *testing.T) { + router := createRouter(t) + t.Run("error when id is not present", func(t *testing.T) { + r := httptest.NewRequest(http.MethodDelete, "http://localhost/v1/presentations/submissions", strings.NewReader("")) + w := httptest.NewRecorder() + assert.Error(t, router.DeleteSubmission(newRequestContextWithParams(map[string]string{"id": ""}), w, r)) + }) + + t.Run("get returns empty after deletion", func(t *testing.T) { + t.Cleanup(func() { + _ = os.Remove(storage.DBFile) + }) + jsonRequestString := `{"presentationJwt":"hello"}` + r := httptest.NewRequest(http.MethodPut, "http://localhost/v1/presentations/submissions", strings.NewReader(jsonRequestString)) + w := httptest.NewRecorder() + assert.NoError(t, router.CreateSubmission(newRequestContext(), w, r)) + r = httptest.NewRequest(http.MethodGet, "http://localhost/v1/presentations/submissions", strings.NewReader("")) + assert.NoError(t, router.GetSubmission(newRequestContextWithParams(map[string]string{"id": "dummy value"}), w, r)) + + r = httptest.NewRequest(http.MethodDelete, "http://localhost/v1/presentations/submissions", strings.NewReader("")) + assert.NoError(t, router.DeleteSubmission(newRequestContextWithParams(map[string]string{"id": "dummy value"}), w, r)) + + r = httptest.NewRequest(http.MethodGet, "http://localhost/v1/presentations/submissions", strings.NewReader("")) + w = httptest.NewRecorder() + assert.Error(t, router.GetSubmission(newRequestContextWithParams(map[string]string{"id": "dummy value"}), w, r)) + }) +} + +// construct a context value as expected by our handler +func newRequestContext() context.Context { + return context.WithValue(context.Background(), framework.KeyRequestState, &framework.RequestState{ + TraceID: uuid.New().String(), + Now: time.Now(), + StatusCode: 1, + }) +} + +// as required by https://github.com/dimfeld/httptreemux's context handler +func newRequestContextWithParams(params map[string]string) context.Context { + ctx := context.WithValue(context.Background(), framework.KeyRequestState, &framework.RequestState{ + TraceID: uuid.New().String(), + Now: time.Now(), + StatusCode: 1, + }) + return httptreemux.AddParamsToContext(ctx, params) +} diff --git a/pkg/service/submission/model.go b/pkg/service/submission/model.go index 90886ec57..025dd5bdd 100644 --- a/pkg/service/submission/model.go +++ b/pkg/service/submission/model.go @@ -26,7 +26,6 @@ type GetSubmissionRequest struct { } type GetSubmissionResponse struct { - ID string `json:"id"` Submission exchange.PresentationSubmission `json:"submission"` } diff --git a/pkg/service/submission/service.go b/pkg/service/submission/service.go index ecae8b612..92f15e17f 100644 --- a/pkg/service/submission/service.go +++ b/pkg/service/submission/service.go @@ -72,7 +72,7 @@ func (s Service) CreateSubmission(request CreateSubmissionRequest) (*CreateSubmi return nil, util.LoggingErrorMsg(err, "provided value is not a valid presentation definition") } - storedSubmission := presentationstorage.StoredSubmission{ID: request.Submission.ID, Submission: request.Submission} + storedSubmission := presentationstorage.StoredSubmission{Submission: request.Submission} if err := s.storage.StoreSubmission(storedSubmission); err != nil { return nil, util.LoggingErrorMsg(err, "could not store presentation") @@ -95,7 +95,7 @@ func (s Service) GetSubmission(request GetSubmissionRequest) (*GetSubmissionResp err := fmt.Errorf("presentation definition with id<%s> could not be found", request.ID) return nil, util.LoggingError(err) } - return &GetSubmissionResponse{ID: storedSubmission.ID, Submission: storedSubmission.Submission}, nil + return &GetSubmissionResponse{Submission: storedSubmission.Submission}, nil } func (s Service) DeleteSubmission(request DeleteSubmissionRequest) error { diff --git a/pkg/service/submission/storage/bolt.go b/pkg/service/submission/storage/bolt.go index 69d53709a..00915dbda 100644 --- a/pkg/service/submission/storage/bolt.go +++ b/pkg/service/submission/storage/bolt.go @@ -26,7 +26,7 @@ func NewBoltSubmissionStorage(db *storage.BoltDB) (*BoltSubmissionStorage, error } func (b BoltSubmissionStorage) StoreSubmission(submission StoredSubmission) error { - id := submission.ID + id := submission.Submission.ID if id == "" { err := errors.New("could not store submission definition without an ID") logrus.WithError(err).Error() diff --git a/pkg/service/submission/storage/storage.go b/pkg/service/submission/storage/storage.go index 0ebdeda34..fdebac4a4 100644 --- a/pkg/service/submission/storage/storage.go +++ b/pkg/service/submission/storage/storage.go @@ -8,7 +8,6 @@ import ( ) type StoredSubmission struct { - ID string `json:"id"` Submission exchange.PresentationSubmission `json:"submission"` } From 4820940c630d82a9a10a694dd3767e55127ef28f Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Thu, 10 Nov 2022 17:57:54 -0500 Subject: [PATCH 03/27] Added the rest of the scaffolding --- config/config.go | 4 + doc/swagger.yaml | 224 +++++++++++++++++---------- pkg/server/router/submission.go | 4 +- pkg/server/router/submission_test.go | 2 +- pkg/server/server.go | 2 + pkg/service/framework/framework.go | 1 + pkg/service/service.go | 11 +- pkg/service/submission/service.go | 43 +++-- 8 files changed, 185 insertions(+), 106 deletions(-) diff --git a/config/config.go b/config/config.go index d43d35560..0f904eb4b 100644 --- a/config/config.go +++ b/config/config.go @@ -54,6 +54,7 @@ type ServicesConfig struct { CredentialConfig CredentialServiceConfig `toml:"credential,omitempty"` ManifestConfig ManifestServiceConfig `toml:"manifest,omitempty"` PresentationConfig PresentationServiceConfig `toml:"presentation,omitempty"` + SubmissionConfig SubmissionServiceConfig `toml:"submission,omitempty"` } // BaseServiceConfig represents configurable properties for a specific component of the SSI Service @@ -209,6 +210,9 @@ func LoadConfig(path string) (*SSIServiceConfig, error) { PresentationConfig: PresentationServiceConfig{ BaseServiceConfig: &BaseServiceConfig{Name: "presentation"}, }, + SubmissionConfig: SubmissionServiceConfig{ + BaseServiceConfig: &BaseServiceConfig{Name: "submission"}, + }, } } else { // load from TOML file diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 98284863f..9799227f3 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -223,16 +223,6 @@ definitions: - id - type type: object - dwn.PublishManifestResponse: - properties: - response: - type: string - status: - type: integer - required: - - response - - status - type: object exchange.ClaimFormat: properties: jwt: @@ -297,6 +287,10 @@ definitions: $ref: '#/definitions/exchange.Filter' id: type: string + intent_to_retain: + type: boolean + name: + type: string optional: type: boolean path: @@ -525,12 +519,16 @@ definitions: type: object github.com_tbd54566975_ssi-service_pkg_server_router.CreateManifestRequest: properties: + description: + type: string format: $ref: '#/definitions/exchange.ClaimFormat' issuerDid: type: string issuerName: type: string + name: + type: string outputDescriptors: items: $ref: '#/definitions/manifest.OutputDescriptor' @@ -587,6 +585,21 @@ definitions: schemaJwt: type: string type: object + github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionRequest: + properties: + presentationJwt: + type: string + required: + - presentationJwt + type: object + github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionResponse: + properties: + status: + description: 'TODO(andres): return an operation here.' + type: string + submission: + $ref: '#/definitions/exchange.PresentationSubmission' + type: object github.com_tbd54566975_ssi-service_pkg_server_router.GetApplicationResponse: properties: application: @@ -703,22 +716,10 @@ definitions: $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemaResponse' type: array type: object - github.com_tbd54566975_ssi-service_pkg_server_router.PublishManifestRequest: + github.com_tbd54566975_ssi-service_pkg_server_router.GetSubmissionResponse: properties: - manifestId: - type: string - required: - - manifestId - type: object - github.com_tbd54566975_ssi-service_pkg_server_router.PublishManifestResponse: - properties: - dwnResponse: - $ref: '#/definitions/dwn.PublishManifestResponse' - manifest: - $ref: '#/definitions/manifest.CredentialManifest' - required: - - dwnResponse - - manifest + submission: + $ref: '#/definitions/exchange.PresentationSubmission' type: object github.com_tbd54566975_ssi-service_pkg_server_router.ResolveDIDResponse: properties: @@ -813,12 +814,16 @@ definitions: type: object manifest.CredentialManifest: properties: + description: + type: string format: $ref: '#/definitions/exchange.ClaimFormat' id: type: string issuer: $ref: '#/definitions/manifest.Issuer' + name: + type: string output_descriptors: items: $ref: '#/definitions/manifest.OutputDescriptor' @@ -952,12 +957,16 @@ definitions: type: object pkg_server_router.CreateManifestRequest: properties: + description: + type: string format: $ref: '#/definitions/exchange.ClaimFormat' issuerDid: type: string issuerName: type: string + name: + type: string outputDescriptors: items: $ref: '#/definitions/manifest.OutputDescriptor' @@ -1014,6 +1023,21 @@ definitions: schemaJwt: type: string type: object + pkg_server_router.CreateSubmissionRequest: + properties: + presentationJwt: + type: string + required: + - presentationJwt + type: object + pkg_server_router.CreateSubmissionResponse: + properties: + status: + description: 'TODO(andres): return an operation here.' + type: string + submission: + $ref: '#/definitions/exchange.PresentationSubmission' + type: object pkg_server_router.GetApplicationResponse: properties: application: @@ -1130,22 +1154,10 @@ definitions: $ref: '#/definitions/pkg_server_router.GetSchemaResponse' type: array type: object - pkg_server_router.PublishManifestRequest: + pkg_server_router.GetSubmissionResponse: properties: - manifestId: - type: string - required: - - manifestId - type: object - pkg_server_router.PublishManifestResponse: - properties: - dwnResponse: - $ref: '#/definitions/dwn.PublishManifestResponse' - manifest: - $ref: '#/definitions/manifest.CredentialManifest' - required: - - dwnResponse - - manifest + submission: + $ref: '#/definitions/exchange.PresentationSubmission' type: object pkg_server_router.ResolveDIDResponse: properties: @@ -1400,7 +1412,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialsResponse' + $ref: '#/definitions/pkg_server_router.GetCredentialsResponse' "400": description: Bad request schema: @@ -1422,14 +1434,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateCredentialRequest' + $ref: '#/definitions/pkg_server_router.CreateCredentialRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateCredentialResponse' + $ref: '#/definitions/pkg_server_router.CreateCredentialResponse' "400": description: Bad request schema: @@ -1486,7 +1498,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialResponse' + $ref: '#/definitions/pkg_server_router.GetCredentialResponse' "400": description: Bad request schema: @@ -1505,14 +1517,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifyCredentialRequest' + $ref: '#/definitions/pkg_server_router.VerifyCredentialRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifyCredentialResponse' + $ref: '#/definitions/pkg_server_router.VerifyCredentialResponse' "400": description: Bad request schema: @@ -1655,36 +1667,6 @@ paths: summary: Resolve a DID tags: - DecentralizedIdentityAPI - /v1/dwn/manifest: - put: - consumes: - - application/json - description: Publish Manifest to DWN - parameters: - - description: request body - in: body - name: request - required: true - schema: - $ref: '#/definitions/pkg_server_router.PublishManifestRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/pkg_server_router.PublishManifestResponse' - "400": - description: Bad request - schema: - type: string - "500": - description: Internal server error - schema: - type: string - summary: Publish Manifest to DWN - tags: - - DWNAPI /v1/keys: put: consumes: @@ -2052,14 +2034,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreatePresentationDefinitionRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.CreatePresentationDefinitionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionResponse' "400": description: Bad request schema: @@ -2116,7 +2098,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetPresentationDefinitionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetPresentationDefinitionResponse' "400": description: Bad request schema: @@ -2124,6 +2106,90 @@ paths: summary: Get PresentationDefinition tags: - PresentationDefinitionAPI + /v1/presentations/submission/{id}: + get: + consumes: + - application/json + description: Get a submission by its ID + parameters: + - description: ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSubmissionResponse' + "400": + description: Bad request + schema: + type: string + summary: Get Submission + tags: + - SubmissionAPI + /v1/presentations/submissions: + put: + consumes: + - application/json + description: Creates a submission in this server ready to be reviewed. + parameters: + - description: request body + in: body + name: request + required: true + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionResponse' + "400": + description: Bad request + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Create Submission + tags: + - SubmissionAPI + /v1/presentations/submissions/{id}: + delete: + consumes: + - application/json + description: Delete a submission by its ID + parameters: + - description: ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad request + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Delete Submission + tags: + - SubmissionAPI /v1/schemas: get: consumes: diff --git a/pkg/server/router/submission.go b/pkg/server/router/submission.go index f4e1fca18..02a0e45c2 100644 --- a/pkg/server/router/submission.go +++ b/pkg/server/router/submission.go @@ -118,9 +118,7 @@ func (sr SubmissionRouter) GetSubmission(ctx context.Context, w http.ResponseWri logrus.WithError(err).Error(errMsg) return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) } - if def == nil { - return framework.NewRequestError(fmt.Errorf("submission with id: %s", *id), http.StatusNotFound) - } + // TODO(andres): introduce not found errors that can be mapped to 404. resp := GetSubmissionResponse{ Submission: def.Submission, diff --git a/pkg/server/router/submission_test.go b/pkg/server/router/submission_test.go index 082d6d2f4..170f8d171 100644 --- a/pkg/server/router/submission_test.go +++ b/pkg/server/router/submission_test.go @@ -50,7 +50,7 @@ func TestSubmissionRouter_CreateSubmission(t *testing.T) { func createRouter(t *testing.T) *SubmissionRouter { svcStorage, err := storage.NewStorage(storage.Bolt) assert.NoError(t, err) - submissionService, err := submission.NewSubmissionService(config.PresentationServiceConfig{}, svcStorage) + submissionService, err := submission.NewSubmissionService(config.SubmissionServiceConfig{}, svcStorage) assert.NoError(t, err) router, err := NewSubmissionRouter(submissionService) assert.NoError(t, err) diff --git a/pkg/server/server.go b/pkg/server/server.go index 5b5f9bfce..752a6b75b 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -102,6 +102,8 @@ func (s *SSIServer) instantiateRouter(service svcframework.Service) error { return s.ManifestAPI(service) case svcframework.Presentation: return s.PresentationAPI(service) + case svcframework.Submission: + return s.SubmissionAPI(service) default: return fmt.Errorf("could not instantiate API for service: %s", serviceType) } diff --git a/pkg/service/framework/framework.go b/pkg/service/framework/framework.go index d357e3ebb..926491ad7 100644 --- a/pkg/service/framework/framework.go +++ b/pkg/service/framework/framework.go @@ -14,6 +14,7 @@ const ( KeyStore Type = "keystore" Manifest Type = "manifest" Presentation Type = "presentation" + Submission Type = "submission" StatusReady StatusState = "ready" StatusNotReady StatusState = "not_ready" diff --git a/pkg/service/service.go b/pkg/service/service.go index 7e30f9b0d..d3703f731 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -3,6 +3,7 @@ package service import ( "fmt" "github.com/tbd54566975/ssi-service/pkg/service/presentation" + "github.com/tbd54566975/ssi-service/pkg/service/submission" "github.com/tbd54566975/ssi-service/config" "github.com/tbd54566975/ssi-service/internal/util" @@ -55,6 +56,9 @@ func validateServiceConfig(config config.ServicesConfig) error { if config.PresentationConfig.IsEmpty() { return fmt.Errorf("%s no config provided", framework.Presentation) } + if config.SubmissionConfig.IsEmpty() { + return fmt.Errorf("%s no config provided", framework.Submission) + } return nil } @@ -101,5 +105,10 @@ func instantiateServices(config config.ServicesConfig) ([]framework.Service, err return nil, util.LoggingErrorMsg(err, "could not instantiate the presentation service") } - return []framework.Service{keyStoreService, didService, schemaService, credentialService, manifestService, presentationService}, nil + submissionService, err := submission.NewSubmissionService(config.SubmissionConfig, storageProvider) + if err != nil { + return nil, util.LoggingErrorMsg(err, "could not instantiate the presentation service") + } + + return []framework.Service{keyStoreService, didService, schemaService, credentialService, manifestService, presentationService, submissionService}, nil } diff --git a/pkg/service/submission/service.go b/pkg/service/submission/service.go index 92f15e17f..6bcfa938a 100644 --- a/pkg/service/submission/service.go +++ b/pkg/service/submission/service.go @@ -10,18 +10,17 @@ import ( "github.com/tbd54566975/ssi-service/config" "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/service/framework" - presentationstorage "github.com/tbd54566975/ssi-service/pkg/service/submission/storage" + submissionstorage "github.com/tbd54566975/ssi-service/pkg/service/submission/storage" "github.com/tbd54566975/ssi-service/pkg/storage" ) type Service struct { - storage presentationstorage.Storage - // TODO(andres) maybe change this - config config.PresentationServiceConfig + storage submissionstorage.Storage + config config.SubmissionServiceConfig } func (s Service) Type() framework.Type { - return framework.Presentation + return framework.Submission } func (s Service) Status() framework.Status { @@ -32,24 +31,24 @@ func (s Service) Status() framework.Status { if !ae.IsEmpty() { return framework.Status{ Status: framework.StatusNotReady, - Message: fmt.Sprintf("presentation service is not ready: %s", ae.Error().Error()), + Message: fmt.Sprintf("submission service is not ready: %s", ae.Error().Error()), } } return framework.Status{Status: framework.StatusReady} } -func (s Service) Config() config.PresentationServiceConfig { +func (s Service) Config() config.SubmissionServiceConfig { return s.config } -func NewSubmissionService(config config.PresentationServiceConfig, s storage.ServiceStorage) (*Service, error) { - presentationStorage, err := presentationstorage.NewSubmissionStorage(s) +func NewSubmissionService(config config.SubmissionServiceConfig, s storage.ServiceStorage) (*Service, error) { + submissionStorage, err := submissionstorage.NewSubmissionStorage(s) if err != nil { - errMsg := "could not instantiate storage for the presentation definition service" + errMsg := "could not instantiate storage for the submission service" return nil, util.LoggingErrorMsg(err, errMsg) } service := Service{ - storage: presentationStorage, + storage: submissionStorage, config: config, } if !service.Status().IsReady() { @@ -58,21 +57,21 @@ func NewSubmissionService(config config.PresentationServiceConfig, s storage.Ser return &service, nil } -// CreateSubmission houses the main service logic for presentation definition creation. It validates the input, and -// produces a presentation definition value that conforms with the Submission specification. +// CreateSubmission houses the main service logic for presentation submission creation. It validates the input, and +// produces a presentation submission value that conforms with the Submission specification. func (s Service) CreateSubmission(request CreateSubmissionRequest) (*CreateSubmissionResponse, error) { - logrus.Debugf("creating presentation definition: %+v", request) + logrus.Debugf("creating presentation submission: %+v", request) if !request.IsValid() { - errMsg := fmt.Sprintf("invalid create presentation definition request: %+v", request) + errMsg := fmt.Sprintf("invalid create presentation submission request: %+v", request) return nil, util.LoggingNewError(errMsg) } if err := exchange.IsValidPresentationSubmission(request.Submission); err != nil { - return nil, util.LoggingErrorMsg(err, "provided value is not a valid presentation definition") + return nil, util.LoggingErrorMsg(err, "provided value is not a valid presentation submission") } - storedSubmission := presentationstorage.StoredSubmission{Submission: request.Submission} + storedSubmission := submissionstorage.StoredSubmission{Submission: request.Submission} if err := s.storage.StoreSubmission(storedSubmission); err != nil { return nil, util.LoggingErrorMsg(err, "could not store presentation") @@ -84,25 +83,25 @@ func (s Service) CreateSubmission(request CreateSubmissionRequest) (*CreateSubmi } func (s Service) GetSubmission(request GetSubmissionRequest) (*GetSubmissionResponse, error) { - logrus.Debugf("getting presentation definition: %s", request.ID) + logrus.Debugf("getting presentation submission: %s", request.ID) storedSubmission, err := s.storage.GetSubmission(request.ID) if err != nil { - err := errors.Wrapf(err, "error getting presentation definition: %s", request.ID) + err := errors.Wrapf(err, "error getting presentation submission: %s", request.ID) return nil, util.LoggingError(err) } if storedSubmission == nil { - err := fmt.Errorf("presentation definition with id<%s> could not be found", request.ID) + err := fmt.Errorf("presentation submission with id<%s> could not be found", request.ID) return nil, util.LoggingError(err) } return &GetSubmissionResponse{Submission: storedSubmission.Submission}, nil } func (s Service) DeleteSubmission(request DeleteSubmissionRequest) error { - logrus.Debugf("deleting presentation definition: %s", request.ID) + logrus.Debugf("deleting presentation submission: %s", request.ID) if err := s.storage.DeleteSubmission(request.ID); err != nil { - errMsg := fmt.Sprintf("could not delete presentation definition with id: %s", request.ID) + errMsg := fmt.Sprintf("could not delete presentation submission with id: %s", request.ID) return util.LoggingErrorMsg(err, errMsg) } From 1988345dca4e30bcf962f67fb67ab08968703fe4 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Fri, 11 Nov 2022 09:32:40 -0500 Subject: [PATCH 04/27] Added List and Review --- doc/swagger.yaml | 141 +++++++++++++++++++++++++++----- pkg/server/router/submission.go | 47 +++++++++++ pkg/server/server.go | 9 +- 3 files changed, 175 insertions(+), 22 deletions(-) diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 9799227f3..795505f6e 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -549,10 +549,22 @@ definitions: type: object github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionRequest: properties: - presentation_definition: - $ref: '#/definitions/exchange.PresentationDefinition' + format: + $ref: '#/definitions/exchange.ClaimFormat' + inputDescriptors: + items: + $ref: '#/definitions/exchange.InputDescriptor' + type: array + name: + type: string + purpose: + type: string + submissionRequirements: + items: + $ref: '#/definitions/exchange.SubmissionRequirement' + type: array required: - - presentation_definition + - inputDescriptors type: object github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionResponse: properties: @@ -683,8 +695,6 @@ definitions: type: object github.com_tbd54566975_ssi-service_pkg_server_router.GetPresentationDefinitionResponse: properties: - id: - type: string presentation_definition: $ref: '#/definitions/exchange.PresentationDefinition' type: object @@ -721,6 +731,18 @@ definitions: submission: $ref: '#/definitions/exchange.PresentationSubmission' type: object + github.com_tbd54566975_ssi-service_pkg_server_router.ListSubmissionRequest: + properties: + filter: + type: string + type: object + github.com_tbd54566975_ssi-service_pkg_server_router.ListSubmissionResponse: + properties: + submissions: + items: + $ref: '#/definitions/exchange.PresentationSubmission' + type: array + type: object github.com_tbd54566975_ssi-service_pkg_server_router.ResolveDIDResponse: properties: didDocument: @@ -730,6 +752,20 @@ definitions: didResolutionMetadata: $ref: '#/definitions/did.DIDResolutionMetadata' type: object + github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionRequest: + properties: + approved: + type: boolean + reason: + type: string + required: + - approved + type: object + github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionResponse: + properties: + submission: + $ref: '#/definitions/exchange.PresentationSubmission' + type: object github.com_tbd54566975_ssi-service_pkg_server_router.StoreKeyRequest: properties: base58PrivateKey: @@ -987,10 +1023,22 @@ definitions: type: object pkg_server_router.CreatePresentationDefinitionRequest: properties: - presentation_definition: - $ref: '#/definitions/exchange.PresentationDefinition' + format: + $ref: '#/definitions/exchange.ClaimFormat' + inputDescriptors: + items: + $ref: '#/definitions/exchange.InputDescriptor' + type: array + name: + type: string + purpose: + type: string + submissionRequirements: + items: + $ref: '#/definitions/exchange.SubmissionRequirement' + type: array required: - - presentation_definition + - inputDescriptors type: object pkg_server_router.CreatePresentationDefinitionResponse: properties: @@ -1121,8 +1169,6 @@ definitions: type: object pkg_server_router.GetPresentationDefinitionResponse: properties: - id: - type: string presentation_definition: $ref: '#/definitions/exchange.PresentationDefinition' type: object @@ -1159,6 +1205,18 @@ definitions: submission: $ref: '#/definitions/exchange.PresentationSubmission' type: object + pkg_server_router.ListSubmissionRequest: + properties: + filter: + type: string + type: object + pkg_server_router.ListSubmissionResponse: + properties: + submissions: + items: + $ref: '#/definitions/exchange.PresentationSubmission' + type: array + type: object pkg_server_router.ResolveDIDResponse: properties: didDocument: @@ -1168,6 +1226,20 @@ definitions: didResolutionMetadata: $ref: '#/definitions/did.DIDResolutionMetadata' type: object + pkg_server_router.ReviewSubmissionRequest: + properties: + approved: + type: boolean + reason: + type: string + required: + - approved + type: object + pkg_server_router.ReviewSubmissionResponse: + properties: + submission: + $ref: '#/definitions/exchange.PresentationSubmission' + type: object pkg_server_router.StoreKeyRequest: properties: base58PrivateKey: @@ -2034,14 +2106,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionRequest' + $ref: '#/definitions/pkg_server_router.CreatePresentationDefinitionRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionResponse' + $ref: '#/definitions/pkg_server_router.CreatePresentationDefinitionResponse' "400": description: Bad request schema: @@ -2098,7 +2170,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetPresentationDefinitionResponse' + $ref: '#/definitions/pkg_server_router.GetPresentationDefinitionResponse' "400": description: Bad request schema: @@ -2132,6 +2204,37 @@ paths: tags: - SubmissionAPI /v1/presentations/submissions: + get: + consumes: + - application/json + description: Reviews a pending submission. After this method is called, the + operation with `id==presentations/submissions/{submission_id}` will be updated + with the result of this invocation. + parameters: + - description: request body + in: body + name: request + required: true + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionResponse' + "400": + description: Bad request + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Review a pending submissions + tags: + - SubmissionAPI put: consumes: - application/json @@ -2201,7 +2304,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemasResponse' + $ref: '#/definitions/pkg_server_router.GetSchemasResponse' "500": description: Internal server error schema: @@ -2219,14 +2322,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaRequest' + $ref: '#/definitions/pkg_server_router.CreateSchemaRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaResponse' + $ref: '#/definitions/pkg_server_router.CreateSchemaResponse' "400": description: Bad request schema: @@ -2283,7 +2386,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemaResponse' + $ref: '#/definitions/pkg_server_router.GetSchemaResponse' "400": description: Bad request schema: @@ -2302,14 +2405,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaRequest' + $ref: '#/definitions/pkg_server_router.VerifySchemaRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaResponse' + $ref: '#/definitions/pkg_server_router.VerifySchemaResponse' "400": description: Bad request schema: diff --git a/pkg/server/router/submission.go b/pkg/server/router/submission.go index 02a0e45c2..ac224b407 100644 --- a/pkg/server/router/submission.go +++ b/pkg/server/router/submission.go @@ -153,3 +153,50 @@ func (sr SubmissionRouter) DeleteSubmission(ctx context.Context, w http.Response return framework.Respond(ctx, w, nil, http.StatusOK) } + +type ListSubmissionRequest struct { + Filter string `json:"filter"` +} + +type ListSubmissionResponse struct { + Submissions []exchange.PresentationSubmission `json:"submissions"` +} + +// ListSubmissions godoc +// @Summary List Submissions +// @Description List existing submissions according to a filtering query. +// @Tags SubmissionAPI +// @Accept json +// @Produce json +// @Param request body ListSubmissionRequest true "request body" +// @Success 200 {object} ListSubmissionResponse +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" +// @Router /v1/presentations/submissions [get] +func (sr SubmissionRouter) ListSubmissions(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + return framework.Respond(ctx, w, nil, http.StatusOK) +} + +type ReviewSubmissionRequest struct { + Approved bool `json:"approved" validate:"required"` + Reason string `json:"reason"` +} + +type ReviewSubmissionResponse struct { + Submission exchange.PresentationSubmission `json:"submission"` +} + +// ReviewSubmission godoc +// @Summary Review a pending submissions +// @Description Reviews a pending submission. After this method is called, the operation with `id==presentations/submissions/{submission_id}` will be updated with the result of this invocation. +// @Tags SubmissionAPI +// @Accept json +// @Produce json +// @Param request body ReviewSubmissionRequest true "request body" +// @Success 200 {object} ReviewSubmissionResponse +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" +// @Router /v1/presentations/submissions [get] +func (sr SubmissionRouter) ReviewSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + return framework.Respond(ctx, w, nil, http.StatusOK) +} diff --git a/pkg/server/server.go b/pkg/server/server.go index 752a6b75b..e406607f5 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -28,7 +28,7 @@ const ( CredentialsPrefix = "/credentials" PresentationsPrefix = "/presentations" DefinitionsPrefix = "/definitions" - SubmissionsPrefix = "/presentations/submissions" + SubmissionsPrefix = "/submissions" ManifestsPrefix = "/manifests" ApplicationsPrefix = "/applications" ResponsesPrefix = "/responses" @@ -178,8 +178,11 @@ func (s *SSIServer) SubmissionAPI(service svcframework.Service) (err error) { return util.LoggingErrorMsg(err, "could not create submission router") } - hand := V1Prefix + SubmissionsPrefix - s.Handle(http.MethodPut, hand, sRouter.CreateSubmission) + handlerPath := V1Prefix + PresentationsPrefix + SubmissionsPrefix + s.Handle(http.MethodPut, handlerPath, sRouter.CreateSubmission) + s.Handle(http.MethodGet, path.Join(handlerPath, "/:id"), sRouter.GetSubmission) + s.Handle(http.MethodGet, handlerPath, sRouter.ListSubmissions) + s.Handle(http.MethodPut, path.Join(handlerPath, "/:id", "/review"), sRouter.ReviewSubmission) return } From b485e806219cd980acbb25d4589328f2c2607de3 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Fri, 11 Nov 2022 09:35:55 -0500 Subject: [PATCH 05/27] Removed the delete method --- doc/swagger.yaml | 73 +++++++---------------- pkg/server/router/submission.go | 28 --------- pkg/server/router/submission_test.go | 28 --------- pkg/service/submission/service.go | 11 ---- pkg/service/submission/storage/bolt.go | 9 --- pkg/service/submission/storage/storage.go | 1 - 6 files changed, 22 insertions(+), 128 deletions(-) diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 795505f6e..8286bf698 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -1438,7 +1438,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetHealthCheckResponse' + $ref: '#/definitions/pkg_server_router.GetHealthCheckResponse' summary: Health Check tags: - HealthCheck @@ -1484,7 +1484,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetCredentialsResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialsResponse' "400": description: Bad request schema: @@ -1506,14 +1506,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreateCredentialRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateCredentialRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.CreateCredentialResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateCredentialResponse' "400": description: Bad request schema: @@ -1570,7 +1570,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetCredentialResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialResponse' "400": description: Bad request schema: @@ -1589,14 +1589,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.VerifyCredentialRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifyCredentialRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.VerifyCredentialResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifyCredentialResponse' "400": description: Bad request schema: @@ -1750,7 +1750,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.StoreKeyRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.StoreKeyRequest' produces: - application/json responses: @@ -1784,7 +1784,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetKeyDetailsResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetKeyDetailsResponse' "400": description: Bad request schema: @@ -1817,7 +1817,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetManifestsResponse' + $ref: '#/definitions/pkg_server_router.GetManifestsResponse' "400": description: Bad request schema: @@ -1839,14 +1839,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateManifestRequest' + $ref: '#/definitions/pkg_server_router.CreateManifestRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateManifestResponse' + $ref: '#/definitions/pkg_server_router.CreateManifestResponse' "400": description: Bad request schema: @@ -1903,7 +1903,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetManifestResponse' + $ref: '#/definitions/pkg_server_router.GetManifestResponse' "400": description: Bad request schema: @@ -1923,7 +1923,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetApplicationsResponse' + $ref: '#/definitions/pkg_server_router.GetApplicationsResponse' "400": description: Bad request schema: @@ -1946,14 +1946,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.SubmitApplicationRequest' + $ref: '#/definitions/pkg_server_router.SubmitApplicationRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.SubmitApplicationResponse' + $ref: '#/definitions/pkg_server_router.SubmitApplicationResponse' "400": description: Bad request schema: @@ -2010,7 +2010,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetApplicationResponse' + $ref: '#/definitions/pkg_server_router.GetApplicationResponse' "400": description: Bad request schema: @@ -2030,7 +2030,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetResponsesResponse' + $ref: '#/definitions/pkg_server_router.GetResponsesResponse' "400": description: Bad request schema: @@ -2087,7 +2087,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetResponseResponse' + $ref: '#/definitions/pkg_server_router.GetResponseResponse' "400": description: Bad request schema: @@ -2106,14 +2106,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreatePresentationDefinitionRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.CreatePresentationDefinitionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionResponse' "400": description: Bad request schema: @@ -2170,7 +2170,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetPresentationDefinitionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetPresentationDefinitionResponse' "400": description: Bad request schema: @@ -2264,35 +2264,6 @@ paths: summary: Create Submission tags: - SubmissionAPI - /v1/presentations/submissions/{id}: - delete: - consumes: - - application/json - description: Delete a submission by its ID - parameters: - - description: ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - type: string - "400": - description: Bad request - schema: - type: string - "500": - description: Internal server error - schema: - type: string - summary: Delete Submission - tags: - - SubmissionAPI /v1/schemas: get: consumes: diff --git a/pkg/server/router/submission.go b/pkg/server/router/submission.go index ac224b407..e3dad111f 100644 --- a/pkg/server/router/submission.go +++ b/pkg/server/router/submission.go @@ -126,34 +126,6 @@ func (sr SubmissionRouter) GetSubmission(ctx context.Context, w http.ResponseWri return framework.Respond(ctx, w, resp, http.StatusOK) } -// DeleteSubmission godoc -// @Summary Delete Submission -// @Description Delete a submission by its ID -// @Tags SubmissionAPI -// @Accept json -// @Produce json -// @Param id path string true "ID" -// @Success 200 {string} string "OK" -// @Failure 400 {string} string "Bad request" -// @Failure 500 {string} string "Internal server error" -// @Router /v1/presentations/submissions/{id} [delete] -func (sr SubmissionRouter) DeleteSubmission(ctx context.Context, w http.ResponseWriter, _ *http.Request) error { - id := framework.GetParam(ctx, IDParam) - if id == nil { - errMsg := "cannot delete a submission without an ID parameter" - logrus.Error(errMsg) - return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest) - } - - if err := sr.service.DeleteSubmission(submission.DeleteSubmissionRequest{ID: *id}); err != nil { - errMsg := fmt.Sprintf("could not delete submission with id: %s", *id) - logrus.WithError(err).Error(errMsg) - return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError) - } - - return framework.Respond(ctx, w, nil, http.StatusOK) -} - type ListSubmissionRequest struct { Filter string `json:"filter"` } diff --git a/pkg/server/router/submission_test.go b/pkg/server/router/submission_test.go index 170f8d171..5882915f1 100644 --- a/pkg/server/router/submission_test.go +++ b/pkg/server/router/submission_test.go @@ -86,34 +86,6 @@ func TestSubmissionRouter_GetSubmission(t *testing.T) { }) } -func TestSubmissionRouter_DeleteSubmission(t *testing.T) { - router := createRouter(t) - t.Run("error when id is not present", func(t *testing.T) { - r := httptest.NewRequest(http.MethodDelete, "http://localhost/v1/presentations/submissions", strings.NewReader("")) - w := httptest.NewRecorder() - assert.Error(t, router.DeleteSubmission(newRequestContextWithParams(map[string]string{"id": ""}), w, r)) - }) - - t.Run("get returns empty after deletion", func(t *testing.T) { - t.Cleanup(func() { - _ = os.Remove(storage.DBFile) - }) - jsonRequestString := `{"presentationJwt":"hello"}` - r := httptest.NewRequest(http.MethodPut, "http://localhost/v1/presentations/submissions", strings.NewReader(jsonRequestString)) - w := httptest.NewRecorder() - assert.NoError(t, router.CreateSubmission(newRequestContext(), w, r)) - r = httptest.NewRequest(http.MethodGet, "http://localhost/v1/presentations/submissions", strings.NewReader("")) - assert.NoError(t, router.GetSubmission(newRequestContextWithParams(map[string]string{"id": "dummy value"}), w, r)) - - r = httptest.NewRequest(http.MethodDelete, "http://localhost/v1/presentations/submissions", strings.NewReader("")) - assert.NoError(t, router.DeleteSubmission(newRequestContextWithParams(map[string]string{"id": "dummy value"}), w, r)) - - r = httptest.NewRequest(http.MethodGet, "http://localhost/v1/presentations/submissions", strings.NewReader("")) - w = httptest.NewRecorder() - assert.Error(t, router.GetSubmission(newRequestContextWithParams(map[string]string{"id": "dummy value"}), w, r)) - }) -} - // construct a context value as expected by our handler func newRequestContext() context.Context { return context.WithValue(context.Background(), framework.KeyRequestState, &framework.RequestState{ diff --git a/pkg/service/submission/service.go b/pkg/service/submission/service.go index 6bcfa938a..d0c8869cd 100644 --- a/pkg/service/submission/service.go +++ b/pkg/service/submission/service.go @@ -96,14 +96,3 @@ func (s Service) GetSubmission(request GetSubmissionRequest) (*GetSubmissionResp } return &GetSubmissionResponse{Submission: storedSubmission.Submission}, nil } - -func (s Service) DeleteSubmission(request DeleteSubmissionRequest) error { - logrus.Debugf("deleting presentation submission: %s", request.ID) - - if err := s.storage.DeleteSubmission(request.ID); err != nil { - errMsg := fmt.Sprintf("could not delete presentation submission with id: %s", request.ID) - return util.LoggingErrorMsg(err, errMsg) - } - - return nil -} diff --git a/pkg/service/submission/storage/bolt.go b/pkg/service/submission/storage/bolt.go index 00915dbda..ccd39e044 100644 --- a/pkg/service/submission/storage/bolt.go +++ b/pkg/service/submission/storage/bolt.go @@ -61,12 +61,3 @@ func (b BoltSubmissionStorage) GetSubmission(id string) (*StoredSubmission, erro } return &stored, nil } - -func (b BoltSubmissionStorage) DeleteSubmission(id string) error { - if err := b.db.Delete(namespace, id); err != nil { - errMsg := fmt.Sprintf("could not delete submission definition: %s", id) - logrus.WithError(err).Error(errMsg) - return errors.Wrapf(err, errMsg) - } - return nil -} diff --git a/pkg/service/submission/storage/storage.go b/pkg/service/submission/storage/storage.go index fdebac4a4..239aa21f3 100644 --- a/pkg/service/submission/storage/storage.go +++ b/pkg/service/submission/storage/storage.go @@ -14,7 +14,6 @@ type StoredSubmission struct { type Storage interface { StoreSubmission(schema StoredSubmission) error GetSubmission(id string) (*StoredSubmission, error) - DeleteSubmission(id string) error } // NewSubmissionStorage finds the submission storage impl for a given ServiceStorage value From 9ed52315afee8cd3a1d77a92d59f222aafe36f93 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Fri, 11 Nov 2022 10:03:54 -0500 Subject: [PATCH 06/27] Simplified for scaffolding --- pkg/server/router/submission.go | 77 ++++--------------- pkg/server/router/submission_test.go | 106 --------------------------- pkg/service/submission/model.go | 4 - 3 files changed, 14 insertions(+), 173 deletions(-) delete mode 100644 pkg/server/router/submission_test.go diff --git a/pkg/server/router/submission.go b/pkg/server/router/submission.go index e3dad111f..333b7bc5d 100644 --- a/pkg/server/router/submission.go +++ b/pkg/server/router/submission.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/TBD54566975/ssi-sdk/credential/exchange" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/tbd54566975/ssi-service/internal/keyaccess" "github.com/tbd54566975/ssi-service/pkg/server/framework" svcframework "github.com/tbd54566975/ssi-service/pkg/service/framework" @@ -32,10 +31,15 @@ type CreateSubmissionRequest struct { PresentationJwt keyaccess.JWT `json:"presentationJwt" validate:"required"` } -type CreateSubmissionResponse struct { - // TODO(andres): return an operation here. - Status string `json:"status"` - Submission exchange.PresentationSubmission `json:"submission"` +type Operation struct { + ID string `json:"id"` + Done bool `json:"bool"` + Result OperationResult `json:"result"` +} + +type OperationResult struct { + Error string `json:"error"` + Response exchange.PresentationSubmission `json:"response"` } // CreateSubmission godoc @@ -45,48 +49,12 @@ type CreateSubmissionResponse struct { // @Accept json // @Produce json // @Param request body CreateSubmissionRequest true "request body" -// @Success 201 {object} CreateSubmissionResponse +// @Success 201 {object} Operation // @Failure 400 {string} string "Bad request" // @Failure 500 {string} string "Internal server error" // @Router /v1/presentations/submissions [put] func (sr SubmissionRouter) CreateSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - var request CreateSubmissionRequest - errMsg := "Invalid create submission request" - if err := framework.Decode(r, &request); err != nil { - logrus.WithError(err).Error(errMsg) - return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) - } - - if err := framework.ValidateRequest(request); err != nil { - logrus.WithError(err).Error(errMsg) - return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) - } - - // TODO: convert from request.PresentationJwt - s := exchange.PresentationSubmission{ - ID: "dummy value", - DefinitionID: "another dummy", - DescriptorMap: []exchange.SubmissionDescriptor{ - { - ID: "what?", - Format: "jwt_vp", - Path: "ohhh yeah", - PathNested: nil, - }, - }, - } - - req := submission.CreateSubmissionRequest{ - Submission: s, - } - sub, err := sr.service.CreateSubmission(req) - if err != nil { - errMsg := fmt.Sprintf("could not create submission definition") - logrus.WithError(err).Error(errMsg) - return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError) - } - - resp := CreateSubmissionResponse{Status: "pending", Submission: sub.Submission} + resp := Operation{} return framework.Respond(ctx, w, resp, http.StatusCreated) } @@ -105,24 +73,7 @@ type GetSubmissionResponse struct { // @Failure 400 {string} string "Bad request" // @Router /v1/presentations/submission/{id} [get] func (sr SubmissionRouter) GetSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - id := framework.GetParam(ctx, IDParam) - if id == nil { - errMsg := "cannot get submission without ID parameter" - logrus.Error(errMsg) - return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest) - } - - def, err := sr.service.GetSubmission(submission.GetSubmissionRequest{ID: *id}) - if err != nil { - errMsg := fmt.Sprintf("could not get submission with id: %s", *id) - logrus.WithError(err).Error(errMsg) - return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) - } - // TODO(andres): introduce not found errors that can be mapped to 404. - - resp := GetSubmissionResponse{ - Submission: def.Submission, - } + resp := GetSubmissionResponse{} return framework.Respond(ctx, w, resp, http.StatusOK) } @@ -146,7 +97,7 @@ type ListSubmissionResponse struct { // @Failure 500 {string} string "Internal server error" // @Router /v1/presentations/submissions [get] func (sr SubmissionRouter) ListSubmissions(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - return framework.Respond(ctx, w, nil, http.StatusOK) + return framework.Respond(ctx, w, ListSubmissionRequest{}, http.StatusOK) } type ReviewSubmissionRequest struct { @@ -170,5 +121,5 @@ type ReviewSubmissionResponse struct { // @Failure 500 {string} string "Internal server error" // @Router /v1/presentations/submissions [get] func (sr SubmissionRouter) ReviewSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - return framework.Respond(ctx, w, nil, http.StatusOK) + return framework.Respond(ctx, w, ReviewSubmissionResponse{}, http.StatusOK) } diff --git a/pkg/server/router/submission_test.go b/pkg/server/router/submission_test.go deleted file mode 100644 index 5882915f1..000000000 --- a/pkg/server/router/submission_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package router - -import ( - "context" - "fmt" - "github.com/dimfeld/httptreemux/v5" - "github.com/google/uuid" - "github.com/stretchr/testify/assert" - "github.com/tbd54566975/ssi-service/config" - "github.com/tbd54566975/ssi-service/pkg/server/framework" - "github.com/tbd54566975/ssi-service/pkg/service/submission" - "github.com/tbd54566975/ssi-service/pkg/storage" - "io" - "net/http" - "net/http/httptest" - "os" - "strings" - "testing" - "time" -) - -func TestSubmissionRouter_CreateSubmission(t *testing.T) { - router := createRouter(t) - - t.Run("simple jwt return submission object", func(t *testing.T) { - t.Cleanup(func() { - _ = os.Remove(storage.DBFile) - }) - - jsonRequestString := `{"presentationJwt":"hello"}` - r := httptest.NewRequest(http.MethodPut, "http://localhost/v1/presentations/submissions", strings.NewReader(jsonRequestString)) - w := httptest.NewRecorder() - - assert.NoError(t, router.CreateSubmission(newRequestContext(), w, r)) - - readBody, err := io.ReadAll(w.Body) - assert.NoError(t, err) - assert.Equal(t, `{"status":"pending","submission":{"id":"dummy value","definition_id":"another dummy","descriptor_map":[{"id":"what?","format":"jwt_vp","path":"ohhh yeah"}]}}`, fmt.Sprintf("%s", readBody)) - }) - - t.Run("missing jwt returns error", func(t *testing.T) { - jsonRequestString := `{}` - r := httptest.NewRequest(http.MethodPut, "http://localhost/v1/presentations/submissions", strings.NewReader(jsonRequestString)) - w := httptest.NewRecorder() - - assert.Error(t, router.CreateSubmission(newRequestContext(), w, r)) - }) -} - -func createRouter(t *testing.T) *SubmissionRouter { - svcStorage, err := storage.NewStorage(storage.Bolt) - assert.NoError(t, err) - submissionService, err := submission.NewSubmissionService(config.SubmissionServiceConfig{}, svcStorage) - assert.NoError(t, err) - router, err := NewSubmissionRouter(submissionService) - assert.NoError(t, err) - return router -} - -func TestSubmissionRouter_GetSubmission(t *testing.T) { - router := createRouter(t) - t.Run("get returns error when not found", func(t *testing.T) { - jsonRequestString := `{"presentationJwt":"hello"}` - r := httptest.NewRequest(http.MethodPut, "http://localhost/v1/presentations/submissions", strings.NewReader(jsonRequestString)) - w := httptest.NewRecorder() - - assert.Error(t, router.GetSubmission(newRequestContextWithParams(map[string]string{"id": "dummy value"}), w, r)) - }) - - t.Run("returns resource after creating it", func(t *testing.T) { - t.Cleanup(func() { - _ = os.Remove(storage.DBFile) - }) - jsonRequestString := `{"presentationJwt":"hello"}` - r := httptest.NewRequest(http.MethodPut, "http://localhost/v1/presentations/submissions", strings.NewReader(jsonRequestString)) - w := httptest.NewRecorder() - assert.NoError(t, router.CreateSubmission(newRequestContext(), w, r)) - - r = httptest.NewRequest(http.MethodGet, "http://localhost/v1/presentations/submissions", strings.NewReader("")) - w = httptest.NewRecorder() - assert.NoError(t, router.GetSubmission(newRequestContextWithParams(map[string]string{"id": "dummy value"}), w, r)) - - readBody, err := io.ReadAll(w.Body) - assert.NoError(t, err) - assert.Equal(t, `{"submission":{"id":"dummy value","definition_id":"another dummy","descriptor_map":[{"id":"what?","format":"jwt_vp","path":"ohhh yeah"}]}}`, fmt.Sprintf("%s", readBody)) - }) -} - -// construct a context value as expected by our handler -func newRequestContext() context.Context { - return context.WithValue(context.Background(), framework.KeyRequestState, &framework.RequestState{ - TraceID: uuid.New().String(), - Now: time.Now(), - StatusCode: 1, - }) -} - -// as required by https://github.com/dimfeld/httptreemux's context handler -func newRequestContextWithParams(params map[string]string) context.Context { - ctx := context.WithValue(context.Background(), framework.KeyRequestState, &framework.RequestState{ - TraceID: uuid.New().String(), - Now: time.Now(), - StatusCode: 1, - }) - return httptreemux.AddParamsToContext(ctx, params) -} diff --git a/pkg/service/submission/model.go b/pkg/service/submission/model.go index 025dd5bdd..e515734d5 100644 --- a/pkg/service/submission/model.go +++ b/pkg/service/submission/model.go @@ -5,10 +5,6 @@ import ( "github.com/TBD54566975/ssi-sdk/util" ) -const ( - Version1 string = "1.0" -) - type CreateSubmissionRequest struct { Submission exchange.PresentationSubmission `json:"submission" validate:"required"` } From 8a0a1e57b4efa2bc8237aa3a71990be5f98f1d3b Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Fri, 11 Nov 2022 10:05:15 -0500 Subject: [PATCH 07/27] spec --- doc/swagger.yaml | 86 ++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 8286bf698..8e78f2da7 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -604,14 +604,6 @@ definitions: required: - presentationJwt type: object - github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionResponse: - properties: - status: - description: 'TODO(andres): return an operation here.' - type: string - submission: - $ref: '#/definitions/exchange.PresentationSubmission' - type: object github.com_tbd54566975_ssi-service_pkg_server_router.GetApplicationResponse: properties: application: @@ -743,6 +735,22 @@ definitions: $ref: '#/definitions/exchange.PresentationSubmission' type: array type: object + github.com_tbd54566975_ssi-service_pkg_server_router.Operation: + properties: + bool: + type: boolean + id: + type: string + result: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.OperationResult' + type: object + github.com_tbd54566975_ssi-service_pkg_server_router.OperationResult: + properties: + error: + type: string + response: + $ref: '#/definitions/exchange.PresentationSubmission' + type: object github.com_tbd54566975_ssi-service_pkg_server_router.ResolveDIDResponse: properties: didDocument: @@ -1078,14 +1086,6 @@ definitions: required: - presentationJwt type: object - pkg_server_router.CreateSubmissionResponse: - properties: - status: - description: 'TODO(andres): return an operation here.' - type: string - submission: - $ref: '#/definitions/exchange.PresentationSubmission' - type: object pkg_server_router.GetApplicationResponse: properties: application: @@ -1217,6 +1217,22 @@ definitions: $ref: '#/definitions/exchange.PresentationSubmission' type: array type: object + pkg_server_router.Operation: + properties: + bool: + type: boolean + id: + type: string + result: + $ref: '#/definitions/pkg_server_router.OperationResult' + type: object + pkg_server_router.OperationResult: + properties: + error: + type: string + response: + $ref: '#/definitions/exchange.PresentationSubmission' + type: object pkg_server_router.ResolveDIDResponse: properties: didDocument: @@ -1438,7 +1454,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetHealthCheckResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetHealthCheckResponse' summary: Health Check tags: - HealthCheck @@ -1615,7 +1631,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetDIDMethodsResponse' + $ref: '#/definitions/pkg_server_router.GetDIDMethodsResponse' summary: Get DID Methods tags: - DecentralizedIdentityAPI @@ -1636,7 +1652,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetDIDsByMethodResponse' + $ref: '#/definitions/pkg_server_router.GetDIDsByMethodResponse' "400": description: Bad request schema: @@ -1654,7 +1670,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateDIDByMethodRequest' + $ref: '#/definitions/pkg_server_router.CreateDIDByMethodRequest' - description: Method in: path name: method @@ -1666,7 +1682,7 @@ paths: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateDIDByMethodResponse' + $ref: '#/definitions/pkg_server_router.CreateDIDByMethodResponse' "400": description: Bad request schema: @@ -1689,7 +1705,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateDIDByMethodRequest' + $ref: '#/definitions/pkg_server_router.CreateDIDByMethodRequest' - description: Method in: path name: method @@ -1706,7 +1722,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetDIDByMethodResponse' + $ref: '#/definitions/pkg_server_router.GetDIDByMethodResponse' "400": description: Bad request schema: @@ -1731,7 +1747,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ResolveDIDResponse' + $ref: '#/definitions/pkg_server_router.ResolveDIDResponse' "400": description: Bad request schema: @@ -2195,7 +2211,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSubmissionResponse' + $ref: '#/definitions/pkg_server_router.GetSubmissionResponse' "400": description: Bad request schema: @@ -2216,14 +2232,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionRequest' + $ref: '#/definitions/pkg_server_router.ReviewSubmissionRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionResponse' + $ref: '#/definitions/pkg_server_router.ReviewSubmissionResponse' "400": description: Bad request schema: @@ -2245,14 +2261,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionRequest' + $ref: '#/definitions/pkg_server_router.CreateSubmissionRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionResponse' + $ref: '#/definitions/pkg_server_router.Operation' "400": description: Bad request schema: @@ -2275,7 +2291,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetSchemasResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemasResponse' "500": description: Internal server error schema: @@ -2293,14 +2309,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreateSchemaRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.CreateSchemaResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaResponse' "400": description: Bad request schema: @@ -2357,7 +2373,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetSchemaResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemaResponse' "400": description: Bad request schema: @@ -2376,14 +2392,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.VerifySchemaRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.VerifySchemaResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaResponse' "400": description: Bad request schema: From 8ddd3c51a07406178fd27ab5f023d62b5eb3d299 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 14 Nov 2022 14:00:07 -0500 Subject: [PATCH 08/27] zero structs --- pkg/server/router/submission.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/server/router/submission.go b/pkg/server/router/submission.go index 333b7bc5d..c47286841 100644 --- a/pkg/server/router/submission.go +++ b/pkg/server/router/submission.go @@ -54,7 +54,7 @@ type OperationResult struct { // @Failure 500 {string} string "Internal server error" // @Router /v1/presentations/submissions [put] func (sr SubmissionRouter) CreateSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - resp := Operation{} + var resp Operation return framework.Respond(ctx, w, resp, http.StatusCreated) } @@ -73,7 +73,7 @@ type GetSubmissionResponse struct { // @Failure 400 {string} string "Bad request" // @Router /v1/presentations/submission/{id} [get] func (sr SubmissionRouter) GetSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - resp := GetSubmissionResponse{} + var resp GetSubmissionResponse return framework.Respond(ctx, w, resp, http.StatusOK) } From 8b0732b0ae05408b6d8f89754dc9d8a92907ff2b Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 14 Nov 2022 14:28:47 -0500 Subject: [PATCH 09/27] Consolidate under the same API --- pkg/server/router/presentation.go | 116 ++++++++++++++++-- pkg/server/router/presentation_test.go | 2 +- pkg/server/router/submission.go | 125 -------------------- pkg/server/server.go | 19 +-- pkg/server/server_presentation_test.go | 2 +- pkg/service/framework/framework.go | 1 - pkg/service/presentation/model.go | 24 ++++ pkg/service/presentation/service.go | 43 ++++++- pkg/service/presentation/storage/bolt.go | 40 ++++++- pkg/service/presentation/storage/storage.go | 14 +++ pkg/service/service.go | 16 +-- pkg/service/submission/model.go | 30 ----- pkg/service/submission/service.go | 98 --------------- pkg/service/submission/storage/bolt.go | 63 ---------- pkg/service/submission/storage/storage.go | 37 ------ 15 files changed, 235 insertions(+), 395 deletions(-) delete mode 100644 pkg/server/router/submission.go delete mode 100644 pkg/service/submission/model.go delete mode 100644 pkg/service/submission/service.go delete mode 100644 pkg/service/submission/storage/bolt.go delete mode 100644 pkg/service/submission/storage/storage.go diff --git a/pkg/server/router/presentation.go b/pkg/server/router/presentation.go index ac87b6c81..7bdedd28f 100644 --- a/pkg/server/router/presentation.go +++ b/pkg/server/router/presentation.go @@ -6,17 +6,18 @@ import ( "github.com/TBD54566975/ssi-sdk/credential/exchange" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "github.com/tbd54566975/ssi-service/internal/keyaccess" "github.com/tbd54566975/ssi-service/pkg/server/framework" svcframework "github.com/tbd54566975/ssi-service/pkg/service/framework" "github.com/tbd54566975/ssi-service/pkg/service/presentation" "net/http" ) -type PresentationDefinitionRouter struct { +type PresentationRouter struct { service *presentation.Service } -func NewPresentationDefinitionRouter(s svcframework.Service) (*PresentationDefinitionRouter, error) { +func NewPresentationDefinitionRouter(s svcframework.Service) (*PresentationRouter, error) { if s == nil { return nil, errors.New("service cannot be nil") } @@ -24,7 +25,7 @@ func NewPresentationDefinitionRouter(s svcframework.Service) (*PresentationDefin if !ok { return nil, fmt.Errorf("could not create presentation router with service type: %s", s.Type()) } - return &PresentationDefinitionRouter{service: service}, nil + return &PresentationRouter{service: service}, nil } type CreatePresentationDefinitionRequest struct { @@ -50,7 +51,7 @@ type CreatePresentationDefinitionResponse struct { // @Failure 400 {string} string "Bad request" // @Failure 500 {string} string "Internal server error" // @Router /v1/presentation/definition [put] -func (pdr PresentationDefinitionRouter) CreatePresentationDefinition(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +func (pr PresentationRouter) CreatePresentationDefinition(ctx context.Context, w http.ResponseWriter, r *http.Request) error { var request CreatePresentationDefinitionRequest errMsg := "Invalid Presentation Definition Request" if err := framework.Decode(r, &request); err != nil { @@ -68,7 +69,7 @@ func (pdr PresentationDefinitionRouter) CreatePresentationDefinition(ctx context logrus.WithError(err).Error(errMsg) return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest) } - serviceResp, err := pdr.service.CreatePresentationDefinition(presentation.CreatePresentationDefinitionRequest{PresentationDefinition: *def}) + serviceResp, err := pr.service.CreatePresentationDefinition(presentation.CreatePresentationDefinitionRequest{PresentationDefinition: *def}) if err != nil { logrus.WithError(err).Error(errMsg) return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError) @@ -125,7 +126,7 @@ type GetPresentationDefinitionResponse struct { // @Success 200 {object} GetPresentationDefinitionResponse // @Failure 400 {string} string "Bad request" // @Router /v1/presentation/definition/{id} [get] -func (pdr PresentationDefinitionRouter) GetPresentationDefinition(ctx context.Context, w http.ResponseWriter, r *http.Request) error { +func (pr PresentationRouter) GetPresentationDefinition(ctx context.Context, w http.ResponseWriter, r *http.Request) error { id := framework.GetParam(ctx, IDParam) if id == nil { errMsg := "cannot get presentation without ID parameter" @@ -133,7 +134,7 @@ func (pdr PresentationDefinitionRouter) GetPresentationDefinition(ctx context.Co return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest) } - def, err := pdr.service.GetPresentationDefinition(presentation.GetPresentationDefinitionRequest{ID: *id}) + def, err := pr.service.GetPresentationDefinition(presentation.GetPresentationDefinitionRequest{ID: *id}) if err != nil { errMsg := fmt.Sprintf("could not get presentation with id: %s", *id) logrus.WithError(err).Error(errMsg) @@ -157,7 +158,7 @@ func (pdr PresentationDefinitionRouter) GetPresentationDefinition(ctx context.Co // @Failure 400 {string} string "Bad request" // @Failure 500 {string} string "Internal server error" // @Router /v1/presentation/definition/{id} [delete] -func (pdr PresentationDefinitionRouter) DeletePresentationDefinition(ctx context.Context, w http.ResponseWriter, _ *http.Request) error { +func (pr PresentationRouter) DeletePresentationDefinition(ctx context.Context, w http.ResponseWriter, _ *http.Request) error { id := framework.GetParam(ctx, IDParam) if id == nil { errMsg := "cannot delete a presentation without an ID parameter" @@ -165,7 +166,7 @@ func (pdr PresentationDefinitionRouter) DeletePresentationDefinition(ctx context return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest) } - if err := pdr.service.DeletePresentationDefinition(presentation.DeletePresentationDefinitionRequest{ID: *id}); err != nil { + if err := pr.service.DeletePresentationDefinition(presentation.DeletePresentationDefinitionRequest{ID: *id}); err != nil { errMsg := fmt.Sprintf("could not delete presentation with id: %s", *id) logrus.WithError(err).Error(errMsg) return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError) @@ -173,3 +174,100 @@ func (pdr PresentationDefinitionRouter) DeletePresentationDefinition(ctx context return framework.Respond(ctx, w, nil, http.StatusOK) } + +type CreateSubmissionRequest struct { + PresentationJwt keyaccess.JWT `json:"presentationJwt" validate:"required"` +} + +type Operation struct { + ID string `json:"id"` + Done bool `json:"bool"` + Result OperationResult `json:"result"` +} + +type OperationResult struct { + Error string `json:"error"` + Response exchange.PresentationSubmission `json:"response"` +} + +// CreateSubmission godoc +// @Summary Create Submission +// @Description Creates a submission in this server ready to be reviewed. +// @Tags SubmissionAPI +// @Accept json +// @Produce json +// @Param request body CreateSubmissionRequest true "request body" +// @Success 201 {object} Operation +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" +// @Router /v1/presentations/submissions [put] +func (pr PresentationRouter) CreateSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + var resp Operation + return framework.Respond(ctx, w, resp, http.StatusCreated) +} + +type GetSubmissionResponse struct { + Submission exchange.PresentationSubmission `json:"submission"` +} + +// GetSubmission godoc +// @Summary Get Submission +// @Description Get a submission by its ID +// @Tags SubmissionAPI +// @Accept json +// @Produce json +// @Param id path string true "ID" +// @Success 200 {object} GetSubmissionResponse +// @Failure 400 {string} string "Bad request" +// @Router /v1/presentations/submission/{id} [get] +func (pr PresentationRouter) GetSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + var resp GetSubmissionResponse + return framework.Respond(ctx, w, resp, http.StatusOK) +} + +type ListSubmissionRequest struct { + Filter string `json:"filter"` +} + +type ListSubmissionResponse struct { + Submissions []exchange.PresentationSubmission `json:"submissions"` +} + +// ListSubmissions godoc +// @Summary List Submissions +// @Description List existing submissions according to a filtering query. +// @Tags SubmissionAPI +// @Accept json +// @Produce json +// @Param request body ListSubmissionRequest true "request body" +// @Success 200 {object} ListSubmissionResponse +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" +// @Router /v1/presentations/submissions [get] +func (pr PresentationRouter) ListSubmissions(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + return framework.Respond(ctx, w, ListSubmissionRequest{}, http.StatusOK) +} + +type ReviewSubmissionRequest struct { + Approved bool `json:"approved" validate:"required"` + Reason string `json:"reason"` +} + +type ReviewSubmissionResponse struct { + Submission exchange.PresentationSubmission `json:"submission"` +} + +// ReviewSubmission godoc +// @Summary Review a pending submissions +// @Description Reviews a pending submission. After this method is called, the operation with `id==presentations/submissions/{submission_id}` will be updated with the result of this invocation. +// @Tags SubmissionAPI +// @Accept json +// @Produce json +// @Param request body ReviewSubmissionRequest true "request body" +// @Success 200 {object} ReviewSubmissionResponse +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" +// @Router /v1/presentations/submissions [get] +func (pr PresentationRouter) ReviewSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + return framework.Respond(ctx, w, ReviewSubmissionResponse{}, http.StatusOK) +} diff --git a/pkg/server/router/presentation_test.go b/pkg/server/router/presentation_test.go index c9dde9201..ba00abba1 100644 --- a/pkg/server/router/presentation_test.go +++ b/pkg/server/router/presentation_test.go @@ -35,7 +35,7 @@ func TestPresentationDefinitionService(t *testing.T) { s, err := storage.NewStorage(storage.Bolt) assert.NoError(t, err) - service, err := presentation.NewPresentationDefinitionService(config.PresentationServiceConfig{}, s) + service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s) assert.NoError(t, err) t.Run("Create returns the created definition", func(t *testing.T) { diff --git a/pkg/server/router/submission.go b/pkg/server/router/submission.go deleted file mode 100644 index c47286841..000000000 --- a/pkg/server/router/submission.go +++ /dev/null @@ -1,125 +0,0 @@ -package router - -import ( - "context" - "fmt" - "github.com/TBD54566975/ssi-sdk/credential/exchange" - "github.com/pkg/errors" - "github.com/tbd54566975/ssi-service/internal/keyaccess" - "github.com/tbd54566975/ssi-service/pkg/server/framework" - svcframework "github.com/tbd54566975/ssi-service/pkg/service/framework" - "github.com/tbd54566975/ssi-service/pkg/service/submission" - "net/http" -) - -type SubmissionRouter struct { - service *submission.Service -} - -func NewSubmissionRouter(s svcframework.Service) (*SubmissionRouter, error) { - if s == nil { - return nil, errors.New("service cannot be nil") - } - service, ok := s.(*submission.Service) - if !ok { - return nil, fmt.Errorf("could not create submission router with service type: %s", s.Type()) - } - return &SubmissionRouter{service: service}, nil -} - -type CreateSubmissionRequest struct { - PresentationJwt keyaccess.JWT `json:"presentationJwt" validate:"required"` -} - -type Operation struct { - ID string `json:"id"` - Done bool `json:"bool"` - Result OperationResult `json:"result"` -} - -type OperationResult struct { - Error string `json:"error"` - Response exchange.PresentationSubmission `json:"response"` -} - -// CreateSubmission godoc -// @Summary Create Submission -// @Description Creates a submission in this server ready to be reviewed. -// @Tags SubmissionAPI -// @Accept json -// @Produce json -// @Param request body CreateSubmissionRequest true "request body" -// @Success 201 {object} Operation -// @Failure 400 {string} string "Bad request" -// @Failure 500 {string} string "Internal server error" -// @Router /v1/presentations/submissions [put] -func (sr SubmissionRouter) CreateSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - var resp Operation - return framework.Respond(ctx, w, resp, http.StatusCreated) -} - -type GetSubmissionResponse struct { - Submission exchange.PresentationSubmission `json:"submission"` -} - -// GetSubmission godoc -// @Summary Get Submission -// @Description Get a submission by its ID -// @Tags SubmissionAPI -// @Accept json -// @Produce json -// @Param id path string true "ID" -// @Success 200 {object} GetSubmissionResponse -// @Failure 400 {string} string "Bad request" -// @Router /v1/presentations/submission/{id} [get] -func (sr SubmissionRouter) GetSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - var resp GetSubmissionResponse - return framework.Respond(ctx, w, resp, http.StatusOK) -} - -type ListSubmissionRequest struct { - Filter string `json:"filter"` -} - -type ListSubmissionResponse struct { - Submissions []exchange.PresentationSubmission `json:"submissions"` -} - -// ListSubmissions godoc -// @Summary List Submissions -// @Description List existing submissions according to a filtering query. -// @Tags SubmissionAPI -// @Accept json -// @Produce json -// @Param request body ListSubmissionRequest true "request body" -// @Success 200 {object} ListSubmissionResponse -// @Failure 400 {string} string "Bad request" -// @Failure 500 {string} string "Internal server error" -// @Router /v1/presentations/submissions [get] -func (sr SubmissionRouter) ListSubmissions(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - return framework.Respond(ctx, w, ListSubmissionRequest{}, http.StatusOK) -} - -type ReviewSubmissionRequest struct { - Approved bool `json:"approved" validate:"required"` - Reason string `json:"reason"` -} - -type ReviewSubmissionResponse struct { - Submission exchange.PresentationSubmission `json:"submission"` -} - -// ReviewSubmission godoc -// @Summary Review a pending submissions -// @Description Reviews a pending submission. After this method is called, the operation with `id==presentations/submissions/{submission_id}` will be updated with the result of this invocation. -// @Tags SubmissionAPI -// @Accept json -// @Produce json -// @Param request body ReviewSubmissionRequest true "request body" -// @Success 200 {object} ReviewSubmissionResponse -// @Failure 400 {string} string "Bad request" -// @Failure 500 {string} string "Internal server error" -// @Router /v1/presentations/submissions [get] -func (sr SubmissionRouter) ReviewSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - return framework.Respond(ctx, w, ReviewSubmissionResponse{}, http.StatusOK) -} diff --git a/pkg/server/server.go b/pkg/server/server.go index e406607f5..bad65822b 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -102,8 +102,6 @@ func (s *SSIServer) instantiateRouter(service svcframework.Service) error { return s.ManifestAPI(service) case svcframework.Presentation: return s.PresentationAPI(service) - case svcframework.Submission: - return s.SubmissionAPI(service) default: return fmt.Errorf("could not instantiate API for service: %s", serviceType) } @@ -170,19 +168,12 @@ func (s *SSIServer) PresentationAPI(service svcframework.Service) (err error) { s.Handle(http.MethodGet, path.Join(handlerPath, "/:id"), pRouter.GetPresentationDefinition) s.Handle(http.MethodDelete, path.Join(handlerPath, "/:id"), pRouter.DeletePresentationDefinition) - return -} -func (s *SSIServer) SubmissionAPI(service svcframework.Service) (err error) { - sRouter, err := router.NewSubmissionRouter(service) - if err != nil { - return util.LoggingErrorMsg(err, "could not create submission router") - } + submissionHandlerPath := V1Prefix + PresentationsPrefix + SubmissionsPrefix - handlerPath := V1Prefix + PresentationsPrefix + SubmissionsPrefix - s.Handle(http.MethodPut, handlerPath, sRouter.CreateSubmission) - s.Handle(http.MethodGet, path.Join(handlerPath, "/:id"), sRouter.GetSubmission) - s.Handle(http.MethodGet, handlerPath, sRouter.ListSubmissions) - s.Handle(http.MethodPut, path.Join(handlerPath, "/:id", "/review"), sRouter.ReviewSubmission) + s.Handle(http.MethodPut, submissionHandlerPath, pRouter.CreateSubmission) + s.Handle(http.MethodGet, path.Join(submissionHandlerPath, "/:id"), pRouter.GetSubmission) + s.Handle(http.MethodGet, submissionHandlerPath, pRouter.ListSubmissions) + s.Handle(http.MethodPut, path.Join(submissionHandlerPath, "/:id", "/review"), pRouter.ReviewSubmission) return } diff --git a/pkg/server/server_presentation_test.go b/pkg/server/server_presentation_test.go index 09f59b883..7df7886d7 100644 --- a/pkg/server/server_presentation_test.go +++ b/pkg/server/server_presentation_test.go @@ -40,7 +40,7 @@ func TestPresentationAPI(t *testing.T) { s, err := storage.NewStorage(storage.Bolt) assert.NoError(t, err) - service, err := presentation.NewPresentationDefinitionService(config.PresentationServiceConfig{}, s) + service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s) assert.NoError(t, err) pRouter, err := router.NewPresentationDefinitionRouter(service) diff --git a/pkg/service/framework/framework.go b/pkg/service/framework/framework.go index 926491ad7..d357e3ebb 100644 --- a/pkg/service/framework/framework.go +++ b/pkg/service/framework/framework.go @@ -14,7 +14,6 @@ const ( KeyStore Type = "keystore" Manifest Type = "manifest" Presentation Type = "presentation" - Submission Type = "submission" StatusReady StatusState = "ready" StatusNotReady StatusState = "not_ready" diff --git a/pkg/service/presentation/model.go b/pkg/service/presentation/model.go index cfad0ce06..784effa94 100644 --- a/pkg/service/presentation/model.go +++ b/pkg/service/presentation/model.go @@ -29,3 +29,27 @@ type GetPresentationDefinitionResponse struct { type DeletePresentationDefinitionRequest struct { ID string `json:"id" validate:"required"` } + +type CreateSubmissionRequest struct { + Submission exchange.PresentationSubmission `json:"submission" validate:"required"` +} + +func (csr CreateSubmissionRequest) IsValid() bool { + return util.IsValidStruct(csr) == nil +} + +type CreateSubmissionResponse struct { + Submission exchange.PresentationSubmission `json:"submission"` +} + +type GetSubmissionRequest struct { + ID string `json:"id" validate:"required"` +} + +type GetSubmissionResponse struct { + Submission exchange.PresentationSubmission `json:"submission"` +} + +type DeleteSubmissionRequest struct { + ID string `json:"id" validate:"required"` +} diff --git a/pkg/service/presentation/service.go b/pkg/service/presentation/service.go index c98b6cba9..8e43fed65 100644 --- a/pkg/service/presentation/service.go +++ b/pkg/service/presentation/service.go @@ -6,7 +6,6 @@ import ( sdkutil "github.com/TBD54566975/ssi-sdk/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/tbd54566975/ssi-service/config" "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/service/framework" @@ -41,7 +40,7 @@ func (s Service) Config() config.PresentationServiceConfig { return s.config } -func NewPresentationDefinitionService(config config.PresentationServiceConfig, s storage.ServiceStorage) (*Service, error) { +func NewPresentationService(config config.PresentationServiceConfig, s storage.ServiceStorage) (*Service, error) { presentationStorage, err := presentationstorage.NewPresentationStorage(s) if err != nil { errMsg := "could not instantiate storage for the presentation definition service" @@ -103,3 +102,43 @@ func (s Service) DeletePresentationDefinition(request DeletePresentationDefiniti return nil } + +// CreateSubmission houses the main service logic for presentation submission creation. It validates the input, and +// produces a presentation submission value that conforms with the Submission specification. +func (s Service) CreateSubmission(request CreateSubmissionRequest) (*CreateSubmissionResponse, error) { + logrus.Debugf("creating presentation submission: %+v", request) + + if !request.IsValid() { + errMsg := fmt.Sprintf("invalid create presentation submission request: %+v", request) + return nil, util.LoggingNewError(errMsg) + } + + if err := exchange.IsValidPresentationSubmission(request.Submission); err != nil { + return nil, util.LoggingErrorMsg(err, "provided value is not a valid presentation submission") + } + + storedSubmission := presentationstorage.StoredSubmission{Submission: request.Submission} + + if err := s.storage.StoreSubmission(storedSubmission); err != nil { + return nil, util.LoggingErrorMsg(err, "could not store presentation") + } + + return &CreateSubmissionResponse{ + Submission: storedSubmission.Submission, + }, nil +} + +func (s Service) GetSubmission(request GetSubmissionRequest) (*GetSubmissionResponse, error) { + logrus.Debugf("getting presentation submission: %s", request.ID) + + storedSubmission, err := s.storage.GetSubmission(request.ID) + if err != nil { + err := errors.Wrapf(err, "error getting presentation submission: %s", request.ID) + return nil, util.LoggingError(err) + } + if storedSubmission == nil { + err := fmt.Errorf("presentation submission with id<%s> could not be found", request.ID) + return nil, util.LoggingError(err) + } + return &GetSubmissionResponse{Submission: storedSubmission.Submission}, nil +} diff --git a/pkg/service/presentation/storage/bolt.go b/pkg/service/presentation/storage/bolt.go index 9013695e7..8bfaba2bb 100644 --- a/pkg/service/presentation/storage/bolt.go +++ b/pkg/service/presentation/storage/bolt.go @@ -11,7 +11,8 @@ import ( ) const ( - namespace = "presentation_definition" + namespace = "presentation_definition" + submissionNamespace = "presentation_submission" ) type BoltPresentationStorage struct { @@ -70,3 +71,40 @@ func (b BoltPresentationStorage) DeletePresentation(id string) error { } return nil } + +func (b BoltPresentationStorage) StoreSubmission(submission StoredSubmission) error { + id := submission.Submission.ID + if id == "" { + err := errors.New("could not store submission definition without an ID") + logrus.WithError(err).Error() + return err + } + jsonBytes, err := json.Marshal(submission) + if err != nil { + errMsg := fmt.Sprintf("could not store submission definition: %s", id) + logrus.WithError(err).Error(errMsg) + return errors.Wrapf(err, errMsg) + } + return b.db.Write(submissionNamespace, id, jsonBytes) +} + +func (b BoltPresentationStorage) GetSubmission(id string) (*StoredSubmission, error) { + jsonBytes, err := b.db.Read(submissionNamespace, id) + if err != nil { + errMsg := fmt.Sprintf("could not get submission definition: %s", id) + logrus.WithError(err).Error(errMsg) + return nil, errors.Wrapf(err, errMsg) + } + if len(jsonBytes) == 0 { + err := fmt.Errorf("submission definition not found with id: %s", id) + logrus.WithError(err).Error("could not get submission definition from storage") + return nil, err + } + var stored StoredSubmission + if err := json.Unmarshal(jsonBytes, &stored); err != nil { + errMsg := fmt.Sprintf("could not unmarshal stored submission definition: %s", id) + logrus.WithError(err).Error(errMsg) + return nil, errors.Wrapf(err, errMsg) + } + return &stored, nil +} diff --git a/pkg/service/presentation/storage/storage.go b/pkg/service/presentation/storage/storage.go index 5f4c19367..28f02053b 100644 --- a/pkg/service/presentation/storage/storage.go +++ b/pkg/service/presentation/storage/storage.go @@ -12,6 +12,11 @@ type StoredPresentation struct { } type Storage interface { + DefinitionStorage + SubmissionStorage +} + +type DefinitionStorage interface { StorePresentation(schema StoredPresentation) error GetPresentation(id string) (*StoredPresentation, error) DeletePresentation(id string) error @@ -34,3 +39,12 @@ func NewPresentationStorage(s storage.ServiceStorage) (Storage, error) { return nil, util.LoggingNewErrorf("unsupported storage type: %s", s.Type()) } } + +type StoredSubmission struct { + Submission exchange.PresentationSubmission `json:"submission"` +} + +type SubmissionStorage interface { + StoreSubmission(schema StoredSubmission) error + GetSubmission(id string) (*StoredSubmission, error) +} diff --git a/pkg/service/service.go b/pkg/service/service.go index d3703f731..a35ecf7d1 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -2,9 +2,6 @@ package service import ( "fmt" - "github.com/tbd54566975/ssi-service/pkg/service/presentation" - "github.com/tbd54566975/ssi-service/pkg/service/submission" - "github.com/tbd54566975/ssi-service/config" "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/service/credential" @@ -12,6 +9,7 @@ import ( "github.com/tbd54566975/ssi-service/pkg/service/framework" "github.com/tbd54566975/ssi-service/pkg/service/keystore" "github.com/tbd54566975/ssi-service/pkg/service/manifest" + "github.com/tbd54566975/ssi-service/pkg/service/presentation" "github.com/tbd54566975/ssi-service/pkg/service/schema" "github.com/tbd54566975/ssi-service/pkg/storage" ) @@ -56,9 +54,6 @@ func validateServiceConfig(config config.ServicesConfig) error { if config.PresentationConfig.IsEmpty() { return fmt.Errorf("%s no config provided", framework.Presentation) } - if config.SubmissionConfig.IsEmpty() { - return fmt.Errorf("%s no config provided", framework.Submission) - } return nil } @@ -100,15 +95,10 @@ func instantiateServices(config config.ServicesConfig) ([]framework.Service, err return nil, util.LoggingErrorMsg(err, "could not instantiate the manifest service") } - presentationService, err := presentation.NewPresentationDefinitionService(config.PresentationConfig, storageProvider) - if err != nil { - return nil, util.LoggingErrorMsg(err, "could not instantiate the presentation service") - } - - submissionService, err := submission.NewSubmissionService(config.SubmissionConfig, storageProvider) + presentationService, err := presentation.NewPresentationService(config.PresentationConfig, storageProvider) if err != nil { return nil, util.LoggingErrorMsg(err, "could not instantiate the presentation service") } - return []framework.Service{keyStoreService, didService, schemaService, credentialService, manifestService, presentationService, submissionService}, nil + return []framework.Service{keyStoreService, didService, schemaService, credentialService, manifestService, presentationService}, nil } diff --git a/pkg/service/submission/model.go b/pkg/service/submission/model.go deleted file mode 100644 index e515734d5..000000000 --- a/pkg/service/submission/model.go +++ /dev/null @@ -1,30 +0,0 @@ -package submission - -import ( - "github.com/TBD54566975/ssi-sdk/credential/exchange" - "github.com/TBD54566975/ssi-sdk/util" -) - -type CreateSubmissionRequest struct { - Submission exchange.PresentationSubmission `json:"submission" validate:"required"` -} - -func (csr CreateSubmissionRequest) IsValid() bool { - return util.IsValidStruct(csr) == nil -} - -type CreateSubmissionResponse struct { - Submission exchange.PresentationSubmission `json:"submission"` -} - -type GetSubmissionRequest struct { - ID string `json:"id" validate:"required"` -} - -type GetSubmissionResponse struct { - Submission exchange.PresentationSubmission `json:"submission"` -} - -type DeleteSubmissionRequest struct { - ID string `json:"id" validate:"required"` -} diff --git a/pkg/service/submission/service.go b/pkg/service/submission/service.go deleted file mode 100644 index d0c8869cd..000000000 --- a/pkg/service/submission/service.go +++ /dev/null @@ -1,98 +0,0 @@ -package submission - -import ( - "fmt" - "github.com/TBD54566975/ssi-sdk/credential/exchange" - sdkutil "github.com/TBD54566975/ssi-sdk/util" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/tbd54566975/ssi-service/config" - "github.com/tbd54566975/ssi-service/internal/util" - "github.com/tbd54566975/ssi-service/pkg/service/framework" - submissionstorage "github.com/tbd54566975/ssi-service/pkg/service/submission/storage" - "github.com/tbd54566975/ssi-service/pkg/storage" -) - -type Service struct { - storage submissionstorage.Storage - config config.SubmissionServiceConfig -} - -func (s Service) Type() framework.Type { - return framework.Submission -} - -func (s Service) Status() framework.Status { - ae := sdkutil.NewAppendError() - if s.storage == nil { - ae.AppendString("no storage configured") - } - if !ae.IsEmpty() { - return framework.Status{ - Status: framework.StatusNotReady, - Message: fmt.Sprintf("submission service is not ready: %s", ae.Error().Error()), - } - } - return framework.Status{Status: framework.StatusReady} -} - -func (s Service) Config() config.SubmissionServiceConfig { - return s.config -} - -func NewSubmissionService(config config.SubmissionServiceConfig, s storage.ServiceStorage) (*Service, error) { - submissionStorage, err := submissionstorage.NewSubmissionStorage(s) - if err != nil { - errMsg := "could not instantiate storage for the submission service" - return nil, util.LoggingErrorMsg(err, errMsg) - } - service := Service{ - storage: submissionStorage, - config: config, - } - if !service.Status().IsReady() { - return nil, errors.New(service.Status().Message) - } - return &service, nil -} - -// CreateSubmission houses the main service logic for presentation submission creation. It validates the input, and -// produces a presentation submission value that conforms with the Submission specification. -func (s Service) CreateSubmission(request CreateSubmissionRequest) (*CreateSubmissionResponse, error) { - logrus.Debugf("creating presentation submission: %+v", request) - - if !request.IsValid() { - errMsg := fmt.Sprintf("invalid create presentation submission request: %+v", request) - return nil, util.LoggingNewError(errMsg) - } - - if err := exchange.IsValidPresentationSubmission(request.Submission); err != nil { - return nil, util.LoggingErrorMsg(err, "provided value is not a valid presentation submission") - } - - storedSubmission := submissionstorage.StoredSubmission{Submission: request.Submission} - - if err := s.storage.StoreSubmission(storedSubmission); err != nil { - return nil, util.LoggingErrorMsg(err, "could not store presentation") - } - - return &CreateSubmissionResponse{ - Submission: storedSubmission.Submission, - }, nil -} - -func (s Service) GetSubmission(request GetSubmissionRequest) (*GetSubmissionResponse, error) { - logrus.Debugf("getting presentation submission: %s", request.ID) - - storedSubmission, err := s.storage.GetSubmission(request.ID) - if err != nil { - err := errors.Wrapf(err, "error getting presentation submission: %s", request.ID) - return nil, util.LoggingError(err) - } - if storedSubmission == nil { - err := fmt.Errorf("presentation submission with id<%s> could not be found", request.ID) - return nil, util.LoggingError(err) - } - return &GetSubmissionResponse{Submission: storedSubmission.Submission}, nil -} diff --git a/pkg/service/submission/storage/bolt.go b/pkg/service/submission/storage/bolt.go deleted file mode 100644 index ccd39e044..000000000 --- a/pkg/service/submission/storage/bolt.go +++ /dev/null @@ -1,63 +0,0 @@ -package storage - -import ( - "fmt" - - "github.com/goccy/go-json" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/tbd54566975/ssi-service/pkg/storage" -) - -const ( - namespace = "presentation_submission" -) - -type BoltSubmissionStorage struct { - db *storage.BoltDB -} - -func NewBoltSubmissionStorage(db *storage.BoltDB) (*BoltSubmissionStorage, error) { - if db == nil { - return nil, errors.New("bolt db reference is nil") - } - return &BoltSubmissionStorage{db: db}, nil -} - -func (b BoltSubmissionStorage) StoreSubmission(submission StoredSubmission) error { - id := submission.Submission.ID - if id == "" { - err := errors.New("could not store submission definition without an ID") - logrus.WithError(err).Error() - return err - } - jsonBytes, err := json.Marshal(submission) - if err != nil { - errMsg := fmt.Sprintf("could not store submission definition: %s", id) - logrus.WithError(err).Error(errMsg) - return errors.Wrapf(err, errMsg) - } - return b.db.Write(namespace, id, jsonBytes) -} - -func (b BoltSubmissionStorage) GetSubmission(id string) (*StoredSubmission, error) { - jsonBytes, err := b.db.Read(namespace, id) - if err != nil { - errMsg := fmt.Sprintf("could not get submission definition: %s", id) - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrapf(err, errMsg) - } - if len(jsonBytes) == 0 { - err := fmt.Errorf("submission definition not found with id: %s", id) - logrus.WithError(err).Error("could not get submission definition from storage") - return nil, err - } - var stored StoredSubmission - if err := json.Unmarshal(jsonBytes, &stored); err != nil { - errMsg := fmt.Sprintf("could not unmarshal stored submission definition: %s", id) - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrapf(err, errMsg) - } - return &stored, nil -} diff --git a/pkg/service/submission/storage/storage.go b/pkg/service/submission/storage/storage.go deleted file mode 100644 index 239aa21f3..000000000 --- a/pkg/service/submission/storage/storage.go +++ /dev/null @@ -1,37 +0,0 @@ -package storage - -import ( - "fmt" - "github.com/TBD54566975/ssi-sdk/credential/exchange" - "github.com/tbd54566975/ssi-service/internal/util" - "github.com/tbd54566975/ssi-service/pkg/storage" -) - -type StoredSubmission struct { - Submission exchange.PresentationSubmission `json:"submission"` -} - -type Storage interface { - StoreSubmission(schema StoredSubmission) error - GetSubmission(id string) (*StoredSubmission, error) -} - -// NewSubmissionStorage finds the submission storage impl for a given ServiceStorage value -func NewSubmissionStorage(s storage.ServiceStorage) (Storage, error) { - switch s.Type() { - case storage.Bolt: - gotBolt, ok := s.(*storage.BoltDB) - if !ok { - errMsg := fmt.Sprintf("trouble instantiating : %s", s.Type()) - return nil, util.LoggingNewError(errMsg) - } - boltStorage, err := NewBoltSubmissionStorage(gotBolt) - if err != nil { - return nil, util.LoggingErrorMsg(err, "could not instantiate schema bolt storage") - } - return boltStorage, err - default: - errMsg := fmt.Errorf("unsupported storage type: %s", s.Type()) - return nil, util.LoggingError(errMsg) - } -} From f51d248c1da5bf974a538c74b130972e1867f822 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 14 Nov 2022 14:29:00 -0500 Subject: [PATCH 10/27] mage spec --- doc/swagger.yaml | 60 ++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 8e78f2da7..36f065382 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -1500,7 +1500,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialsResponse' + $ref: '#/definitions/pkg_server_router.GetCredentialsResponse' "400": description: Bad request schema: @@ -1522,14 +1522,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateCredentialRequest' + $ref: '#/definitions/pkg_server_router.CreateCredentialRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateCredentialResponse' + $ref: '#/definitions/pkg_server_router.CreateCredentialResponse' "400": description: Bad request schema: @@ -1586,7 +1586,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialResponse' + $ref: '#/definitions/pkg_server_router.GetCredentialResponse' "400": description: Bad request schema: @@ -1605,14 +1605,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifyCredentialRequest' + $ref: '#/definitions/pkg_server_router.VerifyCredentialRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifyCredentialResponse' + $ref: '#/definitions/pkg_server_router.VerifyCredentialResponse' "400": description: Bad request schema: @@ -1631,7 +1631,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetDIDMethodsResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetDIDMethodsResponse' summary: Get DID Methods tags: - DecentralizedIdentityAPI @@ -1652,7 +1652,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetDIDsByMethodResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetDIDsByMethodResponse' "400": description: Bad request schema: @@ -1670,7 +1670,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreateDIDByMethodRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateDIDByMethodRequest' - description: Method in: path name: method @@ -1682,7 +1682,7 @@ paths: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.CreateDIDByMethodResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateDIDByMethodResponse' "400": description: Bad request schema: @@ -1705,7 +1705,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreateDIDByMethodRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateDIDByMethodRequest' - description: Method in: path name: method @@ -1722,7 +1722,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetDIDByMethodResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetDIDByMethodResponse' "400": description: Bad request schema: @@ -1747,7 +1747,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.ResolveDIDResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ResolveDIDResponse' "400": description: Bad request schema: @@ -1766,7 +1766,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.StoreKeyRequest' + $ref: '#/definitions/pkg_server_router.StoreKeyRequest' produces: - application/json responses: @@ -1800,7 +1800,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetKeyDetailsResponse' + $ref: '#/definitions/pkg_server_router.GetKeyDetailsResponse' "400": description: Bad request schema: @@ -1833,7 +1833,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetManifestsResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetManifestsResponse' "400": description: Bad request schema: @@ -1855,14 +1855,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreateManifestRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateManifestRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.CreateManifestResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateManifestResponse' "400": description: Bad request schema: @@ -1919,7 +1919,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetManifestResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetManifestResponse' "400": description: Bad request schema: @@ -1939,7 +1939,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetApplicationsResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetApplicationsResponse' "400": description: Bad request schema: @@ -1962,14 +1962,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.SubmitApplicationRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.SubmitApplicationRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.SubmitApplicationResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.SubmitApplicationResponse' "400": description: Bad request schema: @@ -2026,7 +2026,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetApplicationResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetApplicationResponse' "400": description: Bad request schema: @@ -2046,7 +2046,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetResponsesResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetResponsesResponse' "400": description: Bad request schema: @@ -2103,7 +2103,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetResponseResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetResponseResponse' "400": description: Bad request schema: @@ -2211,7 +2211,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetSubmissionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSubmissionResponse' "400": description: Bad request schema: @@ -2232,14 +2232,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.ReviewSubmissionRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.ReviewSubmissionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionResponse' "400": description: Bad request schema: @@ -2261,14 +2261,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreateSubmissionRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.Operation' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.Operation' "400": description: Bad request schema: From 462be9dbc81480d8a46db7107224bcc992a2d8da Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 14 Nov 2022 14:57:50 -0500 Subject: [PATCH 11/27] Minor updates --- pkg/server/router/presentation.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/server/router/presentation.go b/pkg/server/router/presentation.go index 7bdedd28f..30af56c61 100644 --- a/pkg/server/router/presentation.go +++ b/pkg/server/router/presentation.go @@ -181,13 +181,13 @@ type CreateSubmissionRequest struct { type Operation struct { ID string `json:"id"` - Done bool `json:"bool"` - Result OperationResult `json:"result"` + Done bool `json:"done"` + Result OperationResult `json:"result,omitempty"` } type OperationResult struct { - Error string `json:"error"` - Response exchange.PresentationSubmission `json:"response"` + Error string `json:"error,omitempty"` + Response exchange.PresentationSubmission `json:"response,omitempty"` } // CreateSubmission godoc @@ -245,7 +245,7 @@ type ListSubmissionResponse struct { // @Failure 500 {string} string "Internal server error" // @Router /v1/presentations/submissions [get] func (pr PresentationRouter) ListSubmissions(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - return framework.Respond(ctx, w, ListSubmissionRequest{}, http.StatusOK) + return framework.Respond(ctx, w, ListSubmissionResponse{}, http.StatusOK) } type ReviewSubmissionRequest struct { From 358e957427ee880d07d824ffbb263e95510412ac Mon Sep 17 00:00:00 2001 From: Andres Uribe Date: Mon, 14 Nov 2022 13:34:42 -0500 Subject: [PATCH 12/27] [OSE-167] Scaffolding for /v1/operations (#172) * The simplest scaffolding possible * rename Co-authored-by: Gabe --- pkg/server/router/operation.go | 82 ++++++++++++++++++++++++ pkg/server/server.go | 18 ++++++ pkg/service/framework/framework.go | 1 + pkg/service/operation/service.go | 45 +++++++++++++ pkg/service/operation/storage/bolt.go | 38 +++++++++++ pkg/service/operation/storage/storage.go | 36 +++++++++++ pkg/service/service.go | 7 +- 7 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 pkg/server/router/operation.go create mode 100644 pkg/service/operation/service.go create mode 100644 pkg/service/operation/storage/bolt.go create mode 100644 pkg/service/operation/storage/storage.go diff --git a/pkg/server/router/operation.go b/pkg/server/router/operation.go new file mode 100644 index 000000000..a4dfb0bca --- /dev/null +++ b/pkg/server/router/operation.go @@ -0,0 +1,82 @@ +package router + +import ( + "context" + "fmt" + "github.com/pkg/errors" + svcframework "github.com/tbd54566975/ssi-service/pkg/service/framework" + "github.com/tbd54566975/ssi-service/pkg/service/operation" + "net/http" +) + +type OperationRouter struct { + service *operation.Service +} + +func NewOperationRouter(s svcframework.Service) (*OperationRouter, error) { + if s == nil { + return nil, errors.New("service cannot be nil") + } + service, ok := s.(*operation.Service) + if !ok { + return nil, fmt.Errorf("casting service: %s", s.Type()) + } + return &OperationRouter{service: service}, nil +} + +type Operation struct { +} + +// GetOperation godoc +// @Summary Get an operation +// @Description Get operation by its ID +// @Tags OperationAPI +// @Accept json +// @Produce json +// @Param id path string true "ID" +// @Success 200 {object} Operation "OK" +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" +// @Router /v1/operations/{id} [get] +func (pdr OperationRouter) GetOperation(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + return nil +} + +type GetOperationsRequest struct { + Parent string `json:"parent"` + Filter string `json:"filter"` +} + +type GetOperationsResponse struct { + Operations []Operation `json:"operations"` +} + +// GetOperations godoc +// @Summary List operations +// @Description List operations according to the request +// @Tags OperationAPI +// @Accept json +// @Produce json +// @Param request body GetOperationsRequest true "request body" +// @Success 200 {object} GetOperationsResponse "OK" +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" +// @Router /v1/operations [get] +func (pdr OperationRouter) GetOperations(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + return nil +} + +// CancelOperation godoc +// @Summary Cancel an ongoing operation +// @Description Cancels an ongoing operation, if possible. +// @Tags OperationAPI +// @Accept json +// @Produce json +// @Param id path string true "ID" +// @Success 200 {object} GetOperationsResponse "OK" +// @Failure 400 {string} string "Bad request" +// @Failure 500 {string} string "Internal server error" +// @Router /v1/operations [get] +func (pdr OperationRouter) CancelOperation(ctx context.Context, w http.ResponseWriter, r *http.Request) error { + return nil +} diff --git a/pkg/server/server.go b/pkg/server/server.go index bad65822b..96cb14d8f 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -23,6 +23,7 @@ const ( HealthPrefix = "/health" ReadinessPrefix = "/readiness" V1Prefix = "/v1" + OperationPrefix = "/operations" DIDsPrefix = "/dids" SchemasPrefix = "/schemas" CredentialsPrefix = "/credentials" @@ -102,6 +103,8 @@ func (s *SSIServer) instantiateRouter(service svcframework.Service) error { return s.ManifestAPI(service) case svcframework.Presentation: return s.PresentationAPI(service) + case svcframework.Operation: + return s.OperationAPI(service) default: return fmt.Errorf("could not instantiate API for service: %s", serviceType) } @@ -190,6 +193,21 @@ func (s *SSIServer) KeyStoreAPI(service svcframework.Service) (err error) { return } +func (s *SSIServer) OperationAPI(service svcframework.Service) (err error) { + operationRouter, err := router.NewOperationRouter(service) + if err != nil { + return util.LoggingErrorMsg(err, "creating operation router") + } + + handlerPath := V1Prefix + OperationPrefix + + s.Handle(http.MethodGet, path.Join(handlerPath, "/:id"), operationRouter.GetOperation) + s.Handle(http.MethodGet, handlerPath, operationRouter.GetOperations) + s.Handle(http.MethodPut, path.Join(handlerPath, "/:id/cancel"), operationRouter.CancelOperation) + + return +} + func (s *SSIServer) ManifestAPI(service svcframework.Service) (err error) { manifestRouter, err := router.NewManifestRouter(service) if err != nil { diff --git a/pkg/service/framework/framework.go b/pkg/service/framework/framework.go index d357e3ebb..8d1e9bd49 100644 --- a/pkg/service/framework/framework.go +++ b/pkg/service/framework/framework.go @@ -14,6 +14,7 @@ const ( KeyStore Type = "keystore" Manifest Type = "manifest" Presentation Type = "presentation" + Operation Type = "operation" StatusReady StatusState = "ready" StatusNotReady StatusState = "not_ready" diff --git a/pkg/service/operation/service.go b/pkg/service/operation/service.go new file mode 100644 index 000000000..05a4b29e0 --- /dev/null +++ b/pkg/service/operation/service.go @@ -0,0 +1,45 @@ +package operation + +import ( + "fmt" + sdkutil "github.com/TBD54566975/ssi-sdk/util" + "github.com/pkg/errors" + "github.com/tbd54566975/ssi-service/internal/util" + "github.com/tbd54566975/ssi-service/pkg/service/framework" + opstorage "github.com/tbd54566975/ssi-service/pkg/service/operation/storage" + "github.com/tbd54566975/ssi-service/pkg/storage" +) + +type Service struct { + storage opstorage.Storage +} + +func (s Service) Type() framework.Type { + return framework.Operation +} + +func (s Service) Status() framework.Status { + ae := sdkutil.NewAppendError() + if s.storage == nil { + ae.AppendString("no storage configured") + } + if !ae.IsEmpty() { + return framework.Status{ + Status: framework.StatusNotReady, + Message: fmt.Sprintf("operation service is not ready: %s", ae.Error().Error()), + } + } + return framework.Status{Status: framework.StatusReady} +} + +func NewOperationService(s storage.ServiceStorage) (*Service, error) { + opStorage, err := opstorage.NewOperationStorage(s) + if err != nil { + return nil, util.LoggingErrorMsg(err, "creating operation storage") + } + service := &Service{storage: opStorage} + if !service.Status().IsReady() { + return nil, errors.New(service.Status().Message) + } + return service, nil +} diff --git a/pkg/service/operation/storage/bolt.go b/pkg/service/operation/storage/bolt.go new file mode 100644 index 000000000..0198cfaee --- /dev/null +++ b/pkg/service/operation/storage/bolt.go @@ -0,0 +1,38 @@ +package storage + +import ( + "github.com/pkg/errors" + "github.com/tbd54566975/ssi-service/pkg/storage" +) + +type BoltOperationStorage struct { + db *storage.BoltDB +} + +func (b BoltOperationStorage) StoreOperation(op StoredOperation) error { + // TODO(andres) implement me + panic("implement me") +} + +func (b BoltOperationStorage) GetOperation(id string) (*StoredOperation, error) { + // TODO(andres) implement me + panic("implement me") +} + +func (b BoltOperationStorage) GetOperations() ([]StoredOperation, error) { + // TODO(andres) implement me + panic("implement me") +} + +func (b BoltOperationStorage) DeleteOperation(id string) error { + // TODO(andres) implement me + panic("implement me") +} + +func NewBoltOperationStorage(db *storage.BoltDB) (*BoltOperationStorage, error) { + if db == nil { + return nil, errors.New("bolt db reference is nil") + } + return &BoltOperationStorage{db: db}, nil + +} diff --git a/pkg/service/operation/storage/storage.go b/pkg/service/operation/storage/storage.go new file mode 100644 index 000000000..f5d94b4cd --- /dev/null +++ b/pkg/service/operation/storage/storage.go @@ -0,0 +1,36 @@ +package storage + +import ( + "github.com/tbd54566975/ssi-service/internal/util" + "github.com/tbd54566975/ssi-service/pkg/storage" +) + +type StoredOperation struct { + ID string `json:"id"` + Done bool `json:"done"` + Error string `json:"errorResult"` +} + +type Storage interface { + StoreOperation(op StoredOperation) error + GetOperation(id string) (*StoredOperation, error) + GetOperations() ([]StoredOperation, error) + DeleteOperation(id string) error +} + +func NewOperationStorage(s storage.ServiceStorage) (Storage, error) { + switch s.Type() { + case storage.Bolt: + gotBolt, ok := s.(*storage.BoltDB) + if !ok { + return nil, util.LoggingNewErrorf("trouble instantiating : %s", s.Type()) + } + boltStorage, err := NewBoltOperationStorage(gotBolt) + if err != nil { + return nil, util.LoggingErrorMsg(err, "could not instantiate schema bolt storage") + } + return boltStorage, err + default: + return nil, util.LoggingNewErrorf("unsupported storage type: %s", s.Type()) + } +} diff --git a/pkg/service/service.go b/pkg/service/service.go index a35ecf7d1..59482e01a 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -100,5 +100,10 @@ func instantiateServices(config config.ServicesConfig) ([]framework.Service, err return nil, util.LoggingErrorMsg(err, "could not instantiate the presentation service") } - return []framework.Service{keyStoreService, didService, schemaService, credentialService, manifestService, presentationService}, nil + operationService, err := operation.NewOperationService(storageProvider) + if err != nil { + return nil, util.LoggingErrorMsg(err, "could not instantiate the operation service") + } + + return []framework.Service{keyStoreService, didService, schemaService, credentialService, manifestService, presentationService, operationService}, nil } From 8feaff6f38752a08a83dd385708cf8ea3ed781f5 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 14 Nov 2022 15:08:18 -0500 Subject: [PATCH 13/27] Fix lint and compilation --- pkg/server/router/operation.go | 3 --- pkg/service/service.go | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/server/router/operation.go b/pkg/server/router/operation.go index a4dfb0bca..54741f134 100644 --- a/pkg/server/router/operation.go +++ b/pkg/server/router/operation.go @@ -24,9 +24,6 @@ func NewOperationRouter(s svcframework.Service) (*OperationRouter, error) { return &OperationRouter{service: service}, nil } -type Operation struct { -} - // GetOperation godoc // @Summary Get an operation // @Description Get operation by its ID diff --git a/pkg/service/service.go b/pkg/service/service.go index 59482e01a..fabcec46e 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -9,6 +9,7 @@ import ( "github.com/tbd54566975/ssi-service/pkg/service/framework" "github.com/tbd54566975/ssi-service/pkg/service/keystore" "github.com/tbd54566975/ssi-service/pkg/service/manifest" + "github.com/tbd54566975/ssi-service/pkg/service/operation" "github.com/tbd54566975/ssi-service/pkg/service/presentation" "github.com/tbd54566975/ssi-service/pkg/service/schema" "github.com/tbd54566975/ssi-service/pkg/storage" From 736fbd67cfd7e291e9c8e24ad1cf6f94ea0ca7ba Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 14 Nov 2022 15:15:33 -0500 Subject: [PATCH 14/27] PR fixes --- pkg/server/router/presentation.go | 2 +- pkg/server/router/presentation_test.go | 4 ++-- pkg/server/server.go | 2 +- pkg/server/server_presentation_test.go | 2 +- pkg/service/presentation/service.go | 12 ++++-------- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/pkg/server/router/presentation.go b/pkg/server/router/presentation.go index 30af56c61..32585829d 100644 --- a/pkg/server/router/presentation.go +++ b/pkg/server/router/presentation.go @@ -17,7 +17,7 @@ type PresentationRouter struct { service *presentation.Service } -func NewPresentationDefinitionRouter(s svcframework.Service) (*PresentationRouter, error) { +func NewPresentationRouter(s svcframework.Service) (*PresentationRouter, error) { if s == nil { return nil, errors.New("service cannot be nil") } diff --git a/pkg/server/router/presentation_test.go b/pkg/server/router/presentation_test.go index ba00abba1..dc9acb2b5 100644 --- a/pkg/server/router/presentation_test.go +++ b/pkg/server/router/presentation_test.go @@ -13,14 +13,14 @@ import ( func TestPresentationDefinitionRouter(t *testing.T) { t.Run("Nil Service", func(tt *testing.T) { - pdRouter, err := NewPresentationDefinitionRouter(nil) + pdRouter, err := NewPresentationRouter(nil) assert.Error(tt, err) assert.Empty(tt, pdRouter) assert.Contains(tt, err.Error(), "service cannot be nil") }) t.Run("Bad Service", func(tt *testing.T) { - pdRouter, err := NewPresentationDefinitionRouter(&testService{}) + pdRouter, err := NewPresentationRouter(&testService{}) assert.Error(tt, err) assert.Empty(tt, pdRouter) assert.Contains(tt, err.Error(), "could not create presentation router with service type: test") diff --git a/pkg/server/server.go b/pkg/server/server.go index 96cb14d8f..a53a1aaea 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -160,7 +160,7 @@ func (s *SSIServer) CredentialAPI(service svcframework.Service) (err error) { } func (s *SSIServer) PresentationAPI(service svcframework.Service) (err error) { - pRouter, err := router.NewPresentationDefinitionRouter(service) + pRouter, err := router.NewPresentationRouter(service) if err != nil { return util.LoggingErrorMsg(err, "could not create credential router") } diff --git a/pkg/server/server_presentation_test.go b/pkg/server/server_presentation_test.go index 7df7886d7..6e3422fc0 100644 --- a/pkg/server/server_presentation_test.go +++ b/pkg/server/server_presentation_test.go @@ -43,7 +43,7 @@ func TestPresentationAPI(t *testing.T) { service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s) assert.NoError(t, err) - pRouter, err := router.NewPresentationDefinitionRouter(service) + pRouter, err := router.NewPresentationRouter(service) assert.NoError(t, err) t.Cleanup(func() { diff --git a/pkg/service/presentation/service.go b/pkg/service/presentation/service.go index 8e43fed65..f9418acb6 100644 --- a/pkg/service/presentation/service.go +++ b/pkg/service/presentation/service.go @@ -43,8 +43,7 @@ func (s Service) Config() config.PresentationServiceConfig { func NewPresentationService(config config.PresentationServiceConfig, s storage.ServiceStorage) (*Service, error) { presentationStorage, err := presentationstorage.NewPresentationStorage(s) if err != nil { - errMsg := "could not instantiate storage for the presentation definition service" - return nil, util.LoggingErrorMsg(err, errMsg) + return nil, util.LoggingErrorMsg(err, "could not instantiate storage for the presentation definition service") } service := Service{ storage: presentationStorage, @@ -109,8 +108,7 @@ func (s Service) CreateSubmission(request CreateSubmissionRequest) (*CreateSubmi logrus.Debugf("creating presentation submission: %+v", request) if !request.IsValid() { - errMsg := fmt.Sprintf("invalid create presentation submission request: %+v", request) - return nil, util.LoggingNewError(errMsg) + return nil, util.LoggingNewErrorf("invalid create presentation submission request: %+v", request) } if err := exchange.IsValidPresentationSubmission(request.Submission); err != nil { @@ -133,12 +131,10 @@ func (s Service) GetSubmission(request GetSubmissionRequest) (*GetSubmissionResp storedSubmission, err := s.storage.GetSubmission(request.ID) if err != nil { - err := errors.Wrapf(err, "error getting presentation submission: %s", request.ID) - return nil, util.LoggingError(err) + return nil, util.LoggingNewErrorf("error getting presentation submission: %s", request.ID) } if storedSubmission == nil { - err := fmt.Errorf("presentation submission with id<%s> could not be found", request.ID) - return nil, util.LoggingError(err) + return nil, util.LoggingNewErrorf("presentation submission with id<%s> could not be found", request.ID) } return &GetSubmissionResponse{Submission: storedSubmission.Submission}, nil } From 021e0d4d2614a38e932ecacd5108e178e7a7798a Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 14 Nov 2022 15:17:53 -0500 Subject: [PATCH 15/27] rename --- pkg/server/router/presentation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/router/presentation.go b/pkg/server/router/presentation.go index 32585829d..c68ddfeff 100644 --- a/pkg/server/router/presentation.go +++ b/pkg/server/router/presentation.go @@ -176,7 +176,7 @@ func (pr PresentationRouter) DeletePresentationDefinition(ctx context.Context, w } type CreateSubmissionRequest struct { - PresentationJwt keyaccess.JWT `json:"presentationJwt" validate:"required"` + SubmissionJWT keyaccess.JWT `json:"submissionJwt" validate:"required"` } type Operation struct { From 8b1ec9448f27d24f54331847bf270022a1c4254f Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 14 Nov 2022 15:19:50 -0500 Subject: [PATCH 16/27] regen --- doc/swagger.yaml | 151 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 129 insertions(+), 22 deletions(-) diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 36f065382..352e9a503 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -198,6 +198,27 @@ definitions: type: boolean type: object did.Service: + properties: + accept: + items: + type: string + type: array + id: + type: string + routingKeys: + items: + type: string + type: array + serviceEndpoint: + description: |- + A string, map, or set composed of one or more strings and/or maps + All string values must be valid URIs + type: + type: string + required: + - id + - serviceEndpoint + - type type: object did.VerificationMethod: properties: @@ -599,10 +620,10 @@ definitions: type: object github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionRequest: properties: - presentationJwt: + submissionJwt: type: string required: - - presentationJwt + - submissionJwt type: object github.com_tbd54566975_ssi-service_pkg_server_router.GetApplicationResponse: properties: @@ -685,6 +706,20 @@ definitions: $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetManifestResponse' type: array type: object + github.com_tbd54566975_ssi-service_pkg_server_router.GetOperationsRequest: + properties: + filter: + type: string + parent: + type: string + type: object + github.com_tbd54566975_ssi-service_pkg_server_router.GetOperationsResponse: + properties: + operations: + items: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.Operation' + type: array + type: object github.com_tbd54566975_ssi-service_pkg_server_router.GetPresentationDefinitionResponse: properties: presentation_definition: @@ -737,7 +772,7 @@ definitions: type: object github.com_tbd54566975_ssi-service_pkg_server_router.Operation: properties: - bool: + done: type: boolean id: type: string @@ -1081,10 +1116,10 @@ definitions: type: object pkg_server_router.CreateSubmissionRequest: properties: - presentationJwt: + submissionJwt: type: string required: - - presentationJwt + - submissionJwt type: object pkg_server_router.GetApplicationResponse: properties: @@ -1167,6 +1202,20 @@ definitions: $ref: '#/definitions/pkg_server_router.GetManifestResponse' type: array type: object + pkg_server_router.GetOperationsRequest: + properties: + filter: + type: string + parent: + type: string + type: object + pkg_server_router.GetOperationsResponse: + properties: + operations: + items: + $ref: '#/definitions/pkg_server_router.Operation' + type: array + type: object pkg_server_router.GetPresentationDefinitionResponse: properties: presentation_definition: @@ -1219,7 +1268,7 @@ definitions: type: object pkg_server_router.Operation: properties: - bool: + done: type: boolean id: type: string @@ -1833,7 +1882,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetManifestsResponse' + $ref: '#/definitions/pkg_server_router.GetManifestsResponse' "400": description: Bad request schema: @@ -1855,14 +1904,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateManifestRequest' + $ref: '#/definitions/pkg_server_router.CreateManifestRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateManifestResponse' + $ref: '#/definitions/pkg_server_router.CreateManifestResponse' "400": description: Bad request schema: @@ -1919,7 +1968,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetManifestResponse' + $ref: '#/definitions/pkg_server_router.GetManifestResponse' "400": description: Bad request schema: @@ -1939,7 +1988,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetApplicationsResponse' + $ref: '#/definitions/pkg_server_router.GetApplicationsResponse' "400": description: Bad request schema: @@ -1962,14 +2011,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.SubmitApplicationRequest' + $ref: '#/definitions/pkg_server_router.SubmitApplicationRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.SubmitApplicationResponse' + $ref: '#/definitions/pkg_server_router.SubmitApplicationResponse' "400": description: Bad request schema: @@ -2026,7 +2075,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetApplicationResponse' + $ref: '#/definitions/pkg_server_router.GetApplicationResponse' "400": description: Bad request schema: @@ -2046,7 +2095,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetResponsesResponse' + $ref: '#/definitions/pkg_server_router.GetResponsesResponse' "400": description: Bad request schema: @@ -2103,7 +2152,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetResponseResponse' + $ref: '#/definitions/pkg_server_router.GetResponseResponse' "400": description: Bad request schema: @@ -2111,6 +2160,64 @@ paths: summary: Get response tags: - ResponseAPI + /v1/operations: + get: + consumes: + - application/json + description: Cancels an ongoing operation, if possible. + parameters: + - description: ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/pkg_server_router.GetOperationsResponse' + "400": + description: Bad request + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Cancel an ongoing operation + tags: + - OperationAPI + /v1/operations/{id}: + get: + consumes: + - application/json + description: Get operation by its ID + parameters: + - description: ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/pkg_server_router.Operation' + "400": + description: Bad request + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Get an operation + tags: + - OperationAPI /v1/presentation/definition: put: consumes: @@ -2291,7 +2398,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemasResponse' + $ref: '#/definitions/pkg_server_router.GetSchemasResponse' "500": description: Internal server error schema: @@ -2309,14 +2416,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaRequest' + $ref: '#/definitions/pkg_server_router.CreateSchemaRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaResponse' + $ref: '#/definitions/pkg_server_router.CreateSchemaResponse' "400": description: Bad request schema: @@ -2373,7 +2480,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemaResponse' + $ref: '#/definitions/pkg_server_router.GetSchemaResponse' "400": description: Bad request schema: @@ -2392,14 +2499,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaRequest' + $ref: '#/definitions/pkg_server_router.VerifySchemaRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaResponse' + $ref: '#/definitions/pkg_server_router.VerifySchemaResponse' "400": description: Bad request schema: From e7f32e4f098da0aed243d4b4dbc8504c67e088c7 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 14 Nov 2022 16:15:54 -0500 Subject: [PATCH 17/27] Removed unused config --- config/config.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/config/config.go b/config/config.go index 6a7975661..666c824e8 100644 --- a/config/config.go +++ b/config/config.go @@ -56,7 +56,6 @@ type ServicesConfig struct { CredentialConfig CredentialServiceConfig `toml:"credential,omitempty"` ManifestConfig ManifestServiceConfig `toml:"manifest,omitempty"` PresentationConfig PresentationServiceConfig `toml:"presentation,omitempty"` - SubmissionConfig SubmissionServiceConfig `toml:"submission,omitempty"` } // BaseServiceConfig represents configurable properties for a specific component of the SSI Service @@ -139,17 +138,6 @@ func (p *PresentationServiceConfig) IsEmpty() bool { return reflect.DeepEqual(p, &PresentationServiceConfig{}) } -type SubmissionServiceConfig struct { - *BaseServiceConfig -} - -func (d *SubmissionServiceConfig) IsEmpty() bool { - if d == nil { - return true - } - return reflect.DeepEqual(d, &SubmissionServiceConfig{}) -} - // LoadConfig attempts to load a TOML config file from the given path, and coerce it into our object model. // Before loading, defaults are applied on certain properties, which are overwritten if specified in the TOML file. func LoadConfig(path string) (*SSIServiceConfig, error) { @@ -215,9 +203,6 @@ func LoadConfig(path string) (*SSIServiceConfig, error) { PresentationConfig: PresentationServiceConfig{ BaseServiceConfig: &BaseServiceConfig{Name: "presentation"}, }, - SubmissionConfig: SubmissionServiceConfig{ - BaseServiceConfig: &BaseServiceConfig{Name: "submission"}, - }, } } else { // load from TOML file From 691746118b8f732c3d73f40959590c9d2cd4accb Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Fri, 11 Nov 2022 12:39:30 -0500 Subject: [PATCH 18/27] The simplest scaffolding possible --- doc/swagger.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 228f301cb..6a1299451 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -1025,6 +1025,8 @@ definitions: type: string name: type: string + name: + type: string outputDescriptors: items: $ref: '#/definitions/manifest.OutputDescriptor' @@ -2208,14 +2210,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreatePresentationDefinitionRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.CreatePresentationDefinitionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreatePresentationDefinitionResponse' "400": description: Bad request schema: @@ -2272,7 +2274,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetPresentationDefinitionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetPresentationDefinitionResponse' "400": description: Bad request schema: From 09c1570ec6feda5868f481566b41a523aa6df84d Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Fri, 11 Nov 2022 15:02:39 -0500 Subject: [PATCH 19/27] Implemented ops storage --- pkg/service/operation/storage/bolt.go | 65 +++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/pkg/service/operation/storage/bolt.go b/pkg/service/operation/storage/bolt.go index 0198cfaee..d86174d99 100644 --- a/pkg/service/operation/storage/bolt.go +++ b/pkg/service/operation/storage/bolt.go @@ -1,32 +1,81 @@ package storage import ( + "fmt" + "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" ) +const ( + namespace = "operation" +) + type BoltOperationStorage struct { db *storage.BoltDB } func (b BoltOperationStorage) StoreOperation(op StoredOperation) error { - // TODO(andres) implement me - panic("implement me") + id := op.ID + if id == "" { + err := errors.New("ID is required for storing operations") + logrus.WithError(err).Error() + return err + } + jsonBytes, err := json.Marshal(op) + if err != nil { + errMsg := fmt.Sprintf("marshalling operation with id: %s", id) + logrus.WithError(err).Error(errMsg) + return errors.Wrapf(err, errMsg) + } + return b.db.Write(namespace, id, jsonBytes) } func (b BoltOperationStorage) GetOperation(id string) (*StoredOperation, error) { - // TODO(andres) implement me - panic("implement me") + jsonBytes, err := b.db.Read(namespace, id) + if err != nil { + errMsg := fmt.Sprintf("reading operation with id: %s", id) + logrus.WithError(err).Error(errMsg) + return nil, errors.Wrapf(err, errMsg) + } + if len(jsonBytes) == 0 { + err := fmt.Errorf("operation not found with id: %s", id) + logrus.WithError(err).Error("found empty bytes") + return nil, err + } + var stored StoredOperation + if err := json.Unmarshal(jsonBytes, &stored); err != nil { + errMsg := fmt.Sprintf("unmarshalling stored operation: %s", id) + logrus.WithError(err).Error(errMsg) + return nil, errors.Wrapf(err, errMsg) + } + return &stored, nil } func (b BoltOperationStorage) GetOperations() ([]StoredOperation, error) { - // TODO(andres) implement me - panic("implement me") + operations, err := b.db.ReadAll(namespace) + if err != nil { + errMsg := "reading all from db" + logrus.WithError(err).Error("could not get all operations") + return nil, errors.Wrap(err, errMsg) + } + stored := make([]StoredOperation, 0, len(operations)) + for _, manifestBytes := range operations { + var nextOp StoredOperation + if err = json.Unmarshal(manifestBytes, &nextOp); err == nil { + stored = append(stored, nextOp) + } + } + return stored, nil } func (b BoltOperationStorage) DeleteOperation(id string) error { - // TODO(andres) implement me - panic("implement me") + if err := b.db.Delete(namespace, id); err != nil { + return util.LoggingErrorMsgf(err, "deleting operation: %s", id) + } + return nil } func NewBoltOperationStorage(db *storage.BoltDB) (*BoltOperationStorage, error) { From 90e137ad9905d14db4015e3e1861233e9f025c11 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 14 Nov 2022 13:53:30 -0500 Subject: [PATCH 20/27] Simple impl --- pkg/jwt/verification.go | 24 ++++++++++++++++++++++++ pkg/server/router/operation.go | 12 ++++++++++++ pkg/service/manifest/application.go | 17 ++--------------- 3 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 pkg/jwt/verification.go diff --git a/pkg/jwt/verification.go b/pkg/jwt/verification.go new file mode 100644 index 000000000..7c65c9a26 --- /dev/null +++ b/pkg/jwt/verification.go @@ -0,0 +1,24 @@ +package jwt + +import ( + didsdk "github.com/TBD54566975/ssi-sdk/did" + "github.com/pkg/errors" + didint "github.com/tbd54566975/ssi-service/internal/did" + "github.com/tbd54566975/ssi-service/internal/keyaccess" + "github.com/tbd54566975/ssi-service/internal/util" +) + +func VerifyTokenFromDID(did string, token keyaccess.JWT, s *didsdk.Resolver) error { + kid, pubKey, err := didint.ResolveKeyForDID(s, did) + if err != nil { + return errors.Wrapf(err, "failed to resolve applicant's did: %s", did) + } + verifier, err := keyaccess.NewJWKKeyAccessVerifier(kid, pubKey) + if err != nil { + return util.LoggingErrorMsg(err, "could not create application verifier") + } + if err := verifier.Verify(token); err != nil { + return util.LoggingErrorMsg(err, "could not verify the application's signature") + } + return nil +} diff --git a/pkg/server/router/operation.go b/pkg/server/router/operation.go index 54741f134..c1c85b1a2 100644 --- a/pkg/server/router/operation.go +++ b/pkg/server/router/operation.go @@ -3,6 +3,7 @@ package router import ( "context" "fmt" + "github.com/TBD54566975/ssi-sdk/credential/exchange" "github.com/pkg/errors" svcframework "github.com/tbd54566975/ssi-service/pkg/service/framework" "github.com/tbd54566975/ssi-service/pkg/service/operation" @@ -24,6 +25,17 @@ func NewOperationRouter(s svcframework.Service) (*OperationRouter, error) { return &OperationRouter{service: service}, nil } +type Operation struct { + ID string `json:"id"` + Done bool `json:"bool"` + Result *OperationResult `json:"result"` +} + +type OperationResult struct { + Error string `json:"error"` + Response exchange.PresentationSubmission `json:"response"` +} + // GetOperation godoc // @Summary Get an operation // @Description Get operation by its ID diff --git a/pkg/service/manifest/application.go b/pkg/service/manifest/application.go index edf9ecf55..04b6cf9c7 100644 --- a/pkg/service/manifest/application.go +++ b/pkg/service/manifest/application.go @@ -2,31 +2,18 @@ package manifest import ( "fmt" + "github.com/tbd54566975/ssi-service/pkg/jwt" "strings" "github.com/TBD54566975/ssi-sdk/credential/manifest" errresp "github.com/TBD54566975/ssi-sdk/error" - "github.com/pkg/errors" - - didint "github.com/tbd54566975/ssi-service/internal/did" "github.com/tbd54566975/ssi-service/internal/keyaccess" "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/service/credential" ) func (s Service) verifyApplicationJWT(did string, token keyaccess.JWT) error { - kid, pubKey, err := didint.ResolveKeyForDID(s.didResolver, did) - if err != nil { - return errors.Wrapf(err, "failed to resolve applicant's did: %s", did) - } - verifier, err := keyaccess.NewJWKKeyAccessVerifier(kid, pubKey) - if err != nil { - return util.LoggingErrorMsg(err, "could not create application verifier") - } - if err := verifier.Verify(token); err != nil { - return util.LoggingErrorMsg(err, "could not verify the application's signature") - } - return nil + return jwt.VerifyTokenFromDID(did, token, s.didResolver) } // validateCredentialApplication validates the credential application's signature(s) in addition to making sure it From 318ae8d04e18066eb255fbba049e37cb757c4035 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 28 Nov 2022 12:39:55 -0500 Subject: [PATCH 21/27] Submission implementation with test --- pkg/server/router/operation.go | 12 -- pkg/server/router/presentation.go | 57 +++++++- pkg/server/router/presentation_test.go | 6 +- pkg/server/router/testutils_test.go | 6 +- pkg/server/server_presentation_test.go | 153 +++++++++++++++++++- pkg/server/server_test.go | 8 +- pkg/service/operation/model.go | 12 ++ pkg/service/presentation/model.go | 8 +- pkg/service/presentation/service.go | 90 ++++++++++-- pkg/service/presentation/storage/bolt.go | 4 +- pkg/service/presentation/storage/storage.go | 3 + pkg/service/service.go | 2 +- sip/sips/sip6/README.md | 2 +- 13 files changed, 327 insertions(+), 36 deletions(-) create mode 100644 pkg/service/operation/model.go diff --git a/pkg/server/router/operation.go b/pkg/server/router/operation.go index c1c85b1a2..54741f134 100644 --- a/pkg/server/router/operation.go +++ b/pkg/server/router/operation.go @@ -3,7 +3,6 @@ package router import ( "context" "fmt" - "github.com/TBD54566975/ssi-sdk/credential/exchange" "github.com/pkg/errors" svcframework "github.com/tbd54566975/ssi-service/pkg/service/framework" "github.com/tbd54566975/ssi-service/pkg/service/operation" @@ -25,17 +24,6 @@ func NewOperationRouter(s svcframework.Service) (*OperationRouter, error) { return &OperationRouter{service: service}, nil } -type Operation struct { - ID string `json:"id"` - Done bool `json:"bool"` - Result *OperationResult `json:"result"` -} - -type OperationResult struct { - Error string `json:"error"` - Response exchange.PresentationSubmission `json:"response"` -} - // GetOperation godoc // @Summary Get an operation // @Description Get operation by its ID diff --git a/pkg/server/router/presentation.go b/pkg/server/router/presentation.go index 536309f22..0f8502fbe 100644 --- a/pkg/server/router/presentation.go +++ b/pkg/server/router/presentation.go @@ -4,9 +4,13 @@ import ( "context" "fmt" "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/TBD54566975/ssi-sdk/credential/signing" + "github.com/goccy/go-json" "github.com/pkg/errors" "github.com/sirupsen/logrus" + credint "github.com/tbd54566975/ssi-service/internal/credential" "github.com/tbd54566975/ssi-service/internal/keyaccess" + "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/server/framework" svcframework "github.com/tbd54566975/ssi-service/pkg/service/framework" "github.com/tbd54566975/ssi-service/pkg/service/presentation" @@ -179,6 +183,40 @@ type CreateSubmissionRequest struct { SubmissionJWT keyaccess.JWT `json:"submissionJwt" validate:"required"` } +func (r CreateSubmissionRequest) ToServiceRequest() (*presentation.CreateSubmissionRequest, error) { + sdkVp, err := signing.ParseVerifiablePresentationFromJWT(r.SubmissionJWT.String()) + if err != nil { + return nil, errors.Wrap(err, "parsing presentation from jwt") + } + if err := sdkVp.IsValid(); err != nil { + return nil, errors.Wrap(err, "verifying vp validity") + } + + submissionData, err := json.Marshal(sdkVp.PresentationSubmission) + if err != nil { + return nil, errors.Wrap(err, "marshalling presentation_submission") + } + var s exchange.PresentationSubmission + if err := json.Unmarshal(submissionData, &s); err != nil { + return nil, errors.Wrap(err, "unmarshalling presentation submission") + } + if err := s.IsValid(); err != nil { + return nil, errors.Wrap(err, "verifying submission validity") + } + sdkVp.PresentationSubmission = s + + credContainers, err := credint.NewCredentialContainerFromArray(sdkVp.VerifiableCredential) + if err != nil { + return nil, errors.Wrap(err, "parsing verifiable credential array") + } + + return &presentation.CreateSubmissionRequest{ + Presentation: *sdkVp, + SubmissionJWT: r.SubmissionJWT, + Submission: s, + Credentials: credContainers}, nil +} + type Operation struct { ID string `json:"id"` Done bool `json:"done"` @@ -202,7 +240,24 @@ type OperationResult struct { // @Failure 500 {string} string "Internal server error" // @Router /v1/presentations/submissions [put] func (pr PresentationRouter) CreateSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { - var resp Operation + var request CreateSubmissionRequest + if err := framework.Decode(r, &request); err != nil { + return framework.NewRequestError(util.LoggingErrorMsg(err, "invalid create submission request"), http.StatusBadRequest) + } + + req, err := request.ToServiceRequest() + if err != nil { + return framework.NewRequestError(util.LoggingErrorMsg(err, "invalid create submission request"), http.StatusBadRequest) + } + + operation, err := pr.service.CreateSubmission(*req) + if err != nil { + return framework.NewRequestError(util.LoggingErrorMsg(err, "cannot create submission"), http.StatusInternalServerError) + } + + resp := Operation{ + ID: operation.ID, + } return framework.Respond(ctx, w, resp, http.StatusCreated) } diff --git a/pkg/server/router/presentation_test.go b/pkg/server/router/presentation_test.go index dc9acb2b5..3f51be5ed 100644 --- a/pkg/server/router/presentation_test.go +++ b/pkg/server/router/presentation_test.go @@ -35,7 +35,11 @@ func TestPresentationDefinitionService(t *testing.T) { s, err := storage.NewStorage(storage.Bolt) assert.NoError(t, err) - service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s) + keyStoreService := testKeyStoreService(t, s) + didService := testDIDService(t, s, keyStoreService) + schemaService := testSchemaService(t, s, keyStoreService, didService) + + service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s, didService.GetResolver(), schemaService) assert.NoError(t, err) t.Run("Create returns the created definition", func(t *testing.T) { diff --git a/pkg/server/router/testutils_test.go b/pkg/server/router/testutils_test.go index 90bcd5430..6bee35610 100644 --- a/pkg/server/router/testutils_test.go +++ b/pkg/server/router/testutils_test.go @@ -14,7 +14,7 @@ import ( "github.com/tbd54566975/ssi-service/pkg/storage" ) -func testKeyStoreService(t *testing.T, db *storage.BoltDB) *keystore.Service { +func testKeyStoreService(t *testing.T, db storage.ServiceStorage) *keystore.Service { serviceConfig := config.KeyStoreServiceConfig{ServiceKeyPassword: "test-password"} // create a keystore service keystoreService, err := keystore.NewKeyStoreService(serviceConfig, db) @@ -23,7 +23,7 @@ func testKeyStoreService(t *testing.T, db *storage.BoltDB) *keystore.Service { return keystoreService } -func testDIDService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service) *did.Service { +func testDIDService(t *testing.T, db storage.ServiceStorage, keyStore *keystore.Service) *did.Service { serviceConfig := config.DIDServiceConfig{ BaseServiceConfig: &config.BaseServiceConfig{ Name: "did", @@ -38,7 +38,7 @@ func testDIDService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service return didService } -func testSchemaService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service, did *did.Service) *schema.Service { +func testSchemaService(t *testing.T, db storage.ServiceStorage, keyStore *keystore.Service, did *did.Service) *schema.Service { serviceConfig := config.SchemaServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "schema"}} // create a schema service schemaService, err := schema.NewSchemaService(serviceConfig, db, keyStore, did.GetResolver()) diff --git a/pkg/server/server_presentation_test.go b/pkg/server/server_presentation_test.go index 6e3422fc0..e6537078e 100644 --- a/pkg/server/server_presentation_test.go +++ b/pkg/server/server_presentation_test.go @@ -2,13 +2,17 @@ package server import ( "fmt" + "github.com/TBD54566975/ssi-sdk/credential" "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/TBD54566975/ssi-sdk/credential/signing" "github.com/TBD54566975/ssi-sdk/crypto" "github.com/goccy/go-json" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/tbd54566975/ssi-service/config" + "github.com/tbd54566975/ssi-service/internal/keyaccess" "github.com/tbd54566975/ssi-service/pkg/server/router" "github.com/tbd54566975/ssi-service/pkg/service/presentation" "github.com/tbd54566975/ssi-service/pkg/storage" @@ -40,7 +44,11 @@ func TestPresentationAPI(t *testing.T) { s, err := storage.NewStorage(storage.Bolt) assert.NoError(t, err) - service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s) + keyStoreService := testKeyStoreService(t, s) + didService := testDIDService(t, s, keyStoreService) + schemaService := testSchemaService(t, s, keyStoreService, didService) + + service, err := presentation.NewPresentationService(config.PresentationServiceConfig{}, s, didService.GetResolver(), schemaService) assert.NoError(t, err) pRouter, err := router.NewPresentationRouter(service) @@ -136,4 +144,147 @@ func TestPresentationAPI(t *testing.T) { assert.Error(t, pRouter.DeletePresentationDefinition(newRequestContext(), w, req)) w.Flush() }) + + t.Run("full flow of presentation", func(t *testing.T) { + + definition := createPresentationDefinition(t, pRouter) + vc := credential.VerifiableCredential{ + Context: []string{credential.VerifiableCredentialsLinkedDataContext}, + ID: "7035a7ec-66c8-4aec-9191-a34e8cf1e82b", + Type: []string{credential.VerifiablePresentationType}, + Issuer: "did:key:z4oJ8bFEFv7E3omhuK5LrAtL29Nmd8heBey9HtJCSvodSb7nrfaMrd6zb7fjYSRxrfSgBSDeM6Bs59KRKFgXSDWJcfcjs", + IssuanceDate: "2022-11-07T21:28:57Z", + ExpirationDate: "2051-10-05T14:48:00.000Z", + CredentialStatus: nil, + CredentialSubject: credential.CredentialSubject{ + "additionalName": "Mclovin", + "dateOfBirth": "1987-01-02", + "familyName": "Andres", + "givenName": "Uribe", + "id": "did:web:andresuribe.com", + }, + CredentialSchema: nil, + RefreshService: nil, + TermsOfUse: nil, + Evidence: nil, + Proof: nil, + } + + signer0 := getTestVectorKey0Signer(t) + vcData, err := signing.SignVerifiableCredentialJWT(signer0, vc) + assert.NoError(t, err) + ps := exchange.PresentationSubmission{ + ID: "a30e3b91-fb77-4d22-95fa-871689c322e2", + DefinitionID: definition.PresentationDefinition.ID, + DescriptorMap: []exchange.SubmissionDescriptor{ + { + ID: "wa_driver_license", + Format: string(exchange.JWTVPTarget), + Path: "$.verifiableCredential[0]", + PathNested: nil, + }, + }, + } + vp := credential.VerifiablePresentation{ + Context: []string{credential.VerifiableCredentialsLinkedDataContext}, + ID: "a9b575c7-bac2-47e7-a925-c432815ebb4c", + Holder: "did:key:z4oJ8eRi73fvkrXBgqTHZRTropESXLc7Vet8XpJrGUSBZAT2UHvQBYpBEPdAUiyKBi2XC2iFjgtn5Gw2Qd4WXHyj1LxjU", + Type: []string{credential.VerifiablePresentationType}, + PresentationSubmission: ps, + VerifiableCredential: []interface{}{keyaccess.JWT(vcData)}, + Proof: nil, + } + + signer1 := getTestVectorKey1Signer(t) + signed, err := signing.SignVerifiablePresentationJWT(signer1, vp) + assert.NoError(t, err) + + request := router.CreateSubmissionRequest{SubmissionJWT: keyaccess.JWT(signed)} + + value := newRequestValue(t, request) + req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/presentations/submissions", value) + w := httptest.NewRecorder() + + err = pRouter.CreateSubmission(newRequestContext(), w, req) + + require.NoError(t, err) + var resp router.Operation + assert.NoError(t, json.NewDecoder(w.Body).Decode(&resp)) + assert.Contains(t, resp.ID, "presentations/submissions/") + assert.False(t, resp.Done) + assert.Zero(t, resp.Result) + }) +} + +func createPresentationDefinition(t *testing.T, pRouter *router.PresentationRouter) router.CreatePresentationDefinitionResponse { + request := router.CreatePresentationDefinitionRequest{ + Name: "name", + Purpose: "purpose", + Format: nil, + SubmissionRequirements: nil, + InputDescriptors: []exchange.InputDescriptor{ + { + ID: "wa_driver_license", + Name: "washington state business license", + Purpose: "some testing stuff", + Format: nil, + Constraints: &exchange.Constraints{ + Fields: []exchange.Field{ + { + ID: "date_of_birth", + Path: []string{ + "$.credentialSubject.dateOfBirth", + "$.credentialSubject.dob", + "$.vc.credentialSubject.dateOfBirth", + "$.vc.credentialSubject.dob", + }, + }, + }, + }, + }, + }, + } + value := newRequestValue(t, request) + req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/presentations/definitions", value) + w := httptest.NewRecorder() + + assert.NoError(t, pRouter.CreatePresentationDefinition(newRequestContext(), w, req)) + var resp router.CreatePresentationDefinitionResponse + assert.NoError(t, json.NewDecoder(w.Body).Decode(&resp)) + return resp +} + +func getTestVectorKey0Signer(t *testing.T) crypto.JWTSigner { + // The corresponding JWT is below: + // eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkaWQ6a2V5Ono0b0o4YkZFRnY3RTNvbWh1SzVMckF0TDI5Tm1kOGhlQmV5OUh0SkNTdm9kU2I3bnJmYU1yZDZ6YjdmallTUnhyZlNnQlNEZU02QnM1OUtSS0ZnWFNEV0pjZmNqcyIsImp0aSI6IjcwMzVhN2VjLTY2YzgtNGFlYy05MTkxLWEzNGU4Y2YxZTgyYiIsIm5iZiI6MTY2Nzg1NjUzNywic3ViIjoiZGlkOmtleTp6NG9KOGVSaTczZnZrclhCZ3FUSFpSVHJvcEVTWExjN1ZldDhYcEpyR1VTQlpBVDJVSHZRQllwQkVQZEFVaXlLQmkyWEMyaUZqZ3RuNUd3MlFkNFdYSHlqMUx4alUiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJpZCI6IjcwMzVhN2VjLTY2YzgtNGFlYy05MTkxLWEzNGU4Y2YxZTgyYiIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmtleTp6NG9KOGJGRUZ2N0Uzb21odUs1THJBdEwyOU5tZDhoZUJleTlIdEpDU3ZvZFNiN25yZmFNcmQ2emI3ZmpZU1J4cmZTZ0JTRGVNNkJzNTlLUktGZ1hTRFdKY2ZjanMiLCJpc3N1YW5jZURhdGUiOiIyMDIyLTExLTA3VDIxOjI4OjU3WiIsImV4cGlyYXRpb25EYXRlIjoiMjA1MS0xMC0wNVQxNDo0ODowMC4wMDBaIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiYWRkaXRpb25hbE5hbWUiOiJNY2xvdmluIiwiZGF0ZU9mQmlydGgiOiIxOTg3LTAxLTAyIiwiZmFtaWx5TmFtZSI6IkFuZHJlcyIsImdpdmVuTmFtZSI6IlVyaWJlIiwiaWQiOiJkaWQ6a2V5Ono0b0o4ZVJpNzNmdmtyWEJncVRIWlJUcm9wRVNYTGM3VmV0OFhwSnJHVVNCWkFUMlVIdlFCWXBCRVBkQVVpeUtCaTJYQzJpRmpndG41R3cyUWQ0V1hIeWoxTHhqVSJ9fX0.mtrK1nDLL1Ly6iPwIpgpMbLFtoHcH52OQLbBSF-jVK7UHFZdKb8v4e_27uKZO0uszRm11kRV1NnDxoRJNNjFbw + + // AKA the issuers key + // AKA did:key:z4oJ8bFEFv7E3omhuK5LrAtL29Nmd8heBey9HtJCSvodSb7nrfaMrd6zb7fjYSRxrfSgBSDeM6Bs59KRKFgXSDWJcfcjs + knownJWK := crypto.PrivateKeyJWK{ + KTY: "EC", + CRV: "P-256", + X: "SVqB4JcUD6lsfvqMr-OKUNUphdNn64Eay60978ZlL74", + Y: "lf0u0pMj4lGAzZix5u4Cm5CMQIgMNpkwy163wtKYVKI", + D: "0g5vAEKzugrXaRbgKG0Tj2qJ5lMP4Bezds1_sTybkfk", + } + + signer, err := crypto.NewJWTSignerFromJWK(knownJWK.KID, knownJWK) + assert.NoError(t, err) + return *signer +} + +func getTestVectorKey1Signer(t *testing.T) crypto.JWTSigner { + // AKA the submitter of the presentation + // AKA did:key:z4oJ8eRi73fvkrXBgqTHZRTropESXLc7Vet8XpJrGUSBZAT2UHvQBYpBEPdAUiyKBi2XC2iFjgtn5Gw2Qd4WXHyj1LxjU + knownJWK := crypto.PrivateKeyJWK{ + KTY: "EC", + CRV: "P-256", + X: "6HEz8SLP7NgHPGp0bElryiD7u3_cO1EmX-ngsV_yLsI", + Y: "QlIYaYyDLxLkybDan9LOSkfGvjzZsrdgAb_nQr_Li5M", + D: "7m6c2Axy9OWi7-d9hFVhmMe22vQTfQDL_pG-3WFsjzc", + } + + signer, err := crypto.NewJWTSignerFromJWK(knownJWK.KID, knownJWK) + assert.NoError(t, err) + return *signer } diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index 0aa39486a..ed449ee3f 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -195,7 +195,7 @@ func testKeyStore(t *testing.T, bolt *storage.BoltDB) (*router.KeyStoreRouter, * return keyStoreRouter, keyStoreService } -func testKeyStoreService(t *testing.T, db *storage.BoltDB) *keystore.Service { +func testKeyStoreService(t *testing.T, db storage.ServiceStorage) *keystore.Service { serviceConfig := config.KeyStoreServiceConfig{ BaseServiceConfig: &config.BaseServiceConfig{Name: "test-keystore"}, ServiceKeyPassword: "test-password", @@ -208,7 +208,7 @@ func testKeyStoreService(t *testing.T, db *storage.BoltDB) *keystore.Service { return keystoreService } -func testDIDService(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Service) *did.Service { +func testDIDService(t *testing.T, bolt storage.ServiceStorage, keyStore *keystore.Service) *did.Service { serviceConfig := config.DIDServiceConfig{ BaseServiceConfig: &config.BaseServiceConfig{Name: "test-did"}, Methods: []string{"key"}, @@ -232,7 +232,7 @@ func testDIDRouter(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Servic return didRouter } -func testSchemaService(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Service, did *did.Service) *schema.Service { +func testSchemaService(t *testing.T, bolt storage.ServiceStorage, keyStore *keystore.Service, did *did.Service) *schema.Service { schemaService, err := schema.NewSchemaService(config.SchemaServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "test-schema"}}, bolt, keyStore, did.GetResolver()) require.NoError(t, err) require.NotEmpty(t, schemaService) @@ -249,7 +249,7 @@ func testSchemaRouter(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Ser return schemaRouter } -func testCredentialService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service, did *did.Service, schema *schema.Service) *credential.Service { +func testCredentialService(t *testing.T, db storage.ServiceStorage, keyStore *keystore.Service, did *did.Service, schema *schema.Service) *credential.Service { serviceConfig := config.CredentialServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "credential"}} // create a credential service diff --git a/pkg/service/operation/model.go b/pkg/service/operation/model.go new file mode 100644 index 000000000..4dc1f35cb --- /dev/null +++ b/pkg/service/operation/model.go @@ -0,0 +1,12 @@ +package operation + +type Result struct { + Error string `json:"error,omitempty"` + Response interface{} `json:"response,omitempty"` +} + +type Operation struct { + ID string `json:"json"` + Done bool `json:"done"` + Result Result `json:"result,omitempty"` +} diff --git a/pkg/service/presentation/model.go b/pkg/service/presentation/model.go index 784effa94..9d540bc98 100644 --- a/pkg/service/presentation/model.go +++ b/pkg/service/presentation/model.go @@ -1,8 +1,11 @@ package presentation import ( + credsdk "github.com/TBD54566975/ssi-sdk/credential" "github.com/TBD54566975/ssi-sdk/credential/exchange" "github.com/TBD54566975/ssi-sdk/util" + "github.com/tbd54566975/ssi-service/internal/credential" + "github.com/tbd54566975/ssi-service/internal/keyaccess" ) type CreatePresentationDefinitionRequest struct { @@ -31,7 +34,10 @@ type DeletePresentationDefinitionRequest struct { } type CreateSubmissionRequest struct { - Submission exchange.PresentationSubmission `json:"submission" validate:"required"` + Presentation credsdk.VerifiablePresentation `json:"presentation" validate:"required"` + SubmissionJWT keyaccess.JWT `json:"submissionJwt,omitempty" validate:"required"` + Submission exchange.PresentationSubmission `json:"submission" validate:"required"` + Credentials []credential.Container `json:"credentials,omitempty"` } func (csr CreateSubmissionRequest) IsValid() bool { diff --git a/pkg/service/presentation/service.go b/pkg/service/presentation/service.go index ce6875fd3..d9e0d3396 100644 --- a/pkg/service/presentation/service.go +++ b/pkg/service/presentation/service.go @@ -3,19 +3,30 @@ package presentation import ( "fmt" "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/TBD54566975/ssi-sdk/credential/signing" + didsdk "github.com/TBD54566975/ssi-sdk/did" sdkutil "github.com/TBD54566975/ssi-sdk/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/tbd54566975/ssi-service/config" + "github.com/tbd54566975/ssi-service/internal/credential" "github.com/tbd54566975/ssi-service/internal/util" + "github.com/tbd54566975/ssi-service/pkg/jwt" "github.com/tbd54566975/ssi-service/pkg/service/framework" + "github.com/tbd54566975/ssi-service/pkg/service/operation" + opstorage "github.com/tbd54566975/ssi-service/pkg/service/operation/storage" presentationstorage "github.com/tbd54566975/ssi-service/pkg/service/presentation/storage" + "github.com/tbd54566975/ssi-service/pkg/service/schema" "github.com/tbd54566975/ssi-service/pkg/storage" ) type Service struct { - storage presentationstorage.Storage - config config.PresentationServiceConfig + storage presentationstorage.Storage + opsStorage opstorage.Storage + config config.PresentationServiceConfig + resolver *didsdk.Resolver + schema *schema.Service + verifier *credential.Verifier } func (s Service) Type() framework.Type { @@ -40,14 +51,26 @@ func (s Service) Config() config.PresentationServiceConfig { return s.config } -func NewPresentationService(config config.PresentationServiceConfig, s storage.ServiceStorage) (*Service, error) { +func NewPresentationService(config config.PresentationServiceConfig, s storage.ServiceStorage, resolver *didsdk.Resolver, schema *schema.Service) (*Service, error) { presentationStorage, err := presentationstorage.NewPresentationStorage(s) if err != nil { return nil, util.LoggingErrorMsg(err, "could not instantiate definition storage for the presentation service") } + opsStorage, err := opstorage.NewOperationStorage(s) + if err != nil { + return nil, util.LoggingErrorMsg(err, "could not instantiate storage for the operations") + } + verifier, err := credential.NewCredentialVerifier(resolver, schema) + if err != nil { + return nil, util.LoggingErrorMsg(err, "could not instantiate verifier") + } service := Service{ - storage: presentationStorage, - config: config, + storage: presentationStorage, + opsStorage: opsStorage, + config: config, + resolver: resolver, + schema: schema, + verifier: verifier, } if !service.Status().IsReady() { return nil, errors.New(service.Status().Message) @@ -104,9 +127,7 @@ func (s Service) DeletePresentationDefinition(request DeletePresentationDefiniti // CreateSubmission houses the main service logic for presentation submission creation. It validates the input, and // produces a presentation submission value that conforms with the Submission specification. -func (s Service) CreateSubmission(request CreateSubmissionRequest) (*CreateSubmissionResponse, error) { - logrus.Debugf("creating presentation submission: %+v", request) - +func (s Service) CreateSubmission(request CreateSubmissionRequest) (*operation.Operation, error) { if !request.IsValid() { return nil, util.LoggingNewErrorf("invalid create presentation submission request: %+v", request) } @@ -115,14 +136,63 @@ func (s Service) CreateSubmission(request CreateSubmissionRequest) (*CreateSubmi return nil, util.LoggingErrorMsg(err, "provided value is not a valid presentation submission") } + sdkVp, err := signing.ParseVerifiablePresentationFromJWT(request.SubmissionJWT.String()) + if err != nil { + return nil, errors.Wrap(err, "parsing vp from jwt") + } + if err := jwt.VerifyTokenFromDID(sdkVp.Holder, request.SubmissionJWT, s.resolver); err != nil { + return nil, errors.Wrap(err, "verifying token from did") + } + + if _, err := s.storage.GetSubmission(request.Submission.ID); !errors.Is(err, presentationstorage.ErrSubmissionNotFound) { + return nil, errors.Errorf("submission with id %s already present", request.Submission.ID) + } + + definition, err := s.storage.GetPresentation(request.Submission.DefinitionID) + if err != nil { + return nil, util.LoggingErrorMsg(err, "getting presentation definition") + } + + for _, cred := range request.Credentials { + if !cred.IsValid() { + return nil, util.LoggingNewErrorf("invalid credential %+v", cred) + } + if cred.CredentialJWT != nil { + if err := s.verifier.VerifyJWTCredential(*cred.CredentialJWT); err != nil { + return nil, errors.Wrapf(err, "verifying jwt credential %s", cred.CredentialJWT) + } + } else { + if cred.Credential != nil && cred.Credential.Proof != nil { + if err := s.verifier.VerifyDataIntegrityCredential(*cred.Credential); err != nil { + return nil, errors.Wrapf(err, "verifying data integrity credential %+v", cred.Credential) + } + } + } + } + + if err := exchange.VerifyPresentationSubmissionVP(definition.PresentationDefinition, request.Presentation); err != nil { + return nil, util.LoggingErrorMsg(err, "verifying presentation submission vp") + } + storedSubmission := presentationstorage.StoredSubmission{Submission: request.Submission} + // TODO(andres): IO requests should be done in parallel, once we have context wired up. if err := s.storage.StoreSubmission(storedSubmission); err != nil { return nil, util.LoggingErrorMsg(err, "could not store presentation") } - return &CreateSubmissionResponse{ - Submission: storedSubmission.Submission, + opID := fmt.Sprintf("presentations/submissions/%s", storedSubmission.Submission.ID) + storedOp := opstorage.StoredOperation{ + ID: opID, + Done: false, + } + if err := s.opsStorage.StoreOperation(storedOp); err != nil { + return nil, util.LoggingErrorMsg(err, "could not store operation") + } + + return &operation.Operation{ + ID: storedOp.ID, + Done: false, }, nil } diff --git a/pkg/service/presentation/storage/bolt.go b/pkg/service/presentation/storage/bolt.go index 2c1f88371..170571374 100644 --- a/pkg/service/presentation/storage/bolt.go +++ b/pkg/service/presentation/storage/bolt.go @@ -85,7 +85,9 @@ func (b BoltPresentationStorage) GetSubmission(id string) (*StoredSubmission, er return nil, util.LoggingNewErrorf("could not get submission definition: %s", id) } if len(jsonBytes) == 0 { - return nil, util.LoggingNewErrorf("submission definition not found with id: %s", id) + err := errors.Wrapf(ErrSubmissionNotFound, "submission definition not found with id: %s", id) + logrus.WithError(err).Error("could not get submission definition from storage") + return nil, err } var stored StoredSubmission if err := json.Unmarshal(jsonBytes, &stored); err != nil { diff --git a/pkg/service/presentation/storage/storage.go b/pkg/service/presentation/storage/storage.go index 28f02053b..fb49eef8a 100644 --- a/pkg/service/presentation/storage/storage.go +++ b/pkg/service/presentation/storage/storage.go @@ -2,6 +2,7 @@ package storage import ( "github.com/TBD54566975/ssi-sdk/credential/exchange" + "github.com/pkg/errors" "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/storage" ) @@ -48,3 +49,5 @@ type SubmissionStorage interface { StoreSubmission(schema StoredSubmission) error GetSubmission(id string) (*StoredSubmission, error) } + +var ErrSubmissionNotFound = errors.New("submission not found") diff --git a/pkg/service/service.go b/pkg/service/service.go index 4b2e25d72..15247a675 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -97,7 +97,7 @@ func instantiateServices(config config.ServicesConfig) ([]framework.Service, err return nil, util.LoggingErrorMsg(err, "could not instantiate the manifest service") } - presentationService, err := presentation.NewPresentationService(config.PresentationConfig, storageProvider) + presentationService, err := presentation.NewPresentationService(config.PresentationConfig, storageProvider, didResolver, schemaService) if err != nil { return nil, util.LoggingErrorMsg(err, "could not instantiate the presentation service") } diff --git a/sip/sips/sip6/README.md b/sip/sips/sip6/README.md index b2429617b..7432f50c5 100644 --- a/sip/sips/sip6/README.md +++ b/sip/sips/sip6/README.md @@ -184,7 +184,7 @@ A `Submission` object: { "iss": "did:web:andresuribe.com", "vp": { - "presentation_submission": { + "presentation_submission": { "id": "a30e3b91-fb77-4d22-95fa-871689c322e2", "definition_id": "32f54163-7166-48f1-93d8-ff217bdb0653", "descriptor_map": [ From 7738acb13fb1636798066dc5934b9f65f5e94c4b Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Mon, 28 Nov 2022 12:52:43 -0500 Subject: [PATCH 22/27] Swagger spec --- doc/swagger.yaml | 169 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 152 insertions(+), 17 deletions(-) diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 6a1299451..205f14c4f 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -8,6 +8,8 @@ definitions: id: description: Credential ID type: string + revoked: + type: boolean type: object credential.CredentialSchema: properties: @@ -483,6 +485,8 @@ definitions: type: string issuer: type: string + revocable: + type: boolean schema: description: A schema is optional. If present, we'll attempt to look it up and validate the data against it. @@ -627,6 +631,20 @@ definitions: id: type: string type: object + github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialStatusListResponse: + properties: + credential: + $ref: '#/definitions/credential.VerifiableCredential' + credentialJwt: + type: string + id: + type: string + type: object + github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialStatusResponse: + properties: + revoked: + type: boolean + type: object github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialsResponse: properties: credentials: @@ -823,6 +841,18 @@ definitions: items: {} type: array type: object + github.com_tbd54566975_ssi-service_pkg_server_router.UpdateCredentialStatusRequest: + properties: + revoked: + type: boolean + required: + - revoked + type: object + github.com_tbd54566975_ssi-service_pkg_server_router.UpdateCredentialStatusResponse: + properties: + revoked: + type: boolean + type: object github.com_tbd54566975_ssi-service_pkg_server_router.VerifyCredentialRequest: properties: credential: @@ -979,6 +1009,8 @@ definitions: type: string issuer: type: string + revocable: + type: boolean schema: description: A schema is optional. If present, we'll attempt to look it up and validate the data against it. @@ -1025,8 +1057,6 @@ definitions: type: string name: type: string - name: - type: string outputDescriptors: items: $ref: '#/definitions/manifest.OutputDescriptor' @@ -1125,6 +1155,20 @@ definitions: id: type: string type: object + pkg_server_router.GetCredentialStatusListResponse: + properties: + credential: + $ref: '#/definitions/credential.VerifiableCredential' + credentialJwt: + type: string + id: + type: string + type: object + pkg_server_router.GetCredentialStatusResponse: + properties: + revoked: + type: boolean + type: object pkg_server_router.GetCredentialsResponse: properties: credentials: @@ -1321,6 +1365,18 @@ definitions: items: {} type: array type: object + pkg_server_router.UpdateCredentialStatusRequest: + properties: + revoked: + type: boolean + required: + - revoked + type: object + pkg_server_router.UpdateCredentialStatusResponse: + properties: + revoked: + type: boolean + type: object pkg_server_router.VerifyCredentialRequest: properties: credential: @@ -1624,6 +1680,85 @@ paths: summary: Get Credential tags: - CredentialAPI + /v1/credentials/{id}/status: + get: + consumes: + - application/json + description: Get credential status by id + parameters: + - description: ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialStatusResponse' + "400": + description: Bad request + schema: + type: string + summary: Get Credential Status + tags: + - CredentialAPI + put: + consumes: + - application/json + description: Update a credential's status + parameters: + - description: request body + in: body + name: request + required: true + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.UpdateCredentialStatusRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.UpdateCredentialStatusResponse' + "400": + description: Bad request + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Update Credential Status + tags: + - CredentialAPI + /v1/credentials/status/{id}: + get: + consumes: + - application/json + description: Get credential status list by id + parameters: + - description: ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetCredentialStatusListResponse' + "400": + description: Bad request + schema: + type: string + summary: Get Credential Status List + tags: + - CredentialAPI /v1/credentials/verification: put: consumes: @@ -1796,7 +1931,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.StoreKeyRequest' + $ref: '#/definitions/pkg_server_router.StoreKeyRequest' produces: - application/json responses: @@ -1830,7 +1965,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetKeyDetailsResponse' + $ref: '#/definitions/pkg_server_router.GetKeyDetailsResponse' "400": description: Bad request schema: @@ -2158,7 +2293,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetOperationsResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetOperationsResponse' "400": description: Bad request schema: @@ -2187,7 +2322,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.Operation' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.Operation' "400": description: Bad request schema: @@ -2299,7 +2434,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.GetSubmissionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSubmissionResponse' "400": description: Bad request schema: @@ -2320,14 +2455,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.ReviewSubmissionRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/pkg_server_router.ReviewSubmissionResponse' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.ReviewSubmissionResponse' "400": description: Bad request schema: @@ -2349,14 +2484,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/pkg_server_router.CreateSubmissionRequest' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSubmissionRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/pkg_server_router.Operation' + $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.Operation' "400": description: Bad request schema: @@ -2379,7 +2514,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemasResponse' + $ref: '#/definitions/pkg_server_router.GetSchemasResponse' "500": description: Internal server error schema: @@ -2397,14 +2532,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaRequest' + $ref: '#/definitions/pkg_server_router.CreateSchemaRequest' produces: - application/json responses: "201": description: Created schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.CreateSchemaResponse' + $ref: '#/definitions/pkg_server_router.CreateSchemaResponse' "400": description: Bad request schema: @@ -2461,7 +2596,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.GetSchemaResponse' + $ref: '#/definitions/pkg_server_router.GetSchemaResponse' "400": description: Bad request schema: @@ -2480,14 +2615,14 @@ paths: name: request required: true schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaRequest' + $ref: '#/definitions/pkg_server_router.VerifySchemaRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/github.com_tbd54566975_ssi-service_pkg_server_router.VerifySchemaResponse' + $ref: '#/definitions/pkg_server_router.VerifySchemaResponse' "400": description: Bad request schema: From df62d0f8e63470c9979cb1e3fde49ec83339c8b3 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Tue, 29 Nov 2022 11:27:27 -0500 Subject: [PATCH 23/27] Neatly wrapping up --- go.mod | 11 +++++++---- go.sum | 16 ++++++---------- internal/did/access.go | 13 ++++++++++--- pkg/server/server_presentation_test.go | 5 +---- pkg/service/presentation/service.go | 14 +++++++------- pkg/service/presentation/storage/bolt.go | 4 +--- 6 files changed, 32 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 1867fb1b6..9ad9fbab0 100644 --- a/go.mod +++ b/go.mod @@ -2,9 +2,12 @@ module github.com/tbd54566975/ssi-service go 1.19 +// TODO(andres): Remove this once https://github.com/TBD54566975/ssi-sdk/pull/260 is merged. +replace github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221128221135-c0926ad9dcb6 => ../ssi-sdk + require ( github.com/BurntSushi/toml v1.2.1 - github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221110170444-a9e67907c8f9 + github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221128221135-c0926ad9dcb6 github.com/ardanlabs/conf v1.5.0 github.com/dimfeld/httptreemux/v5 v5.5.0 github.com/go-playground/locales v0.14.0 @@ -31,7 +34,7 @@ require ( ) require ( - github.com/bits-and-blooms/bitset v1.3.3 // indirect + github.com/bits-and-blooms/bitset v1.4.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -46,11 +49,11 @@ require ( github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.1.0 // indirect github.com/multiformats/go-multicodec v0.7.0 // indirect - github.com/piprate/json-gold v0.4.2 // indirect + github.com/piprate/json-gold v0.5.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.1.0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect - github.com/santhosh-tekuri/jsonschema/v5 v5.0.2 // indirect + github.com/santhosh-tekuri/jsonschema/v5 v5.1.0 // indirect golang.org/x/sys v0.2.0 // indirect golang.org/x/term v0.2.0 // indirect golang.org/x/text v0.4.0 // indirect diff --git a/go.sum b/go.sum index b4a7c185d..b3a123dce 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,9 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221110170444-a9e67907c8f9 h1:46Xgt4DbZhaw4mOWYlABWuSMhZSGKsURlo2yzDjfVyA= -github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221110170444-a9e67907c8f9/go.mod h1:c2wq4GipGxjAPXgnxNBvnuB7OqDNKjriKuD60H0Q6Ho= github.com/ardanlabs/conf v1.5.0 h1:5TwP6Wu9Xi07eLFEpiCUF3oQXh9UzHMDVnD3u/I5d5c= github.com/ardanlabs/conf v1.5.0/go.mod h1:ILsMo9dMqYzCxDjDXTiwMI0IgxOJd0MOiucbQY2wlJw= -github.com/bits-and-blooms/bitset v1.3.3 h1:R1XWiopGiXf66xygsiLpzLo67xEYvMkHw3w+rCOSAwg= -github.com/bits-and-blooms/bitset v1.3.3/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.4.0 h1:+YZ8ePm+He2pU3dZlIZiOeAKfrBkXi1lSrXJ/Xzgbu8= +github.com/bits-and-blooms/bitset v1.4.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -78,29 +76,27 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 h1:Yl0tPBa8QPjGmesFh1D0rDy+q1Twx6FyU7VWHi8wZbI= github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4= -github.com/piprate/json-gold v0.4.2 h1:Rq8V+637HOFcj20KdTqW/g/llCwX2qtau0g5d1pD79o= -github.com/piprate/json-gold v0.4.2/go.mod h1:OK1z7UgtBZk06n2cDE2OSq1kffmjFFp5/2yhLLCz9UM= +github.com/piprate/json-gold v0.5.0 h1:RmGh1PYboCFcchVFuh2pbSWAZy4XJaqTMU4KQYsApbM= +github.com/piprate/json-gold v0.5.0/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/santhosh-tekuri/jsonschema/v5 v5.0.2 h1:zOYFITq/5SO7YOv39/Taw8s1skb0Py39K5V2XvCEP48= -github.com/santhosh-tekuri/jsonschema/v5 v5.0.2/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= +github.com/santhosh-tekuri/jsonschema/v5 v5.1.0 h1:wSUNu/w/7OQ0Y3NVnfTU5uxzXY4uMpXW92VXEJKqBB0= +github.com/santhosh-tekuri/jsonschema/v5 v5.1.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/internal/did/access.go b/internal/did/access.go index 31e862a06..2efbcd0e0 100644 --- a/internal/did/access.go +++ b/internal/did/access.go @@ -3,7 +3,6 @@ package did import ( "crypto" "fmt" - "github.com/TBD54566975/ssi-sdk/cryptosuite" didsdk "github.com/TBD54566975/ssi-sdk/did" "github.com/goccy/go-json" @@ -66,11 +65,19 @@ func extractKeyFromVerificationMethod(method didsdk.VerificationMethod) (pubKey return case method.PublicKeyJWK != nil: jwkBytes, jwkErr := json.Marshal(method.PublicKeyJWK) - if err != nil { + if jwkErr != nil { err = jwkErr return } - pubKey, err = jwk.ParseKey(jwkBytes) + parsed, parseErr := jwk.ParseKey(jwkBytes) + if parseErr != nil { + err = parseErr + return + } + if err = parsed.Raw(&pubKey); err != nil { + return + } + return } err = errors.New("no public key found in verification method") diff --git a/pkg/server/server_presentation_test.go b/pkg/server/server_presentation_test.go index e6537078e..442dc1ee6 100644 --- a/pkg/server/server_presentation_test.go +++ b/pkg/server/server_presentation_test.go @@ -145,7 +145,7 @@ func TestPresentationAPI(t *testing.T) { w.Flush() }) - t.Run("full flow of presentation", func(t *testing.T) { + t.Run("well formed submission returns operation", func(t *testing.T) { definition := createPresentationDefinition(t, pRouter) vc := credential.VerifiableCredential{ @@ -255,9 +255,6 @@ func createPresentationDefinition(t *testing.T, pRouter *router.PresentationRout } func getTestVectorKey0Signer(t *testing.T) crypto.JWTSigner { - // The corresponding JWT is below: - // eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkaWQ6a2V5Ono0b0o4YkZFRnY3RTNvbWh1SzVMckF0TDI5Tm1kOGhlQmV5OUh0SkNTdm9kU2I3bnJmYU1yZDZ6YjdmallTUnhyZlNnQlNEZU02QnM1OUtSS0ZnWFNEV0pjZmNqcyIsImp0aSI6IjcwMzVhN2VjLTY2YzgtNGFlYy05MTkxLWEzNGU4Y2YxZTgyYiIsIm5iZiI6MTY2Nzg1NjUzNywic3ViIjoiZGlkOmtleTp6NG9KOGVSaTczZnZrclhCZ3FUSFpSVHJvcEVTWExjN1ZldDhYcEpyR1VTQlpBVDJVSHZRQllwQkVQZEFVaXlLQmkyWEMyaUZqZ3RuNUd3MlFkNFdYSHlqMUx4alUiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJpZCI6IjcwMzVhN2VjLTY2YzgtNGFlYy05MTkxLWEzNGU4Y2YxZTgyYiIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiZGlkOmtleTp6NG9KOGJGRUZ2N0Uzb21odUs1THJBdEwyOU5tZDhoZUJleTlIdEpDU3ZvZFNiN25yZmFNcmQ2emI3ZmpZU1J4cmZTZ0JTRGVNNkJzNTlLUktGZ1hTRFdKY2ZjanMiLCJpc3N1YW5jZURhdGUiOiIyMDIyLTExLTA3VDIxOjI4OjU3WiIsImV4cGlyYXRpb25EYXRlIjoiMjA1MS0xMC0wNVQxNDo0ODowMC4wMDBaIiwiY3JlZGVudGlhbFN1YmplY3QiOnsiYWRkaXRpb25hbE5hbWUiOiJNY2xvdmluIiwiZGF0ZU9mQmlydGgiOiIxOTg3LTAxLTAyIiwiZmFtaWx5TmFtZSI6IkFuZHJlcyIsImdpdmVuTmFtZSI6IlVyaWJlIiwiaWQiOiJkaWQ6a2V5Ono0b0o4ZVJpNzNmdmtyWEJncVRIWlJUcm9wRVNYTGM3VmV0OFhwSnJHVVNCWkFUMlVIdlFCWXBCRVBkQVVpeUtCaTJYQzJpRmpndG41R3cyUWQ0V1hIeWoxTHhqVSJ9fX0.mtrK1nDLL1Ly6iPwIpgpMbLFtoHcH52OQLbBSF-jVK7UHFZdKb8v4e_27uKZO0uszRm11kRV1NnDxoRJNNjFbw - // AKA the issuers key // AKA did:key:z4oJ8bFEFv7E3omhuK5LrAtL29Nmd8heBey9HtJCSvodSb7nrfaMrd6zb7fjYSRxrfSgBSDeM6Bs59KRKFgXSDWJcfcjs knownJWK := crypto.PrivateKeyJWK{ diff --git a/pkg/service/presentation/service.go b/pkg/service/presentation/service.go index d9e0d3396..b51cf8566 100644 --- a/pkg/service/presentation/service.go +++ b/pkg/service/presentation/service.go @@ -129,11 +129,11 @@ func (s Service) DeletePresentationDefinition(request DeletePresentationDefiniti // produces a presentation submission value that conforms with the Submission specification. func (s Service) CreateSubmission(request CreateSubmissionRequest) (*operation.Operation, error) { if !request.IsValid() { - return nil, util.LoggingNewErrorf("invalid create presentation submission request: %+v", request) + return nil, errors.Errorf("invalid create presentation submission request: %+v", request) } if err := exchange.IsValidPresentationSubmission(request.Submission); err != nil { - return nil, util.LoggingErrorMsg(err, "provided value is not a valid presentation submission") + return nil, errors.Wrap(err, "provided value is not a valid presentation submission") } sdkVp, err := signing.ParseVerifiablePresentationFromJWT(request.SubmissionJWT.String()) @@ -150,12 +150,12 @@ func (s Service) CreateSubmission(request CreateSubmissionRequest) (*operation.O definition, err := s.storage.GetPresentation(request.Submission.DefinitionID) if err != nil { - return nil, util.LoggingErrorMsg(err, "getting presentation definition") + return nil, errors.Wrap(err, "getting presentation definition") } for _, cred := range request.Credentials { if !cred.IsValid() { - return nil, util.LoggingNewErrorf("invalid credential %+v", cred) + return nil, errors.Errorf("invalid credential %+v", cred) } if cred.CredentialJWT != nil { if err := s.verifier.VerifyJWTCredential(*cred.CredentialJWT); err != nil { @@ -171,14 +171,14 @@ func (s Service) CreateSubmission(request CreateSubmissionRequest) (*operation.O } if err := exchange.VerifyPresentationSubmissionVP(definition.PresentationDefinition, request.Presentation); err != nil { - return nil, util.LoggingErrorMsg(err, "verifying presentation submission vp") + return nil, errors.Wrap(err, "verifying presentation submission vp") } storedSubmission := presentationstorage.StoredSubmission{Submission: request.Submission} // TODO(andres): IO requests should be done in parallel, once we have context wired up. if err := s.storage.StoreSubmission(storedSubmission); err != nil { - return nil, util.LoggingErrorMsg(err, "could not store presentation") + return nil, errors.Wrap(err, "could not store presentation") } opID := fmt.Sprintf("presentations/submissions/%s", storedSubmission.Submission.ID) @@ -187,7 +187,7 @@ func (s Service) CreateSubmission(request CreateSubmissionRequest) (*operation.O Done: false, } if err := s.opsStorage.StoreOperation(storedOp); err != nil { - return nil, util.LoggingErrorMsg(err, "could not store operation") + return nil, errors.Wrap(err, "could not store operation") } return &operation.Operation{ diff --git a/pkg/service/presentation/storage/bolt.go b/pkg/service/presentation/storage/bolt.go index 170571374..2c6b7c7e1 100644 --- a/pkg/service/presentation/storage/bolt.go +++ b/pkg/service/presentation/storage/bolt.go @@ -85,9 +85,7 @@ func (b BoltPresentationStorage) GetSubmission(id string) (*StoredSubmission, er return nil, util.LoggingNewErrorf("could not get submission definition: %s", id) } if len(jsonBytes) == 0 { - err := errors.Wrapf(ErrSubmissionNotFound, "submission definition not found with id: %s", id) - logrus.WithError(err).Error("could not get submission definition from storage") - return nil, err + return nil, util.LoggingErrorMsgf(ErrSubmissionNotFound, "submission not found with id: %s", id) } var stored StoredSubmission if err := json.Unmarshal(jsonBytes, &stored); err != nil { From fd3f1978dd35631adfaa515254b3f422dc05e14d Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Tue, 29 Nov 2022 12:04:53 -0500 Subject: [PATCH 24/27] Upgrade mod --- go.mod | 5 +---- go.sum | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 9ad9fbab0..a17570d00 100644 --- a/go.mod +++ b/go.mod @@ -2,12 +2,9 @@ module github.com/tbd54566975/ssi-service go 1.19 -// TODO(andres): Remove this once https://github.com/TBD54566975/ssi-sdk/pull/260 is merged. -replace github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221128221135-c0926ad9dcb6 => ../ssi-sdk - require ( github.com/BurntSushi/toml v1.2.1 - github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221128221135-c0926ad9dcb6 + github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129164433-9d3e18cca186 github.com/ardanlabs/conf v1.5.0 github.com/dimfeld/httptreemux/v5 v5.5.0 github.com/go-playground/locales v0.14.0 diff --git a/go.sum b/go.sum index b3a123dce..ca6a74e9f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129164433-9d3e18cca186 h1:US/emNy9L9vvHVOhwRsaRdikhQ91lcL2K46ekESZJw0= +github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129164433-9d3e18cca186/go.mod h1:RFja7smnRZpi5OPDPpW35TDK3SbHuAGCHs8wbNTSBxY= github.com/ardanlabs/conf v1.5.0 h1:5TwP6Wu9Xi07eLFEpiCUF3oQXh9UzHMDVnD3u/I5d5c= github.com/ardanlabs/conf v1.5.0/go.mod h1:ILsMo9dMqYzCxDjDXTiwMI0IgxOJd0MOiucbQY2wlJw= github.com/bits-and-blooms/bitset v1.4.0 h1:+YZ8ePm+He2pU3dZlIZiOeAKfrBkXi1lSrXJ/Xzgbu8= From 5becb82a96f110eacd2af7b419c3e6463120d69c Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Tue, 29 Nov 2022 12:21:09 -0500 Subject: [PATCH 25/27] minor improvements --- pkg/jwt/verification.go | 3 +++ pkg/server/router/presentation.go | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/jwt/verification.go b/pkg/jwt/verification.go index 7c65c9a26..4aa629916 100644 --- a/pkg/jwt/verification.go +++ b/pkg/jwt/verification.go @@ -8,6 +8,9 @@ import ( "github.com/tbd54566975/ssi-service/internal/util" ) +// VerifyTokenFromDID verifies that the information in the token was digitally signed by the public key associated with +// the public key of the verification method of the did's document. The passed in resolver is used to map from the did +// to the did document. func VerifyTokenFromDID(did string, token keyaccess.JWT, s *didsdk.Resolver) error { kid, pubKey, err := didint.ResolveKeyForDID(s, did) if err != nil { diff --git a/pkg/server/router/presentation.go b/pkg/server/router/presentation.go index 0f8502fbe..014a9530a 100644 --- a/pkg/server/router/presentation.go +++ b/pkg/server/router/presentation.go @@ -183,7 +183,7 @@ type CreateSubmissionRequest struct { SubmissionJWT keyaccess.JWT `json:"submissionJwt" validate:"required"` } -func (r CreateSubmissionRequest) ToServiceRequest() (*presentation.CreateSubmissionRequest, error) { +func (r CreateSubmissionRequest) toServiceRequest() (*presentation.CreateSubmissionRequest, error) { sdkVp, err := signing.ParseVerifiablePresentationFromJWT(r.SubmissionJWT.String()) if err != nil { return nil, errors.Wrap(err, "parsing presentation from jwt") @@ -245,7 +245,7 @@ func (pr PresentationRouter) CreateSubmission(ctx context.Context, w http.Respon return framework.NewRequestError(util.LoggingErrorMsg(err, "invalid create submission request"), http.StatusBadRequest) } - req, err := request.ToServiceRequest() + req, err := request.toServiceRequest() if err != nil { return framework.NewRequestError(util.LoggingErrorMsg(err, "invalid create submission request"), http.StatusBadRequest) } From 55b5e3ba68e617157ff45d7a50f3acc26a67b140 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Tue, 29 Nov 2022 13:02:52 -0500 Subject: [PATCH 26/27] PR comments. --- go.mod | 2 +- go.sum | 4 ++-- pkg/server/router/presentation.go | 12 +++++----- pkg/server/server_did_test.go | 2 +- pkg/service/operation/storage/bolt.go | 32 +++++++++------------------ pkg/service/presentation/service.go | 4 ++-- 6 files changed, 22 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index a17570d00..2df0190dd 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/BurntSushi/toml v1.2.1 - github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129164433-9d3e18cca186 + github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129172807-92be548d54be github.com/ardanlabs/conf v1.5.0 github.com/dimfeld/httptreemux/v5 v5.5.0 github.com/go-playground/locales v0.14.0 diff --git a/go.sum b/go.sum index ca6a74e9f..fc909af4a 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129164433-9d3e18cca186 h1:US/emNy9L9vvHVOhwRsaRdikhQ91lcL2K46ekESZJw0= -github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129164433-9d3e18cca186/go.mod h1:RFja7smnRZpi5OPDPpW35TDK3SbHuAGCHs8wbNTSBxY= +github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129172807-92be548d54be h1:KK6Dbr1rFPQSAyeWQGRUm7ba1K3XoxAmlFicLeR/6Ek= +github.com/TBD54566975/ssi-sdk v0.0.2-alpha.0.20221129172807-92be548d54be/go.mod h1:RFja7smnRZpi5OPDPpW35TDK3SbHuAGCHs8wbNTSBxY= github.com/ardanlabs/conf v1.5.0 h1:5TwP6Wu9Xi07eLFEpiCUF3oQXh9UzHMDVnD3u/I5d5c= github.com/ardanlabs/conf v1.5.0/go.mod h1:ILsMo9dMqYzCxDjDXTiwMI0IgxOJd0MOiucbQY2wlJw= github.com/bits-and-blooms/bitset v1.4.0 h1:+YZ8ePm+He2pU3dZlIZiOeAKfrBkXi1lSrXJ/Xzgbu8= diff --git a/pkg/server/router/presentation.go b/pkg/server/router/presentation.go index 014a9530a..2c7b88682 100644 --- a/pkg/server/router/presentation.go +++ b/pkg/server/router/presentation.go @@ -184,15 +184,15 @@ type CreateSubmissionRequest struct { } func (r CreateSubmissionRequest) toServiceRequest() (*presentation.CreateSubmissionRequest, error) { - sdkVp, err := signing.ParseVerifiablePresentationFromJWT(r.SubmissionJWT.String()) + sdkVP, err := signing.ParseVerifiablePresentationFromJWT(r.SubmissionJWT.String()) if err != nil { return nil, errors.Wrap(err, "parsing presentation from jwt") } - if err := sdkVp.IsValid(); err != nil { + if err := sdkVP.IsValid(); err != nil { return nil, errors.Wrap(err, "verifying vp validity") } - submissionData, err := json.Marshal(sdkVp.PresentationSubmission) + submissionData, err := json.Marshal(sdkVP.PresentationSubmission) if err != nil { return nil, errors.Wrap(err, "marshalling presentation_submission") } @@ -203,15 +203,15 @@ func (r CreateSubmissionRequest) toServiceRequest() (*presentation.CreateSubmiss if err := s.IsValid(); err != nil { return nil, errors.Wrap(err, "verifying submission validity") } - sdkVp.PresentationSubmission = s + sdkVP.PresentationSubmission = s - credContainers, err := credint.NewCredentialContainerFromArray(sdkVp.VerifiableCredential) + credContainers, err := credint.NewCredentialContainerFromArray(sdkVP.VerifiableCredential) if err != nil { return nil, errors.Wrap(err, "parsing verifiable credential array") } return &presentation.CreateSubmissionRequest{ - Presentation: *sdkVp, + Presentation: *sdkVP, SubmissionJWT: r.SubmissionJWT, Submission: s, Credentials: credContainers}, nil diff --git a/pkg/server/server_did_test.go b/pkg/server/server_did_test.go index 25a512040..4733cd22e 100644 --- a/pkg/server/server_did_test.go +++ b/pkg/server/server_did_test.go @@ -298,7 +298,7 @@ func TestDIDAPI(t *testing.T) { } err = didService.ResolveDID(newRequestContextWithParams(badParams), w, req) assert.Error(tt, err) - assert.Contains(tt, err.Error(), "did:key:abcd: selected encoding not supported") + assert.Contains(tt, err.Error(), "selected encoding not supported") w.Flush() diff --git a/pkg/service/operation/storage/bolt.go b/pkg/service/operation/storage/bolt.go index d86174d99..b74d0caa0 100644 --- a/pkg/service/operation/storage/bolt.go +++ b/pkg/service/operation/storage/bolt.go @@ -1,7 +1,6 @@ package storage import ( - "fmt" "github.com/goccy/go-json" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -20,15 +19,11 @@ type BoltOperationStorage struct { func (b BoltOperationStorage) StoreOperation(op StoredOperation) error { id := op.ID if id == "" { - err := errors.New("ID is required for storing operations") - logrus.WithError(err).Error() - return err + return util.LoggingNewError("ID is required for storing operations") } jsonBytes, err := json.Marshal(op) if err != nil { - errMsg := fmt.Sprintf("marshalling operation with id: %s", id) - logrus.WithError(err).Error(errMsg) - return errors.Wrapf(err, errMsg) + return util.LoggingErrorMsgf(err, "marshalling operation with id: %s", id) } return b.db.Write(namespace, id, jsonBytes) } @@ -36,20 +31,14 @@ func (b BoltOperationStorage) StoreOperation(op StoredOperation) error { func (b BoltOperationStorage) GetOperation(id string) (*StoredOperation, error) { jsonBytes, err := b.db.Read(namespace, id) if err != nil { - errMsg := fmt.Sprintf("reading operation with id: %s", id) - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrapf(err, errMsg) + return nil, util.LoggingErrorMsgf(err, "reading operation with id: %s", id) } if len(jsonBytes) == 0 { - err := fmt.Errorf("operation not found with id: %s", id) - logrus.WithError(err).Error("found empty bytes") - return nil, err + return nil, util.LoggingNewErrorf("operation not found with id: %s", id) } var stored StoredOperation if err := json.Unmarshal(jsonBytes, &stored); err != nil { - errMsg := fmt.Sprintf("unmarshalling stored operation: %s", id) - logrus.WithError(err).Error(errMsg) - return nil, errors.Wrapf(err, errMsg) + return nil, util.LoggingErrorMsgf(err, "unmarshalling stored operation: %s", id) } return &stored, nil } @@ -57,16 +46,15 @@ func (b BoltOperationStorage) GetOperation(id string) (*StoredOperation, error) func (b BoltOperationStorage) GetOperations() ([]StoredOperation, error) { operations, err := b.db.ReadAll(namespace) if err != nil { - errMsg := "reading all from db" - logrus.WithError(err).Error("could not get all operations") - return nil, errors.Wrap(err, errMsg) + return nil, util.LoggingErrorMsgf(err, "could not get all operations") } stored := make([]StoredOperation, 0, len(operations)) - for _, manifestBytes := range operations { + for i, manifestBytes := range operations { var nextOp StoredOperation - if err = json.Unmarshal(manifestBytes, &nextOp); err == nil { - stored = append(stored, nextOp) + if err = json.Unmarshal(manifestBytes, &nextOp); err != nil { + logrus.WithError(err).WithField("idx", i).Warnf("Skipping operation") } + stored = append(stored, nextOp) } return stored, nil } diff --git a/pkg/service/presentation/service.go b/pkg/service/presentation/service.go index b51cf8566..118103104 100644 --- a/pkg/service/presentation/service.go +++ b/pkg/service/presentation/service.go @@ -136,11 +136,11 @@ func (s Service) CreateSubmission(request CreateSubmissionRequest) (*operation.O return nil, errors.Wrap(err, "provided value is not a valid presentation submission") } - sdkVp, err := signing.ParseVerifiablePresentationFromJWT(request.SubmissionJWT.String()) + sdkVP, err := signing.ParseVerifiablePresentationFromJWT(request.SubmissionJWT.String()) if err != nil { return nil, errors.Wrap(err, "parsing vp from jwt") } - if err := jwt.VerifyTokenFromDID(sdkVp.Holder, request.SubmissionJWT, s.resolver); err != nil { + if err := jwt.VerifyTokenFromDID(sdkVP.Holder, request.SubmissionJWT, s.resolver); err != nil { return nil, errors.Wrap(err, "verifying token from did") } From b5079270250a1b0fae5f9388822b44fd74384166 Mon Sep 17 00:00:00 2001 From: Andres Uribe Gonzalez Date: Tue, 29 Nov 2022 13:04:39 -0500 Subject: [PATCH 27/27] PR comments. --- pkg/server/router/presentation.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/server/router/presentation.go b/pkg/server/router/presentation.go index 2c7b88682..0111c5a2f 100644 --- a/pkg/server/router/presentation.go +++ b/pkg/server/router/presentation.go @@ -242,17 +242,20 @@ type OperationResult struct { func (pr PresentationRouter) CreateSubmission(ctx context.Context, w http.ResponseWriter, r *http.Request) error { var request CreateSubmissionRequest if err := framework.Decode(r, &request); err != nil { - return framework.NewRequestError(util.LoggingErrorMsg(err, "invalid create submission request"), http.StatusBadRequest) + return framework.NewRequestError( + util.LoggingErrorMsg(err, "invalid create submission request"), http.StatusBadRequest) } req, err := request.toServiceRequest() if err != nil { - return framework.NewRequestError(util.LoggingErrorMsg(err, "invalid create submission request"), http.StatusBadRequest) + return framework.NewRequestError( + util.LoggingErrorMsg(err, "invalid create submission request"), http.StatusBadRequest) } operation, err := pr.service.CreateSubmission(*req) if err != nil { - return framework.NewRequestError(util.LoggingErrorMsg(err, "cannot create submission"), http.StatusInternalServerError) + return framework.NewRequestError( + util.LoggingErrorMsg(err, "cannot create submission"), http.StatusInternalServerError) } resp := Operation{