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

Commit

Permalink
feat: suppor path_nested in presex.
Browse files Browse the repository at this point in the history
Signed-off-by: Volodymyr Kubiv <volodymyr.kubiv@euristiq.com>
  • Loading branch information
vkubiv committed Oct 25, 2022
1 parent 368f53b commit 6dd3aa9
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 11 deletions.
44 changes: 34 additions & 10 deletions pkg/doc/presexch/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ func (pd *PresentationDefinition) Match(vp *verifiable.Presentation, // nolint:g
return nil, fmt.Errorf("failed to parse descriptor map: %w", err)
}

builder := gval.Full(jsonpath.PlaceholderExtension())
result := make(map[string]*verifiable.Credential)

for i := range descriptorMap {
Expand All @@ -121,12 +120,9 @@ func (pd *PresentationDefinition) Match(vp *verifiable.Presentation, // nolint:g
descriptorMapProperty, mapping.ID)
}

// TODO need to revisit this logic
mapping = pd.getPathNestedIfExists(mapping)

vc, selectErr := selectByPath(builder, typelessVP, mapping.Path, opts)
vc, selectErr := selectVC(typelessVP, mapping, opts)
if selectErr != nil {
return nil, fmt.Errorf("failed to select vc from submission: %w", selectErr)
return nil, selectErr
}

inputDescriptor := pd.inputDescriptor(mapping.ID)
Expand All @@ -151,12 +147,40 @@ func (pd *PresentationDefinition) Match(vp *verifiable.Presentation, // nolint:g
return result, nil
}

