Skip to content

Commit bd2ee94

Browse files
committed
#3663: change credentialsubject to map, use casting instead of marshalling where possible
1 parent dc6a361 commit bd2ee94

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+213
-219
lines changed

api/ssi_types_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,14 @@ func createVerifiableCredential() vcr.VerifiableCredential {
129129
ssi.MustParseURI("NutsOrganizationCredential"),
130130
ssi.MustParseURI("VerifiableCredential"),
131131
},
132-
Issuer: ssi.MustParseURI("did:nuts:CuE3qeFGGLhEAS3gKzhMCeqd1dGa9at5JCbmCfyMU2Ey"),
133-
IssuanceDate: issuanceDate,
134-
CredentialSubject: []interface{}{"subject"},
135-
Proof: []interface{}{"because"},
132+
Issuer: ssi.MustParseURI("did:nuts:CuE3qeFGGLhEAS3gKzhMCeqd1dGa9at5JCbmCfyMU2Ey"),
133+
IssuanceDate: issuanceDate,
134+
CredentialSubject: []map[string]any{
135+
{
136+
"id": "subject",
137+
},
138+
},
139+
Proof: []interface{}{"because"},
136140
}
137141
}
138142

auth/api/auth/v1/api_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/nuts-foundation/nuts-node/auth/services/oauth"
3535
"github.com/nuts-foundation/nuts-node/core"
3636
"github.com/nuts-foundation/nuts-node/core/to"
37+
"github.com/nuts-foundation/nuts-node/test"
3738
"github.com/nuts-foundation/nuts-node/vcr"
3839
"github.com/nuts-foundation/nuts-node/vcr/credential"
3940
"github.com/nuts-foundation/nuts-node/vdr"
@@ -507,7 +508,7 @@ func TestWrapper_RequestAccessToken(t *testing.T) {
507508
Type: []ssi.URI{*credential.NutsAuthorizationCredentialTypeURI, vc.VerifiableCredentialTypeV1URI()},
508509
Issuer: vdr.TestDIDA.URI(),
509510
IssuanceDate: issuanceDate,
510-
CredentialSubject: []interface{}{credential.NutsAuthorizationCredentialSubject{
511+
CredentialSubject: []map[string]any{test.MustRemarshalIntoMap(credential.NutsAuthorizationCredentialSubject{
511512
ID: vdr.TestDIDB.String(),
512513
PurposeOfUse: "eTransfer",
513514
Resources: []credential.Resource{
@@ -517,7 +518,7 @@ func TestWrapper_RequestAccessToken(t *testing.T) {
517518
UserContext: true,
518519
},
519520
},
520-
}},
521+
})},
521522
Proof: []interface{}{vc.Proof{}},
522523
},
523524
}

