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

Commit 52cdf1a

Browse files
authored
wip: Service type property MUST be a string or a set of strings. (#3389)
Service type property MUST be a string or a set of strings. Signed-off-by: Sandra Vrtikapa <sandra.vrtikapa@securekey.com> Signed-off-by: Sandra Vrtikapa <sandra.vrtikapa@securekey.com>
1 parent 2fa20ab commit 52cdf1a

File tree

11 files changed

+216
-18
lines changed

11 files changed

+216
-18
lines changed

pkg/didcomm/protocol/didexchange/didex_interop.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build ACAPyInterop
12
// +build ACAPyInterop
23

34
/*
@@ -58,8 +59,11 @@ func convertPeerToSov(doc *did.Doc) (*did.Doc, error) {
5859
}
5960

6061
func interopRecipientKey(doc *did.Doc) (string, error) {
61-
if doc.Service[0].Type == "IndyAgent" {
62+
serviceType := didcommutil.GetServiceType(doc.Service[0].Type)
63+
64+
if serviceType == "IndyAgent" {
6265
return recipientKey(doc)
6366
}
67+
6468
return "", fmt.Errorf("recipientKeyAsDIDKey: invalid DID Doc service type: '%v'", doc.Service[0].Type)
6569
}

pkg/didcomm/protocol/didexchange/states.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/hyperledger/aries-framework-go/pkg/didcomm/transport"
2525
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
2626
vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
27+
"github.com/hyperledger/aries-framework-go/pkg/internal/didcommutil"
2728
"github.com/hyperledger/aries-framework-go/pkg/kms"
2829
"github.com/hyperledger/aries-framework-go/pkg/kms/localkms"
2930
connectionstore "github.com/hyperledger/aries-framework-go/pkg/store/connection"
@@ -455,7 +456,7 @@ func (ctx *context) handleInboundRequest(request *Request, options *options,
455456

456457
var serviceType string
457458
if len(requestDidDoc.Service) > 0 {
458-
serviceType = requestDidDoc.Service[0].Type
459+
serviceType = didcommutil.GetServiceType(requestDidDoc.Service[0].Type)
459460
} else {
460461
accept, e := destination.ServiceEndpoint.Accept()
461462
if e != nil {
@@ -713,6 +714,7 @@ func (ctx *context) getMyDIDDoc(pubDID string, routerConnections []string, servi
713714
}
714715
case didCommV2ServiceType:
715716
svc = did.Service{
717+
Type: didCommV2ServiceType,
716718
ServiceEndpoint: model.NewDIDCommV2Endpoint([]model.DIDCommV2Endpoint{
717719
{URI: serviceEndpoint, RoutingKeys: routingKeys},
718720
}),
@@ -736,7 +738,7 @@ func (ctx *context) getMyDIDDoc(pubDID string, routerConnections []string, servi
736738
}
737739

738740
if newService {
739-
switch newDID.Service[0].Type {
741+
switch didcommutil.GetServiceType(newDID.Service[0].Type) {
740742
case didCommServiceType, "IndyAgent":
741743
recKey, _ := fingerprint.CreateDIDKey(newDID.VerificationMethod[0].Value)
742744
newDID.Service[0].RecipientKeys = []string{recKey}
@@ -748,6 +750,9 @@ func (ctx *context) getMyDIDDoc(pubDID string, routerConnections []string, servi
748750
}
749751

750752
newDID.Service[0].RecipientKeys = recKeys
753+
754+
default:
755+
return nil, fmt.Errorf("getMyDIDDoc: invalid DID Doc service type: '%v'", newDID.Service[0].Type)
751756
}
752757
}
753758

@@ -1123,6 +1128,8 @@ func (ctx *context) getServiceBlock(i *OOBInvitation) (*did.Service, error) {
11231128
// RFC0587: In case the accept property is set in both the DID service block and the out-of-band message,
11241129
// the out-of-band property takes precedence.
11251130
if isDIDCommV2(i.MediaTypeProfiles) {
1131+
block.Type = didCommV2ServiceType
1132+
11261133
uri, err := block.ServiceEndpoint.URI()
11271134
if err != nil {
11281135
logger.Debugf("block ServiceEndpoint URI empty for DIDcomm V2, skipping it.")
@@ -1137,6 +1144,7 @@ func (ctx *context) getServiceBlock(i *OOBInvitation) (*did.Service, error) {
11371144
{URI: uri, Accept: i.MediaTypeProfiles, RoutingKeys: routingKeys},
11381145
})
11391146
} else {
1147+
block.Type = didCommServiceType
11401148
block.Accept = i.MediaTypeProfiles
11411149
}
11421150
}
@@ -1251,7 +1259,9 @@ func recipientKeyAsDIDKey(doc *did.Doc) (string, error) {
12511259
err error
12521260
)
12531261

1254-
switch doc.Service[0].Type {
1262+
serviceType := didcommutil.GetServiceType(doc.Service[0].Type)
1263+
1264+
switch serviceType {
12551265
case vdrapi.DIDCommServiceType:
12561266
return recipientKey(doc)
12571267
case vdrapi.DIDCommV2ServiceType:

pkg/didcomm/protocol/didexchange/states_test.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,7 +1828,7 @@ func TestGetDIDDocAndConnection(t *testing.T) {
18281828
keyType: kms.ED25519Type,
18291829
keyAgreementType: kms.X25519ECDHKWType,
18301830
}
1831-
didDoc, err := ctx.getMyDIDDoc("", nil, "")
1831+
didDoc, err := ctx.getMyDIDDoc("", nil, didCommServiceType)
18321832
require.Error(t, err)
18331833
require.Contains(t, err.Error(), "creator error")
18341834
require.Nil(t, didDoc)
@@ -1848,10 +1848,11 @@ func TestGetDIDDocAndConnection(t *testing.T) {
18481848
keyType: kms.ED25519Type,
18491849
keyAgreementType: kms.X25519ECDHKWType,
18501850
}
1851-
didDoc, err := ctx.getMyDIDDoc("", nil, "")
1851+
didDoc, err := ctx.getMyDIDDoc("", nil, didCommV2ServiceType)
18521852
require.NoError(t, err)
18531853
require.NotNil(t, didDoc)
18541854
})
1855+
18551856
t.Run("successfully created peer did with didcomm V2 service bloc", func(t *testing.T) {
18561857
connRec, err := connection.NewRecorder(&protocol.MockProvider{})
18571858
require.NoError(t, err)
@@ -1910,6 +1911,27 @@ func TestGetDIDDocAndConnection(t *testing.T) {
19101911
require.Contains(t, err.Error(), "did doc - add key to the router")
19111912
require.Nil(t, didDoc)
19121913
})
1914+
1915+
t.Run("error - invalid service type", func(t *testing.T) {
1916+
connRec, err := connection.NewRecorder(&protocol.MockProvider{})
1917+
require.NoError(t, err)
1918+
didConnStore, err := didstore.NewConnectionStore(&protocol.MockProvider{})
1919+
require.NoError(t, err)
1920+
customKMS := newKMS(t, mockstorage.NewMockStoreProvider())
1921+
ctx := context{
1922+
kms: customKMS,
1923+
vdRegistry: &mockvdr.MockVDRegistry{CreateValue: mockdiddoc.GetMockDIDDoc(t, false)},
1924+
connectionRecorder: connRec,
1925+
connectionStore: didConnStore,
1926+
routeSvc: &mockroute.MockMediatorSvc{},
1927+
keyType: kms.ED25519Type,
1928+
keyAgreementType: kms.X25519ECDHKWType,
1929+
}
1930+
didDoc, err := ctx.getMyDIDDoc("", nil, "")
1931+
require.Error(t, err)
1932+
require.Nil(t, didDoc)
1933+
require.Contains(t, err.Error(), "getMyDIDDoc: invalid DID Doc service type: ''")
1934+
})
19131935
}
19141936

19151937
const sovDoc = `{

pkg/didcomm/protocol/legacyconnection/states.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
2828
"github.com/hyperledger/aries-framework-go/pkg/doc/util/kmsdidkey"
2929
vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
30+
"github.com/hyperledger/aries-framework-go/pkg/internal/didcommutil"
3031
"github.com/hyperledger/aries-framework-go/pkg/internal/didkeyutil"
3132
"github.com/hyperledger/aries-framework-go/pkg/kms"
3233
"github.com/hyperledger/aries-framework-go/pkg/kms/localkms"
@@ -337,7 +338,7 @@ func (ctx *context) handleInboundRequest(request *Request, options *options,
337338

338339
var serviceType string
339340
if len(requestDidDoc.Service) > 0 {
340-
serviceType = requestDidDoc.Service[0].Type
341+
serviceType = didcommutil.GetServiceType(requestDidDoc.Service[0].Type)
341342
} else {
342343
serviceType = legacyDIDCommServiceType
343344
}
@@ -579,7 +580,7 @@ func (ctx *context) getMyDIDDoc(pubDID string, routerConnections []string, servi
579580
}
580581

581582
if newService {
582-
switch newDID.Service[0].Type {
583+
switch didcommutil.GetServiceType(newDID.Service[0].Type) {
583584
case didCommServiceType, legacyDIDCommServiceType:
584585
newDID.Service[0].RecipientKeys = []string{base58.Encode(newDID.VerificationMethod[0].Value)}
585586
default:
@@ -832,7 +833,9 @@ func isDIDCommV2(mediaTypeProfiles []string) bool {
832833

833834
// returns the did:key ID of the first element in the doc's destination RecipientKeys.
834835
func recipientKey(doc *did.Doc) (string, error) {
835-
switch doc.Service[0].Type {
836+
serviceType := didcommutil.GetServiceType(doc.Service[0].Type)
837+
838+
switch serviceType {
836839
case vdrapi.DIDCommServiceType, legacyDIDCommServiceType:
837840
dest, err := service.CreateDestination(doc)
838841
if err != nil {

pkg/doc/did/doc.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ func (pk *VerificationMethod) JSONWebKey() *jwk.JWK {
373373
// Service DID doc service.
374374
type Service struct {
375375
ID string `json:"id"`
376-
Type string `json:"type"`
376+
Type interface{} `json:"type"`
377377
Priority uint `json:"priority,omitempty"`
378378
RecipientKeys []string `json:"recipientKeys,omitempty"`
379379
RoutingKeys []string `json:"routingKeys,omitempty"`
@@ -700,12 +700,15 @@ func populateServices(didID, baseURI string, rawServices []map[string]interface{
700700
}
701701

702702
service := Service{
703-
ID: id, Type: stringEntry(rawService[jsonldType]), relativeURL: isRelative,
703+
ID: id,
704+
Type: rawService[jsonldType],
705+
relativeURL: isRelative,
704706
ServiceEndpoint: sp,
705707
RecipientKeys: recipientKeys,
706708
Priority: uintEntry(rawService[jsonldPriority]),
707709
RoutingKeys: routingKeys,
708-
recipientKeysRelativeURL: recipientKeysRelativeURL, routingKeysRelativeURL: routingKeysRelativeURL,
710+
recipientKeysRelativeURL: recipientKeysRelativeURL,
711+
routingKeysRelativeURL: routingKeysRelativeURL,
709712
}
710713

711714
delete(rawService, jsonldID)

pkg/doc/did/doc_test.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ func TestValid(t *testing.T) {
406406
eServices := []Service{
407407
{
408408
ID: "did:example:123456789abcdefghi#inbox",
409-
Type: "SocialWebInboxService",
409+
Type: []interface{}{"SocialWebInboxService"},
410410
ServiceEndpoint: model.NewDIDCommV1Endpoint("https://social.example.com/83hfh37dj"),
411411
Properties: map[string]interface{}{"spamCost": map[string]interface{}{"amount": "0.50", "currency": "USD"}},
412412
},
@@ -1808,6 +1808,22 @@ func TestDIDSchemas(t *testing.T) {
18081808
})
18091809
}
18101810

1811+
func TestServiceTypeSchema(t *testing.T) {
1812+
t.Run("success - an array of strings", func(t *testing.T) {
1813+
doc, err := ParseDocument([]byte(multipleServiceTypes))
1814+
require.NoError(t, err)
1815+
require.NotEmpty(t, doc)
1816+
require.Equal(t, doc.Service[0].Type, []interface{}{"SocialWebInboxService", "SomeOtherService"})
1817+
})
1818+
1819+
t.Run("error - not an array of strings", func(t *testing.T) {
1820+
doc, err := ParseDocument([]byte(invalidServiceType))
1821+
require.Error(t, err)
1822+
require.Empty(t, doc)
1823+
require.Contains(t, err.Error(), "Invalid type. Expected: string, given: integer")
1824+
})
1825+
}
1826+
18111827
func TestNewEmbeddedVerificationMethod(t *testing.T) {
18121828
vm := NewEmbeddedVerification(&VerificationMethod{}, Authentication)
18131829
require.NotNil(t, vm)
@@ -2214,3 +2230,31 @@ const docV011WithVerificationRelationships = `{
22142230
"publicKeyBase58": "B12NYF8RrR3h41TDCTJojY59usg3mbtbjnFs7Eud1Y6u"
22152231
}]
22162232
}`
2233+
2234+
const multipleServiceTypes = `
2235+
{
2236+
"@context": ["https://www.w3.org/ns/did/v1"],
2237+
"id": "did:example:21tDAKCERh95uGgKbJNHYp",
2238+
"service": [
2239+
{
2240+
"id": "did:example:123456789abcdefghi#inbox",
2241+
"type": ["SocialWebInboxService","SomeOtherService"],
2242+
"serviceEndpoint": "https://social.example.com/83hfh37dj"
2243+
}
2244+
],
2245+
"created": "2002-10-10T17:00:00Z"
2246+
}`
2247+
2248+
const invalidServiceType = `
2249+
{
2250+
"@context": ["https://www.w3.org/ns/did/v1"],
2251+
"id": "did:example:21tDAKCERh95uGgKbJNHYp",
2252+
"service": [
2253+
{
2254+
"id": "did:example:123456789abcdefghi#inbox",
2255+
"type": [123, "SomeType"],
2256+
"serviceEndpoint": "https://social.example.com/83hfh37dj"
2257+
}
2258+
],
2259+
"created": "2002-10-10T17:00:00Z"
2260+
}`

pkg/doc/did/schema.go

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,19 @@ const (
181181
"type": "string"
182182
},
183183
"type": {
184-
"type": "string"
184+
"oneOf": [
185+
{
186+
"type": "string"
187+
},
188+
{
189+
"type": "array",
190+
"items": [
191+
{
192+
"type": "string"
193+
}
194+
]
195+
}
196+
]
185197
},
186198
"serviceEndpoint": {
187199
"oneOf": [
@@ -377,7 +389,19 @@ const (
377389
"type": "string"
378390
},
379391
"type": {
380-
"type": "string"
392+
"oneOf": [
393+
{
394+
"type": "string"
395+
},
396+
{
397+
"type": "array",
398+
"items": [
399+
{
400+
"type": "string"
401+
}
402+
]
403+
}
404+
]
381405
},
382406
"serviceEndpoint": {
383407
"oneOf": [
@@ -543,7 +567,19 @@ const (
543567
"type": "string"
544568
},
545569
"type": {
546-
"type": "string"
570+
"oneOf": [
571+
{
572+
"type": "string"
573+
},
574+
{
575+
"type": "array",
576+
"items": [
577+
{
578+
"type": "string"
579+
}
580+
]
581+
}
582+
]
547583
},
548584
"serviceEndpoint": {
549585
"oneOf": [

pkg/doc/did/testdata/valid_doc.jsonld

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"service": [
3131
{
3232
"id": "did:example:123456789abcdefghi#inbox",
33-
"type": "SocialWebInboxService",
33+
"type": ["SocialWebInboxService"],
3434
"serviceEndpoint": "https://social.example.com/83hfh37dj",
3535
"spamCost": {
3636
"amount": "0.50",

pkg/internal/didcommutil/util.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright SecureKey Technologies Inc. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package didcommutil
8+
9+
// GetServiceType will return service type if service type is a string or first service type if it is an array.
10+
func GetServiceType(serviceType interface{}) string {
11+
val := ""
12+
13+
switch t := serviceType.(type) {
14+
case string:
15+
val = t
16+
case []string:
17+
if len(t) > 0 {
18+
val = t[0]
19+
}
20+
case []interface{}:
21+
if len(t) > 0 {
22+
if str, ok := t[0].(string); ok {
23+
val = str
24+
}
25+
}
26+
}
27+
28+
return val
29+
}

0 commit comments

Comments
 (0)