func (pd *PresentationDefinition) getPathNestedIfExists(mapping *InputDescriptorMapping) *InputDescriptorMapping {
if mapping.PathNested != nil {
return pd.getPathNestedIfExists(mapping.PathNested)
func selectVC(typelessVerifiable interface{},
mapping *InputDescriptorMapping, opts *MatchOptions) (*verifiable.Credential, error) {
builder := gval.Full(jsonpath.PlaceholderExtension())

var vc *verifiable.Credential

var err error

for {
vc, err = selectByPath(builder, typelessVerifiable, mapping.Path, opts)
if err != nil {
return nil, fmt.Errorf("failed to select vc from submission: %w", err)
}

if mapping.PathNested == nil {
break
}

mapping = mapping.PathNested

var vcBytes []byte

vcBytes, err = vc.MarshalJSON()
if err != nil {
return nil, fmt.Errorf("failed to marshal vc: %w", err)
}

err = json.Unmarshal(vcBytes, &typelessVerifiable)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal vc: %w", err)
}
}

return mapping
return vc, nil
}

// Ensures the matched credentials meet the submission requirements.
Expand Down
147 changes: 146 additions & 1 deletion pkg/doc/presexch/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestPresentationDefinition_Match(t *testing.T) {
t.Run("match one credential", func(t *testing.T) {
uri := randomURI()

customType := "CustomType"
customType := "CustomType" //nolint: goconst

expected := newVC([]string{uri})
expected.Types = append(expected.Types, customType)
Expand Down Expand Up @@ -65,6 +65,94 @@ func TestPresentationDefinition_Match(t *testing.T) {
require.Equal(t, expected.ID, result.ID)
})

t.Run("match one nested credential", func(t *testing.T) {
uri := randomURI()

customType := "CustomType"

expectedNested := newVC([]string{uri})
expectedNested.Types = append(expectedNested.Types, customType)
expectedNested.ID = "http://test.credential.com/123456"

expected := newVCWithCustomFld([]string{uri}, "nestedVC", expectedNested)
expected.Types = append(expected.Types, customType)

defs := &PresentationDefinition{
InputDescriptors: []*InputDescriptor{{
ID: uuid.New().String(),
Schema: []*Schema{{
URI: fmt.Sprintf("%s#%s", uri, customType),
}},
}},
}

docLoader := createTestDocumentLoader(t, uri, customType)

matched, err := defs.Match(newVP(t,
&PresentationSubmission{DescriptorMap: []*InputDescriptorMapping{{
ID: defs.InputDescriptors[0].ID,
Path: "$.verifiableCredential[0]",
PathNested: &InputDescriptorMapping{
ID: defs.InputDescriptors[0].ID,
Path: "$.nestedVC",
},
}}},
expected,
), docLoader, WithCredentialOptions(verifiable.WithJSONLDDocumentLoader(docLoader)))
require.NoError(t, err)
require.Len(t, matched, 1)
result, ok := matched[defs.InputDescriptors[0].ID]
require.True(t, ok)
require.Equal(t, expectedNested.ID, result.ID)
})

t.Run("match one nested jwt credential", func(t *testing.T) {
uri := randomURI()
contextLoader := createTestDocumentLoader(t, uri)
agent := newAgent(t)

customType := "CustomType"

expectedNested := newSignedJWTVC(t, agent, []string{uri})

expected := newVCWithCustomFld([]string{uri}, "nestedVC", expectedNested)
expected.Types = append(expected.Types, customType)
expected.ID = "http://test.credential.com/123456"

defs := &PresentationDefinition{
InputDescriptors: []*InputDescriptor{{
ID: uuid.New().String(),
Schema: []*Schema{{
URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType),
}},
}},
}

docLoader := createTestDocumentLoader(t, uri, customType)

matched, err := defs.Match(newVP(t,
&PresentationSubmission{DescriptorMap: []*InputDescriptorMapping{{
ID: defs.InputDescriptors[0].ID,
Path: "$.verifiableCredential[0]",
PathNested: &InputDescriptorMapping{
ID: defs.InputDescriptors[0].ID,
Path: "$.nestedVC",
},
}}},
expected,
), docLoader,
WithCredentialOptions(
verifiable.WithJSONLDDocumentLoader(contextLoader),
verifiable.WithPublicKeyFetcher(verifiable.NewVDRKeyResolver(agent.VDRegistry()).PublicKeyFetcher()),
),
)
require.NoError(t, err)
require.Len(t, matched, 1)
result, ok := matched[defs.InputDescriptors[0].ID]
require.True(t, ok)
require.Equal(t, expectedNested.ID, result.ID)
})

t.Run("match one signed credential", func(t *testing.T) {
uri := randomURI()
contextLoader := createTestDocumentLoader(t, uri)
Expand Down Expand Up @@ -394,6 +482,30 @@ func newVC(ctx []string) *verifiable.Credential {
return vc
}

func newVCWithCustomFld(ctx []string, fldName string, fld interface{}) *verifiable.Credential {
vc := &verifiable.Credential{
Context: []string{verifiable.ContextURI},
Types: []string{verifiable.VCType},
ID: "http://test.credential.com/123",
Issuer: verifiable.Issuer{ID: "http://test.issuer.com"},
Issued: &util.TimeWrapper{
Time: time.Now(),
},
Subject: map[string]interface{}{
"id": uuid.New().String(),
},
CustomFields: map[string]interface{}{
fldName: fld,
},
}

if ctx != nil {
vc.Context = append(vc.Context, ctx...)
}

return vc
}

func newSignedVC(t *testing.T,
agent *context.Provider, ctx []string, ctxLoader jsonld.DocumentLoader) *verifiable.Credential {
t.Helper()
Expand Down Expand Up @@ -428,6 +540,39 @@ func newSignedVC(t *testing.T,
return vc
}

func newSignedJWTVC(t *testing.T,
agent *context.Provider, ctx []string) *verifiable.Credential {
t.Helper()

vc := newVC(ctx)

keyID, kh, err := agent.KMS().Create(kms.ED25519Type)
require.NoError(t, err)

signer := suite.NewCryptoSigner(agent.Crypto(), kh)

pubKey, kt, err := agent.KMS().ExportPubKeyBytes(keyID)
require.NoError(t, err)
require.Equal(t, kms.ED25519Type, kt)

issuer, verMethod := fingerprint.CreateDIDKeyByCode(fingerprint.ED25519PubKeyMultiCodec, pubKey)

vc.Issuer = verifiable.Issuer{ID: issuer}

claims, err := vc.JWTClaims(false)
require.NoError(t, err)

jwsAlgo, err := verifiable.KeyTypeToJWSAlgo(kms.ED25519Type)
require.NoError(t, err)

jws, err := claims.MarshalJWS(jwsAlgo, signer, verMethod)
require.NoError(t, err)

vc.JWT = jws

return vc
}

func newVP(t *testing.T, submission *PresentationSubmission, vcs ...*verifiable.Credential) *verifiable.Presentation {
vp, err := verifiable.NewPresentation(verifiable.WithCredentials(vcs...))
require.NoError(t, err)
Expand Down

0 comments on commit 6dd3aa9

Please sign in to comment.