auth/api/iam/api.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -754,12 +754,7 @@ func (r Wrapper) RequestServiceAccessToken(ctx context.Context, request RequestS
754754
// by the nuts-node to build the correct wallet for a DID. See https://github.com/nuts-foundation/nuts-node/issues/3696
755755
// As a sideeffect it is no longer possible to pass signed credentials to this API.
756756
for _, cred := range credentials {
757-
var credentialSubject []map[string]interface{}
758-
if err := cred.UnmarshalCredentialSubject(&credentialSubject); err != nil {
759-
// extremely unlikely
760-
return nil, core.InvalidInputError("failed to parse credentialSubject.id: %w", err)
761-
}
762-
for _, credSub := range credentialSubject {
757+
for _, credSub := range cred.CredentialSubject {
763758
if _, ok := credSub["id"]; ok {
764759
return nil, core.InvalidInputError("self-asserted credentials MUST NOT contain a 'credentialSubject.id'")
765760
}

auth/api/iam/api_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,7 @@ func TestWrapper_RequestServiceAccessToken(t *testing.T) {
10341034
body.Credentials = &[]vc.VerifiableCredential{
10351035
{
10361036
ID: to.Ptr(ssi.MustParseURI("not empty")),
1037-
CredentialSubject: []any{map[string]string{"id": "not empty"}},
1037+
CredentialSubject: []map[string]any{{"id": "not empty"}},
10381038
},
10391039
}
10401040
request := RequestServiceAccessTokenRequestObject{SubjectID: holderSubjectID, Body: body}
@@ -1498,7 +1498,7 @@ func createIssuerCredential(issuerDID did.DID, holderDID did.DID) *vc.Verifiable
14981498
Issuer: issuerDID.URI(),
14991499
Context: []ssi.URI{credential.NutsV1ContextURI},
15001500
Type: []ssi.URI{credType},
1501-
CredentialSubject: []interface{}{map[string]interface{}{"id": holderDID.String()}},
1501+
CredentialSubject: []map[string]any{{"id": holderDID.String()}},
15021502
IssuanceDate: time.Now(),
15031503
}
15041504
verifiableCredential, _ := vc.CreateJWTVerifiableCredential(nil, template, captureFn)

auth/api/iam/s2s_vptoken_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ func TestWrapper_handleS2SAccessTokenRequest(t *testing.T) {
308308
t.Run("VC without credentialSubject.id", func(t *testing.T) {
309309
ctx := newTestClient(t)
310310
presentation := test.CreateJSONLDPresentation(t, *subjectDID, proofVisitor, vc.VerifiableCredential{
311-
CredentialSubject: []interface{}{map[string]string{}},
311+
CredentialSubject: []map[string]any{{}},
312312
})
313313

314314
resp, err := ctx.client.handleS2SAccessTokenRequest(context.Background(), clientID, issuerSubjectID, requestedScope, submissionJSON, presentation.Raw())
@@ -353,8 +353,8 @@ func TestWrapper_handleS2SAccessTokenRequest(t *testing.T) {
353353
// This indicates the client presented credentials that don't actually match the presentation definition,
354354
// which could indicate a malicious client.
355355
otherVerifiableCredential := vc.VerifiableCredential{
356-
CredentialSubject: []interface{}{
357-
map[string]interface{}{
356+
CredentialSubject: []map[string]any{
357+
{
358358
"id": subjectDID.String(),
359359
// just for demonstration purposes, what matters is that the credential does not match the presentation definition.
360360
"IsAdministrator": true,

auth/api/iam/user.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ func (r Wrapper) issueEmployeeCredential(ctx context.Context, session user.Sessi
180180
Issuer: issuerDID,
181181
IssuanceDate: issuanceDate,
182182
ExpirationDate: &expirationDate,
183-
CredentialSubject: []interface{}{
184-
map[string]string{
183+
CredentialSubject: []map[string]any{
184+
{
185185
"id": session.Wallet.DID.String(),
186186
"identifier": userDetails.Id,
187187
"name": userDetails.Name,

auth/api/iam/user_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ func TestWrapper_handleUserLanding(t *testing.T) {
131131
assert.Equal(t, jwa.EC, sessionKey.KeyType())
132132
// check for details of issued NutsEmployeeCredential
133133
assert.Equal(t, "NutsEmployeeCredential", employeeCredentialTemplate.Type[0].String())
134-
employeeCredentialSubject := employeeCredentialTemplate.CredentialSubject[0].(map[string]string)
135-
assert.True(t, strings.HasPrefix(employeeCredentialSubject["id"], "did:jwk:"))
134+
employeeCredentialSubject := employeeCredentialTemplate.CredentialSubject[0]
135+
assert.True(t, strings.HasPrefix(employeeCredentialSubject["id"].(string), "did:jwk:"))
136136
assert.Equal(t, userDetails.Id, employeeCredentialSubject["identifier"])
137137
assert.Equal(t, userDetails.Name, employeeCredentialSubject["name"])
138138
assert.Equal(t, userDetails.Role, employeeCredentialSubject["roleName"])

auth/client/iam/openid4vp_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,8 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) {
292292
credential.NutsV1ContextURI,
293293
},
294294
Type: []ssi.URI{vc.VerifiableCredentialTypeV1URI(), ssi.MustParseURI("NutsEmployeeCredential")},
295-
CredentialSubject: []interface{}{
296-
map[string]interface{}{
295+
CredentialSubject: []map[string]any{
296+
{
297297
"roleName": "employee",
298298
"name": "John Doe",
299299
"identifier": "123",

auth/services/oauth/relying_party_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,19 @@ func TestService_CreateJwtBearerToken(t *testing.T) {
133133
Type: []ssi.URI{*credential.NutsAuthorizationCredentialTypeURI, vc.VerifiableCredentialTypeV1URI()},
134134
Issuer: vdr.TestDIDA.URI(),
135135
IssuanceDate: issuanceDate,
136-
CredentialSubject: []interface{}{credential.NutsAuthorizationCredentialSubject{
137-
ID: vdr.TestDIDB.String(),
138-
PurposeOfUse: "eTransfer",
139-
Resources: []credential.Resource{
140-
{
141-
Path: "/composition/1",
142-
Operations: []string{"read"},
143-
UserContext: true,
136+
CredentialSubject: []map[string]any{
137+
{
138+
"id": vdr.TestDIDB.String(),
139+
"purposeOfUse": "eTransfer",
140+
"resources": []any{
141+
map[string]any{
142+
"path": "/composition/1",
143+
"operations": []string{"read"},
144+
"userContext": true,
145+
},
144146
},
145147
},
146-
}},
148+
},
147149
Proof: []interface{}{vc.Proof{}},
148150
}
149151

auth/services/selfsigned/signer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ func TestSessionStore_SigningSessionStatus(t *testing.T) {
247247
assert.Equal(t, employer.URI(), credential.Issuer)
248248
assert.Equal(t, []ssi.URI{ssi.MustParseURI("NutsEmployeeCredential")}, credential.Type)
249249

250-
credentialSubject := credential.CredentialSubject[0].(map[string]interface{})
250+
credentialSubject := credential.CredentialSubject[0]
251251
assert.Equal(t, employer.String(), credentialSubject["id"])
252252
assert.Equal(t, "Organization", credentialSubject["type"])
253253
require.IsType(t, map[string]interface{}{}, credentialSubject["member"])

auth/services/selfsigned/types/types.go

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ package types
2020

2121
import (
2222
"context"
23-
"encoding/json"
2423
"github.com/nuts-foundation/nuts-node/vcr/credential"
2524
"strings"
2625
"time"
@@ -44,27 +43,26 @@ type Session struct {
4443
Employee Employee
4544
}
4645

47-
func (s Session) CredentialSubject() []interface{} {
48-
subject := EmployeeIdentityCredentialSubject{
49-
BaseCredentialSubject: credential.BaseCredentialSubject{
50-
ID: s.Employer,
46+
func (s Session) CredentialSubject() []map[string]any {
47+
member := map[string]any{
48+
"identifier": s.Employee.Identifier,
49+
"member": map[string]any{
50+
"familyName": s.Employee.FamilyName,
51+
"initials": s.Employee.Initials,
52+
"type": "Person",
5153
},
52-
Type: "Organization",
53-
Member: EmployeeIdentityCredentialMember{
54-
Identifier: s.Employee.Identifier,
55-
Member: EmployeeIdentityCredentialMemberMember{
56-
FamilyName: s.Employee.FamilyName,
57-
Initials: s.Employee.Initials,
58-
Type: "Person",
59-
},
60-
RoleName: s.Employee.RoleName,
61-
Type: "EmployeeRole",
54+
"type": "EmployeeRole",
55+
}
56+
if s.Employee.RoleName != nil {
57+
member["roleName"] = *s.Employee.RoleName
58+
}
59+
return []map[string]any{
60+
{
61+
"id": s.Employer,
62+
"type": "Organization",
63+
"member": member,
6264
},
6365
}
64-
data, _ := json.Marshal(subject)
65-
result := map[string]interface{}{}
66-
_ = json.Unmarshal(data, &result)
67-
return []interface{}{result}
6866
}
6967

7068
// HumanReadableContract returns the contract text without the contract type (e.g. "NL:LoginContract:v3")

auth/services/selfsigned/types/types_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func TestSession_CredentialSubject(t *testing.T) {
5555
}
5656
res := s.CredentialSubject()
5757
require.Len(t, res, 1)
58-
subject := res[0].(map[string]interface{})
58+
subject := res[0]
5959
// subject is an organization and contains information about the employer
6060
require.Equal(t, "did:nuts:123", subject["id"])
6161
require.Equal(t, "Organization", subject["type"])

auth/services/selfsigned/validator_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -448,10 +448,10 @@ func createOrganizationCredential(issuerDID string) vc.VerifiableCredential {
448448
Context: []ssi.URI{credential.NutsV1ContextURI},
449449
Type: []ssi.URI{ssi.MustParseURI("NutsOrganizationCredential")},
450450
Issuer: did.MustParseDID(issuerDID).URI(),
451-
CredentialSubject: []interface{}{
452-
credential.NutsOrganizationCredentialSubject{
453-
ID: issuerDID,
454-
Organization: map[string]string{
451+
CredentialSubject: []map[string]any{
452+
{
453+
"id": issuerDID,
454+
"organization": map[string]string{
455455
"name": "CareBears",
456456
"city": "CareTown",
457457
},

didman/didman.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import (
3838
"github.com/nuts-foundation/nuts-node/didman/log"
3939
"github.com/nuts-foundation/nuts-node/jsonld"
4040
"github.com/nuts-foundation/nuts-node/vcr"
41-
"github.com/nuts-foundation/nuts-node/vcr/credential"
4241
)
4342

4443
// ModuleName contains the name of this module: Didman
@@ -501,13 +500,7 @@ func (d *didman) resolveOrganizationDIDDocument(organization vc.VerifiableCreden
501500
if len(organization.CredentialSubject) == 0 {
502501
return nil, did.DID{}, errors.New("no credential subjects in organization credential")
503502
}
504-
credentialSubject := make([]credential.BaseCredentialSubject, 0)
505-
err := organization.UnmarshalCredentialSubject(&credentialSubject)
506-
if err != nil {
507-
return nil, did.DID{}, fmt.Errorf("unable to get DID from organization credential: %w", err)
508-
}
509-
organizationDIDStr := credentialSubject[0].ID
510-
organizationDID, err := did.ParseDID(organizationDIDStr)
503+
organizationDID, err := organization.SubjectDID()
511504
if err != nil {
512505
return nil, did.DID{}, fmt.Errorf("unable to parse DID from organization credential: %w", err)
513506
}

didman/didman_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -859,8 +859,8 @@ func TestDidman_SearchOrganizations(t *testing.T) {
859859
ctx := newMockContext(t)
860860
credentialWithInvalidSubjectID := vc.VerifiableCredential{}
861861
_ = json.Unmarshal([]byte(jsonld.TestOrganizationCredential), &credentialWithInvalidSubjectID)
862-
credentialWithInvalidSubjectID.CredentialSubject = []interface{}{
863-
map[string]interface{}{
862+
credentialWithInvalidSubjectID.CredentialSubject = []map[string]any{
863+
{
864864
"id": "90",
865865
},
866866
}
@@ -877,8 +877,8 @@ func TestDidman_SearchOrganizations(t *testing.T) {
877877
ctx := newMockContext(t)
878878
credentialWithoutSubjectID := vc.VerifiableCredential{}
879879
_ = json.Unmarshal([]byte(jsonld.TestOrganizationCredential), &credentialWithoutSubjectID)
880-
credentialWithoutSubjectID.CredentialSubject = []interface{}{
881-
map[string]interface{}{},
880+
credentialWithoutSubjectID.CredentialSubject = []map[string]any{
881+
{},
882882
}
883883

884884
ctx.vcr.EXPECT().Search(reqCtx, searchTerms, false, nil).Return([]vc.VerifiableCredential{credentialWithoutSubjectID}, nil)

discovery/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ func (r *clientRegistrationManager) findCredentialsAndBuildPresentation(ctx cont
239239
registrationCredential = vc.VerifiableCredential{
240240
Context: []ssi.URI{vc.VCContextV1URI(), credential.NutsV1ContextURI},
241241
Type: []ssi.URI{vc.VerifiableCredentialTypeV1URI(), credential.DiscoveryRegistrationCredentialTypeV1URI()},
242-
CredentialSubject: []interface{}{parameters},
242+
CredentialSubject: []map[string]any{parameters},
243243
}
244244
credentials = append(credentials, credential.AutoCorrectSelfAttestedCredential(registrationCredential, subjectDID))
245245
}

discovery/test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ func createCredential(issuerDID did.DID, subjectDID did.DID, credentialSubject m
197197
Issuer: issuerDID.URI(),
198198
IssuanceDate: issuanceDate,
199199
ExpirationDate: &expirationDate,
200-
CredentialSubject: []interface{}{credentialSubject},
200+
CredentialSubject: []map[string]any{credentialSubject},
201201
}, func(ctx context.Context, claims map[string]interface{}, headers map[string]interface{}) (string, error) {
202202
if claimVisitor != nil {
203203
claimVisitor(claims)
@@ -214,7 +214,7 @@ func createHolderCredential(subjectDID did.DID, credentialSubject map[string]int
214214
c := vc.VerifiableCredential{
215215
Context: []ssi.URI{vc.VCContextV1URI(), credential.NutsV1ContextURI},
216216
Type: []ssi.URI{vc.VerifiableCredentialTypeV1URI(), credential.DiscoveryRegistrationCredentialTypeV1URI()},
217-
CredentialSubject: []interface{}{credentialSubject},
217+
CredentialSubject: []map[string]any{credentialSubject},
218218
}
219219
c = credential.AutoCorrectSelfAttestedCredential(c, subjectDID)
220220
// serialize/deserialize

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ require (
3131
github.com/nats-io/nats-server/v2 v2.10.26
3232
github.com/nats-io/nats.go v1.39.1
3333
github.com/nuts-foundation/crypto-ecies v0.0.0-20211207143025-5b84f9efce2b
34-
github.com/nuts-foundation/go-did v0.15.0
34+
github.com/nuts-foundation/go-did v0.15.1-0.20250319081900-aa2a47f72926
3535
github.com/nuts-foundation/go-leia/v4 v4.1.0
3636
github.com/nuts-foundation/go-stoabs v1.11.0
3737
github.com/nuts-foundation/sqlite v1.0.0

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,12 @@ github.com/nuts-foundation/crypto-ecies v0.0.0-20211207143025-5b84f9efce2b h1:80
365365
github.com/nuts-foundation/crypto-ecies v0.0.0-20211207143025-5b84f9efce2b/go.mod h1:6YUioYirD6/8IahZkoS4Ypc8xbeJW76Xdk1QKcziNTM=
366366
github.com/nuts-foundation/go-did v0.15.0 h1:aNl6KC8jiyRJGl9PPKFBboLLC0wUm5h+tjE1UBDQEPw=
367367
github.com/nuts-foundation/go-did v0.15.0/go.mod h1:swjCJvcRxc+i1nyieIERWEb3vFb4N7iYC+qen2OIbNg=
368+
github.com/nuts-foundation/go-did v0.15.1-0.20250318150623-d2ac97c002b6 h1:8dPWXKbZbe10YtN2o3vNKw9jzHF4l0k4S/ZLA+JZ/bo=
369+
github.com/nuts-foundation/go-did v0.15.1-0.20250318150623-d2ac97c002b6/go.mod h1:+CKZlsyh7oXp35uXfXkWkauVKgAFmxxhvV9EFDc1Ips=
370+
github.com/nuts-foundation/go-did v0.15.1-0.20250318154645-e72bfe1fdc5b h1:705SSiJW5L6ZtcCseFAej2aIdDz3T223acONGsXjT9g=
371+
github.com/nuts-foundation/go-did v0.15.1-0.20250318154645-e72bfe1fdc5b/go.mod h1:+CKZlsyh7oXp35uXfXkWkauVKgAFmxxhvV9EFDc1Ips=
372+
github.com/nuts-foundation/go-did v0.15.1-0.20250319081900-aa2a47f72926 h1:9E0uBk36lyEQCaXI/8ElgJO7ng/71ZCIvsHoezVllLY=
373+
github.com/nuts-foundation/go-did v0.15.1-0.20250319081900-aa2a47f72926/go.mod h1:+CKZlsyh7oXp35uXfXkWkauVKgAFmxxhvV9EFDc1Ips=
368374
github.com/nuts-foundation/go-leia/v4 v4.1.0 h1:5Jo9c5hL4G6IP4JTI/UpPfpY6BILlHbdv9+PTf4lc14=
369375
github.com/nuts-foundation/go-leia/v4 v4.1.0/go.mod h1:tYveGED8tSbQYhZNv2DVTc51c2zEWmSF+MG96PAtalY=
370376
github.com/nuts-foundation/go-stoabs v1.11.0 h1:q18jVruPdFcVhodDrnKuhq/24i0pUC/YXgzJS0glKUU=

test/json.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package test
2+
3+
import "encoding/json"
4+
5+
func remarshal(src interface{}, dst interface{}) error {
6+
asJSON, err := json.Marshal(src)
7+
if err != nil {
8+
return err
9+
}
10+
return json.Unmarshal(asJSON, &dst)
11+
}
12+
13+
func MustRemarshalIntoMap(v interface{}) map[string]any {
14+
var result map[string]interface{}
15+
if err := remarshal(v, &result); err != nil {
16+
panic(err)
17+
}
18+
return result
19+
}

0 commit comments

Comments
 (0)