From 3919b0f7ab97933796ccd66c31c5cc473cc3870f Mon Sep 17 00:00:00 2001 From: talwinder50 Date: Fri, 21 Jan 2022 12:57:29 -0500 Subject: [PATCH] feat: [WACI-Issuance] Support to read credential manifest closes #561 Signed-off-by: talwinder50 --- cmd/adapter-rest/startcmd/start.go | 7 +- pkg/profile/issuer/profile.go | 3 +- pkg/profile/issuer/profile_test.go | 6 ++ pkg/restapi/issuer/operation/operations.go | 34 +++++++-- .../issuer/operation/operations_test.go | 74 +++++++++++++++++-- .../manifest-config/outputdescriptors.json | 2 +- 6 files changed, 106 insertions(+), 20 deletions(-) diff --git a/cmd/adapter-rest/startcmd/start.go b/cmd/adapter-rest/startcmd/start.go index 6b32f7db..2a07ef67 100644 --- a/cmd/adapter-rest/startcmd/start.go +++ b/cmd/adapter-rest/startcmd/start.go @@ -835,8 +835,8 @@ func addIssuerHandlers(parameters *adapterRestParameters, framework *aries.Aries if err != nil { return fmt.Errorf("aries-framework - failed to get aries context : %w", err) } - // TODO #572 Pass the output descriptors to issuer - _, err = readCMOutputDescriptorFile(parameters.cmOutputDescriptorsFilePath) + + CMOutputDescriptor, err := readCMOutputDescriptorFile(parameters.cmOutputDescriptorsFilePath) if err != nil { return fmt.Errorf("failed to read and validate manifest output descriptors : %w", err) } @@ -857,7 +857,7 @@ func addIssuerHandlers(parameters *adapterRestParameters, framework *aries.Aries if err != nil { return fmt.Errorf("failed to init trustbloc did creator: %w", err) } - // TODO #572 Pass the output descriptors to issuer + // TODO #575 Persist the manifest output descriptor in database // add issuer endpoints issuerService, err := issuer.New(&issuerops.Config{ AriesCtx: ariesCtx, @@ -872,6 +872,7 @@ func addIssuerHandlers(parameters *adapterRestParameters, framework *aries.Aries ExternalURL: parameters.externalURL, DidDomain: parameters.trustblocDomain, JSONLDDocumentLoader: ariesCtx.JSONLDDocumentLoader(), + CmOutputDescriptor: CMOutputDescriptor, }) if err != nil { return fmt.Errorf("failed to init issuer ops: %w", err) diff --git a/pkg/profile/issuer/profile.go b/pkg/profile/issuer/profile.go index 7130d023..6cbb36ae 100644 --- a/pkg/profile/issuer/profile.go +++ b/pkg/profile/issuer/profile.go @@ -12,6 +12,7 @@ import ( "fmt" "time" + "github.com/hyperledger/aries-framework-go/pkg/doc/cm" "github.com/hyperledger/aries-framework-go/spi/storage" "github.com/trustbloc/edge-adapter/pkg/internal/common/adapterutil" @@ -45,7 +46,7 @@ type ProfileData struct { OIDCClientParams *OIDCClientParams `json:"oidcParams,omitempty"` CredentialScopes []string `json:"credScopes,omitempty"` LinkedWalletURL string `json:"linkedWallet,omitempty"` - // Todo #issue Add credential manifest issuer object + CredentialManifestIssuer cm.Issuer `json:"issuer,omitempty"` } // OIDCClientParams optional set of oidc client parameters that the issuer may set, for static client registration. diff --git a/pkg/profile/issuer/profile_test.go b/pkg/profile/issuer/profile_test.go index 7974fa2d..6525a969 100644 --- a/pkg/profile/issuer/profile_test.go +++ b/pkg/profile/issuer/profile_test.go @@ -11,8 +11,10 @@ import ( "errors" "testing" + "github.com/google/uuid" "github.com/hyperledger/aries-framework-go/component/storageutil/mem" mockstorage "github.com/hyperledger/aries-framework-go/component/storageutil/mock" + "github.com/hyperledger/aries-framework-go/pkg/doc/cm" "github.com/hyperledger/aries-framework-go/spi/storage" "github.com/stretchr/testify/require" ) @@ -53,6 +55,10 @@ func TestCredentialRecord_SaveProfile(t *testing.T) { Name: "Issuer Profile 1", SupportedVCContexts: []string{"https://w3id.org/citizenship/v3"}, URL: "http://issuer.example.com", + CredentialManifestIssuer: cm.Issuer{ + ID: uuid.New().String(), + Name: "Example University", + }, } err = record.SaveProfile(value) diff --git a/pkg/restapi/issuer/operation/operations.go b/pkg/restapi/issuer/operation/operations.go index cce5dbbf..d7f170c1 100644 --- a/pkg/restapi/issuer/operation/operations.go +++ b/pkg/restapi/issuer/operation/operations.go @@ -104,6 +104,7 @@ const ( // WACI interaction constants credentialManifestFormat = "dif/credential-manifest/manifest@v1.0" + credentialManifestVersion = "1.0.0" credentialFulfillmentFormat = "dif/credential-manifest/fulfillment@v1.0" offerCredentialAttachMediaType = "application/json" redirectStatusOK = "OK" @@ -155,6 +156,7 @@ type Config struct { ExternalURL string DidDomain string JSONLDDocumentLoader ld.DocumentLoader + CmOutputDescriptor map[string][]*cm.OutputDescriptor } // New returns issuer rest instance. @@ -342,6 +344,7 @@ type Operation struct { getOIDCClientFunc func(string, string) (oidcClient, error) didDomain string jsonldDocLoader ld.DocumentLoader + cmOutputDescriptor map[string][]*cm.OutputDescriptor } // GetRESTHandlers get all controller API handler available for this service. @@ -1290,8 +1293,13 @@ func (o *Operation) handleProposeCredential(msg service.DIDCommAction) (issuecre } } - // get manifest - manifest := o.readCredentialManifest() + txn, err := o.getTxn(userInvMap.TxID) + if err != nil { + return nil, fmt.Errorf("failed to get trasaction data: %w", err) + } + + // read credential manifest + manifest := o.readCredentialManifest(profile, txn.CredScope) // get unsigned credential vc, err := o.createCredential(getUserDataURL(profile.URL), userInvMap.TxToken, oauthToken, @@ -1728,12 +1736,24 @@ func (o *Operation) hanlDIDExStateMsg(msg service.StateMsg) error { return nil } -// read credential manifest from profile URL endpoints -// TODO for now returning empty manifest, TO BE IMPLEMENTED [issue##561 & issue#563] -func (o *Operation) readCredentialManifest() *cm.CredentialManifest { - return &cm.CredentialManifest{ - ID: uuid.NewString(), +/* +read credential manifest issuer detail from persisted profile data and scope +from the persisted transaction cred scope. +*/ +// TODO issue#561 Add credential manifest presentation definition +func (o *Operation) readCredentialManifest(profileData *issuer.ProfileData, txnCredScope string) *cm.CredentialManifest { + for scope, cmDesciptor := range o.cmOutputDescriptor { + if scope == txnCredScope { + return &cm.CredentialManifest{ + ID: uuid.NewString(), + Version: credentialManifestVersion, + Issuer: profileData.CredentialManifestIssuer, + OutputDescriptors: cmDesciptor, + } + } } + + return nil } func prepareOfferCredentialMessage(manifest *cm.CredentialManifest, fulfillment *verifiable.Presentation) *issuecredsvc.OfferCredentialParams { // nolint:lll diff --git a/pkg/restapi/issuer/operation/operations_test.go b/pkg/restapi/issuer/operation/operations_test.go index 3b06f956..2e3608be 100644 --- a/pkg/restapi/issuer/operation/operations_test.go +++ b/pkg/restapi/issuer/operation/operations_test.go @@ -61,6 +61,7 @@ const ( inviteeDID = "did:example:0d76fa4e1386" inviterDID = "did:example:e6025bfdbb8f" mockOIDCProvider = "mock.provider.local" + mockCredScope = "prc" ) func TestNew(t *testing.T) { @@ -3454,22 +3455,48 @@ func TestWACIIssuanceHandler(t *testing.T) { ConnIDByDIDs: connID, } + c.cmOutputDescriptor = map[string][]*cm.OutputDescriptor{ + mockCredScope: { + &cm.OutputDescriptor{ + ID: uuid.New().String(), + Schema: "https://www.w3.org/2018/credentials/examples/v1", + }, + }, + } + invitationID := uuid.New().String() issuerID := uuid.New().String() profile := createProfileData(issuerID) profile.SupportsWACI = true + profile.CredentialManifestIssuer = cm.Issuer{ + ID: uuid.New().String(), + Name: "Example University", + } err = c.profileStore.SaveProfile(profile) require.NoError(t, err) - err = c.storeUserInvitationMapping(&UserInvitationMapping{ + usrInvitationMapping := &UserInvitationMapping{ InvitationID: invitationID, IssuerID: issuerID, + TxID: uuid.New().String(), TxToken: uuid.New().String(), - }) + } + + err = c.storeUserInvitationMapping(usrInvitationMapping) require.NoError(t, err) + txDataSample := &txnData{ + IssuerID: profile.ID, + CredScope: mockCredScope, + } + + tdByte, err := json.Marshal(txDataSample) + require.NoError(t, err) + + c.txnStore.Put(usrInvitationMapping.TxID, tdByte) + go c.didCommActionListener(actionCh) done := make(chan struct{}) @@ -3502,20 +3529,44 @@ func TestWACIIssuanceHandler(t *testing.T) { invitationID := uuid.New().String() issuerID := uuid.New().String() - + c.cmOutputDescriptor = map[string][]*cm.OutputDescriptor{ + mockCredScope: { + &cm.OutputDescriptor{ + ID: uuid.New().String(), + Schema: "https://www.w3.org/2018/credentials/examples/v1", + }, + }, + } profile := createProfileData(issuerID) profile.SupportsWACI = true + profile.CredentialManifestIssuer = cm.Issuer{ + ID: uuid.New().String(), + Name: "Example University", + } err = c.profileStore.SaveProfile(profile) require.NoError(t, err) - err = c.storeUserInvitationMapping(&UserInvitationMapping{ + usrInvitationMapping := &UserInvitationMapping{ InvitationID: invitationID, IssuerID: issuerID, + TxID: uuid.New().String(), TxToken: uuid.New().String(), - }) + } + + err = c.storeUserInvitationMapping(usrInvitationMapping) + require.NoError(t, err) + + txDataSample := &txnData{ + IssuerID: profile.ID, + CredScope: mockCredScope, + } + + tdByte, err := json.Marshal(txDataSample) require.NoError(t, err) + c.txnStore.Put(usrInvitationMapping.TxID, tdByte) + go c.didCommActionListener(actionCh) // credential data error @@ -3603,18 +3654,23 @@ func TestWACIIssuanceHandler(t *testing.T) { InvitationID: newInvitationID, }), "failed to get OIDC access token for WACI transaction") - // token store put error newInvitationID = uuid.New().String() issuerID = uuid.New().String() - err = c.storeUserInvitationMapping(&UserInvitationMapping{ + usrInvitationMapping = &UserInvitationMapping{ InvitationID: newInvitationID, IssuerID: issuerID, + TxID: usrInvitationMapping.TxID, TxToken: uuid.New().String(), - }) + } + err = c.storeUserInvitationMapping(usrInvitationMapping) require.NoError(t, err) profile = createProfileData(issuerID) profile.SupportsWACI = true + profile.CredentialManifestIssuer = cm.Issuer{ + ID: uuid.New().String(), + Name: "Example University", + } err = c.profileStore.SaveProfile(profile) require.NoError(t, err) @@ -3629,6 +3685,8 @@ func TestWACIIssuanceHandler(t *testing.T) { errPut: errors.New("error inserting data"), } + c.txnStore.Put(usrInvitationMapping.TxID, tdByte) + c.httpClient = &mockHTTPClient{ respValue: &http.Response{ StatusCode: http.StatusOK, diff --git a/test/bdd/fixtures/adapter-rest/manifest-config/outputdescriptors.json b/test/bdd/fixtures/adapter-rest/manifest-config/outputdescriptors.json index 8e7050ad..61d5890b 100644 --- a/test/bdd/fixtures/adapter-rest/manifest-config/outputdescriptors.json +++ b/test/bdd/fixtures/adapter-rest/manifest-config/outputdescriptors.json @@ -69,7 +69,7 @@ } } ], - "prc": [ + "PermanentResidentCard": [ { "id": "prc_output", "schema": "https://w3id.org/citizenship/v1",