-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathconfigClient.go
More file actions
738 lines (653 loc) · 29.3 KB
/
Copy pathconfigClient.go
File metadata and controls
738 lines (653 loc) · 29.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
package config
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"reflect"
"strings"
"github.com/fiware/VCVerifier/logging"
"github.com/mitchellh/mapstructure"
)
type EndpointType int
const DEFAULT_LIST_TYPE = "ebsi"
const (
Unknown EndpointType = iota
TrustedIssuers
TrustedParticipants
)
func (e EndpointType) String() string {
switch e {
case TrustedIssuers:
return "TRUSTED_ISSUERS"
case TrustedParticipants:
return "TRUSTED_PARTICIPANTS"
default:
return "UNKNOWN"
}
}
func (e EndpointType) MarshalJSON() ([]byte, error) {
return json.Marshal(e.String())
}
func (e *EndpointType) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
switch s {
case "TRUSTED_ISSUERS":
*e = TrustedIssuers
case "TRUSTED_PARTICIPANTS":
*e = TrustedParticipants
default:
*e = Unknown
}
return nil
}
const SERVICES_PATH = "service"
var ErrorCcsNoResponse = errors.New("no_response_from_ccs")
var ErrorCcsErrorResponse = errors.New("error_response_from_ccs")
var ErrorCcsEmptyResponse = errors.New("empty_response_from_ccs")
var ErrorNoSuchScope = errors.New("requested_scope_does_not_exist")
type HttpClient interface {
Get(url string) (resp *http.Response, err error)
}
type ConfigClient interface {
GetServices() (services []ConfiguredService, err error)
}
type HttpConfigClient struct {
client HttpClient
configEndpoint string
}
type ServicesResponse struct {
Total int `json:"total"`
PageNumber int `json:"pageNumber"`
PageSize int `json:"pageSize"`
Services []ConfiguredService `json:"services"`
}
type ConfiguredService struct {
// Default OIDC scope to be used if none is specified
DefaultOidcScope string `json:"defaultOidcScope" mapstructure:"defaultOidcScope"`
ServiceScopes map[string]ScopeEntry `json:"oidcScopes,omitempty" mapstructure:"oidcScopes"`
Id string `json:"id" mapstructure:"id"`
AuthorizationType string `json:"authorizationType,omitempty" mapstructure:"authorizationType,omitempty"`
AuthorizationPath string `json:"authorizationPath,omitempty" mapstructure:"authorizationPath,omitempty"`
// AllowedOrigins specifies the list of origins permitted for CORS requests
// to this service. When empty or nil, no service-specific restriction is
// applied and the verifier falls back to the global default (wildcard).
// Set to ["*"] to explicitly allow all origins for this service.
AllowedOrigins []string `json:"allowedOrigins,omitempty" mapstructure:"allowedOrigins,omitempty"`
}
type ScopeEntry struct {
// credential types with their trust configuration
Credentials []Credential `json:"credentials" mapstructure:"credentials"`
// Proofs to be requested - see https://identity.foundation/presentation-exchange/#presentation-definition
PresentationDefinition *PresentationDefinition `json:"presentationDefinition,omitempty" mapstructure:"presentationDefinition,omitempty"`
// Query to request the credentials to be included in the presentation
DCQL *DCQL `json:"dcql,omitempty" mapstructure:"dcql,omitempty"`
// When set, the claim are flatten to plain JWT-claims before being included, instead of keeping the credential/presentation structure, where the claims are under the key vc or vp
FlatClaims bool `json:"flatClaims" mapstructure:"flatClaims"`
}
type Credential struct {
// Type of the credential
Type string `json:"type" mapstructure:"type"`
// A list of (EBSI Trusted Issuers Registry compatible) endpoints to retrieve the trusted participants from.
TrustedParticipantsLists TrustedParticipantsLists `json:"trustedParticipantsLists,omitempty" mapstructure:"trustedParticipantsLists,omitempty"`
// A list of (EBSI Trusted Issuers Registry compatible) endpoints to retrieve the trusted issuers from. The attributes need to be formatted to comply with the verifiers requirements.
TrustedIssuersLists TrustedIssuersLists `json:"trustedIssuersLists,omitempty" mapstructure:"trustedIssuersLists,omitempty"`
// Configuration of Holder Verification
HolderVerification HolderVerification `json:"holderVerification" mapstructure:"holderVerification"`
// Does the given credential require a compliancy credential
RequireCompliance bool `json:"requireCompliance" mapstructure:"requireCompliance"`
// Configuration for the credential its inclusion into the JWT.
JwtInclusion JwtInclusion `json:"jwtInclusion" mapstructure:"jwtInclusion"`
// Per-credential configuration for the W3C Bitstring Status List /
// StatusList2021 revocation-list check. Defaults to an enabled
// CredentialStatus when omitted so that the revocation check is active
// unless explicitly disabled.
CredentialStatus CredentialStatus `json:"credentialStatus" mapstructure:"credentialStatus"`
}
func (c *Credential) UnmarshalJSON(data []byte) error {
type Alias Credential
aux := &struct{ *Alias }{Alias: (*Alias)(c)}
if err := json.Unmarshal(data, aux); err != nil {
return err
}
if c.CredentialStatus.Enabled == nil {
t := true
c.CredentialStatus.Enabled = &t
}
return nil
}
func (c Credential) MarshalJSON() ([]byte, error) {
type Alias Credential
if c.CredentialStatus.Enabled == nil {
t := true
c.CredentialStatus.Enabled = &t
}
return json.Marshal((Alias)(c))
}
// CredentialStatus holds the per-credential-type configuration for the
// status-list based revocation check. Defaults to enabled so that the
// revocation check is active unless explicitly disabled.
type CredentialStatus struct {
// Enabled toggles the revocation-list check for this credential type.
// Defaults to true when absent so that status-list lookups are performed
// unless explicitly disabled.
Enabled *bool `json:"enabled,omitempty" mapstructure:"enabled"`
// AcceptedPurposes lists the status purposes this credential type enforces
// (for example "revocation" or "suspension"). When empty callers should
// fall back to DefaultAcceptedStatusPurposes(). The field is intentionally
// left un-defaulted at mapstructure level so the YAML can distinguish
// "not set" from an explicit empty list.
AcceptedPurposes []string `json:"acceptedPurposes,omitempty" mapstructure:"acceptedPurposes,omitempty"`
// RequireStatus rejects credentials of this type that are missing a
// credentialStatus entry when set to true. Defaults to false so that
// credentials without status information are accepted.
RequireStatus bool `json:"requireStatus" mapstructure:"requireStatus"`
}
// IsEnabled returns true when Enabled is nil (absent) or explicitly true.
func (cs *CredentialStatus) IsEnabled() bool {
return cs.Enabled == nil || *cs.Enabled
}
func (cs *CredentialStatus) UnmarshalJSON(data []byte) error {
type Alias CredentialStatus
aux := &struct{ *Alias }{Alias: (*Alias)(cs)}
if err := json.Unmarshal(data, aux); err != nil {
return err
}
if cs.Enabled == nil {
t := true
cs.Enabled = &t
}
return nil
}
func (cs CredentialStatus) MarshalJSON() ([]byte, error) {
type Alias CredentialStatus
if cs.Enabled == nil {
t := true
cs.Enabled = &t
}
return json.Marshal((Alias)(cs))
}
type JwtInclusion struct {
// Should the given credential be included into the generated JWT; defaults to true when absent.
Enabled *bool `json:"enabled,omitempty" mapstructure:"enabled"`
// Should the complete credential be embedded
FullInclusion bool `json:"fullInclusion" mapstructure:"fullInclusion"`
// Claims to be included. Default empty list
ClaimsToInclude []ClaimInclusion `json:"claimsToInclude" mapstructure:"claimsToInclude"`
}
// IsEnabled returns true when Enabled is nil (absent) or explicitly true.
func (j *JwtInclusion) IsEnabled() bool {
return j.Enabled == nil || *j.Enabled
}
func (j *JwtInclusion) UnmarshalJSON(data []byte) error {
type Alias JwtInclusion
aux := &struct{ *Alias }{Alias: (*Alias)(j)}
if err := json.Unmarshal(data, aux); err != nil {
return err
}
if j.Enabled == nil {
t := true
j.Enabled = &t
}
if j.ClaimsToInclude == nil {
j.ClaimsToInclude = make([]ClaimInclusion, 0)
}
return nil
}
func (j JwtInclusion) MarshalJSON() ([]byte, error) {
type Alias JwtInclusion
if j.Enabled == nil {
t := true
j.Enabled = &t
}
if j.ClaimsToInclude == nil {
j.ClaimsToInclude = make([]ClaimInclusion, 0)
}
return json.Marshal((Alias)(j))
}
type ClaimInclusion struct {
// Key of the claim to be included. All objects under this key will be included unchanged.
OriginalKey string `json:"originalKey" mapstructure:"originalKey"`
// Key of the claim to be used in the jwt. If not provided, the original one will be used.
NewKey string `json:"newKey" mapstructure:"newKey"`
}
type TrustedParticipantsList struct {
// Type of praticipants list to be used - either gaia-x or ebsi
Type string `json:"type" mapstructure:"type"`
// url of the list
Url string `json:"url" mapstructure:"url"`
}
type TrustedParticipantsLists []TrustedParticipantsList
func (t *TrustedParticipantsLists) UnmarshalJSON(data []byte) error {
// Try structured format first
var structured []TrustedParticipantsList
if err := json.Unmarshal(data, &structured); err == nil {
*t = structured
return nil
}
// Fallback to string array format
var urls []string
if err := json.Unmarshal(data, &urls); err != nil {
return err
}
result := make([]TrustedParticipantsList, len(urls))
for i, url := range urls {
result[i] = TrustedParticipantsList{
Type: DEFAULT_LIST_TYPE,
Url: url,
}
}
*t = result
return nil
}
// TrustedIssuersList represents a single trusted issuers registry endpoint
// with an associated type (e.g. "ebsi", "ebsi-v5"). Mirrors
// TrustedParticipantsList for issuers.
type TrustedIssuersList struct {
// Type of issuers list to be used — "ebsi" for v3/v4, "ebsi-v5" for v5.
Type string `json:"type" mapstructure:"type"`
// Url of the trusted issuers registry endpoint.
Url string `json:"url" mapstructure:"url"`
}
// TrustedIssuersLists is a slice of TrustedIssuersList with a custom JSON
// unmarshaler that accepts both the new structured format and the legacy
// plain string array format for backward compatibility.
type TrustedIssuersLists []TrustedIssuersList
// UnmarshalJSON supports two JSON formats:
// - Structured: [{"type":"ebsi-v5","url":"https://..."}]
// - Legacy string array: ["https://..."] — each URL defaults to type "ebsi".
func (t *TrustedIssuersLists) UnmarshalJSON(data []byte) error {
// Try structured format first
var structured []TrustedIssuersList
if err := json.Unmarshal(data, &structured); err == nil {
*t = structured
return nil
}
// Fallback to string array format
var urls []string
if err := json.Unmarshal(data, &urls); err != nil {
return err
}
result := make([]TrustedIssuersList, len(urls))
for i, url := range urls {
result[i] = TrustedIssuersList{
Type: DEFAULT_LIST_TYPE,
Url: url,
}
}
*t = result
return nil
}
// MarshalJSON always serializes as a plain array of URL strings, dropping the
// per-entry Type. This is the API-facing representation: config.Credential
// (and thus TrustedIssuersLists) is embedded directly in ccsapi request/response
// bodies, and callers only need the endpoint URLs. Type is an internal-only
// concern (dispatches between ebsi/ebsi-v5/gaia-x lookups, see
// trustedparticipant.go) that is never surfaced over the API and is untouched
// on the database persistence path, which round-trips through the separate
// CredentialDB model instead of this type's JSON methods.
func (t TrustedIssuersLists) MarshalJSON() ([]byte, error) {
urls := make([]string, len(t))
for i, entry := range t {
urls[i] = entry.Url
}
return json.Marshal(urls)
}
// trustedIssuersListsType is the reflect.Type for TrustedIssuersLists, cached
// to avoid repeated reflect calls in the decode hook.
var trustedIssuersListsType = reflect.TypeOf(TrustedIssuersLists{})
// TrustedIssuersListsDecodeHook returns a mapstructure DecodeHookFuncType that
// converts a legacy plain-string slice (from YAML) into a TrustedIssuersLists
// value. Each bare URL string becomes a TrustedIssuersList entry with the
// default type ("ebsi"). Structured entries (maps) are decoded inline.
// This mirrors the JSON backward-compatibility provided by UnmarshalJSON but
// for the YAML/mapstructure code path.
func TrustedIssuersListsDecodeHook() mapstructure.DecodeHookFuncType {
return func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) {
if to != trustedIssuersListsType {
return data, nil
}
slice, ok := data.([]interface{})
if !ok {
return data, nil
}
result := make(TrustedIssuersLists, 0, len(slice))
for _, item := range slice {
switch v := item.(type) {
case string:
// Legacy plain-URL format → default type.
result = append(result, TrustedIssuersList{
Type: DEFAULT_LIST_TYPE,
Url: v,
})
case map[string]interface{}:
// Structured format — extract type and url.
entry := TrustedIssuersList{}
if t, ok := v["type"]; ok {
entry.Type = fmt.Sprintf("%v", t)
}
if u, ok := v["url"]; ok {
entry.Url = fmt.Sprintf("%v", u)
}
result = append(result, entry)
default:
// Unrecognised element — let mapstructure surface an error.
return data, nil
}
}
return result, nil
}
}
// EndpointEntry describes a single trust-registry endpoint together with its
// type and the list format it exposes.
type EndpointEntry struct {
// Type classifies the registry: TrustedIssuers or TrustedParticipants.
Type EndpointType `json:"type" mapstructure:"type"`
// ListType is the format of the registry list. Values: "ebsi", "gaia-x".
// Defaults to "ebsi" when omitted.
ListType string `json:"listType" mapstructure:"listType" default:"ebsi"`
// Endpoint is the URL of the registry.
Endpoint string `json:"endpoint" mapstructure:"endpoint"`
}
type HolderVerification struct {
// should holder verification be enabled
Enabled bool `json:"enabled" mapstructure:"enabled"`
// the claim containing the holder; defaults to "subject" when absent.
Claim string `json:"claim" mapstructure:"claim"`
}
func (h *HolderVerification) UnmarshalJSON(data []byte) error {
type Alias HolderVerification
aux := &struct{ *Alias }{Alias: (*Alias)(h)}
if err := json.Unmarshal(data, aux); err != nil {
return err
}
if h.Claim == "" {
h.Claim = "subject"
}
return nil
}
func (h HolderVerification) MarshalJSON() ([]byte, error) {
type Alias HolderVerification
if h.Claim == "" {
h.Claim = "subject"
}
return json.Marshal((Alias)(h))
}
type PresentationDefinition struct {
Id string `json:"id"`
// List of requested inputs
InputDescriptors []InputDescriptor `json:"input_descriptors" mapstructure:"input_descriptors"`
// Format of the credential to be requested
Format map[string]FormatObject `json:"format" mapstructure:"format"`
// A human readable name for the definition
Name string `json:"name,omitempty" mapstructure:"name,omitempty"`
// A string that describes the purpose for which the definition should be used
Purpose string `json:"purpose,omitempty" mapstructure:"purpose,omitempty"`
}
type FormatObject struct {
// list of algorithms to be requested for credential - f.e. ES256
Alg []string `json:"alg" mapstructure:"alg"`
ProofType []string `json:"proofType,omitempty" mapstructure:"proofType,omitempty"`
}
type InputDescriptor struct {
// Id of the descriptor
Id string `json:"id" mapstructure:"id"`
// defines the information to be requested
Constraints Constraints `json:"constraints" mapstructure:"constraints"`
// Format of the credential to be requested
Format map[string]FormatObject `json:"format,omitempty" mapstructure:"format,omitempty"`
// A human readable name for the definition
Name string `json:"name,omitempty" mapstructure:"name,omitempty"`
// A string that describes the purpose for which the definition should be used
Purpose string `json:"purpose,omitempty" mapstructure:"purpose,omitempty"`
}
type Constraints struct {
// array of objects to describe the information to be included
Fields []Fields `json:"fields" mapstructure:"fields"`
}
type Fields struct {
// Id of the field
Id string `json:"id" mapstructure:"id"`
// A list of JsonPaths for the requested claim
Path []string `json:"path" mapstructure:"path"`
// Does it need to be included? Defaults to true when absent.
Optional *bool `json:"optional,omitempty" mapstructure:"optional,omitempty"`
// a custom filter to be applied on the fields, f.e. restrict to certain values
Filter interface{} `json:"filter,omitempty" mapstructure:"filter"`
}
// set Optional as true if missing
func (f *Fields) UnmarshalJSON(data []byte) error {
type Alias Fields
aux := &struct {
Optional *bool `json:"optional"`
*Alias
}{
Alias: (*Alias)(f),
}
if err := json.Unmarshal(data, aux); err != nil {
return err
}
if aux.Optional == nil {
t := true
f.Optional = &t
} else {
f.Optional = aux.Optional
}
return nil
}
func (f Fields) MarshalJSON() ([]byte, error) {
type Alias Fields
optVal := true
if f.Optional != nil {
optVal = *f.Optional
}
return json.Marshal(&struct {
Optional bool `json:"optional"`
Alias
}{
Optional: optVal,
Alias: (Alias)(f),
})
}
// DCQL defines a JSON encoded query to request the credentials to be included in the presentation
type DCQL struct {
// A non-empty array of Credential Queries that specify the requested Credentials.
Credentials []CredentialQuery `json:"credentials" mapstructure:"credentials"`
// A non-empty array of Credential Set Queries that specifies additional constraints on which of the requested Credentials to return.
CredentialSets []CredentialSetQuery `json:"credential_sets,omitempty" mapstructure:"credential_sets,omitempty"`
}
// CredentialQuery is an object representing a request for a presentation of one or more matching Credentials
type CredentialQuery struct {
// A string identifying the Credential in the response and, if provided, the constraints in credential_sets. The value MUST be a non-empty string consisting of alphanumeric, underscore (_), or hyphen (-) characters. Within the Authorization Request, the same id MUST NOT be present more than once.
Id string `json:"id,omitempty" mapstructure:"id,omitempty"`
// A string that specifies the format of the requested Credential.
Format string `json:"format,omitempty" mapstructure:"format,omitempty"`
// A boolean which indicates whether multiple Credentials can be returned for this Credential Query. If omitted, the default value is false.
Multiple bool `json:"multiple" mapstructure:"multiple"`
// A non-empty array of objects that specifies claims in the requested Credential. Verifiers MUST NOT point to the same claim more than once in a single query. Wallets SHOULD ignore such duplicate claim queries.
Claims []ClaimsQuery `json:"claims" mapstructure:"claims"`
// Defines additional properties requested by the Verifier that apply to the metadata and validity data of the Credential. The properties of this object are defined per Credential Format. If empty, no specific constraints are placed on the metadata or validity of the requested Credential.
Meta *MetaDataQuery `json:"meta,omitempty" mapstructure:"meta,omitempty"`
// A boolean which indicates whether the Verifier requires a Cryptographic Holder Binding proof. Defaults to true when absent.
RequireCryptographicHolderBinding *bool `json:"require_cryptographic_holder_binding,omitempty" mapstructure:"require_cryptographic_holder_binding"`
// A non-empty array containing arrays of identifiers for elements in claims that specifies which combinations of claims for the Credential are requested.
ClaimSets [][]string `json:"claim_sets,omitempty" mapstructure:"claim_sets,omitempty"`
// A non-empty array of objects that specifies expected authorities or trust frameworks that certify Issuers, that the Verifier will accept. Every Credential returned by the Wallet SHOULD match at least one of the conditions present in the corresponding trusted_authorities array if present.
TrustedAuthorities []TrustedAuthorityQuery `json:"trusted_authorities,omitempty" mapstructure:"trusted_authorities,omitempty"`
}
// RequiresCryptographicHolderBinding returns true when the field is nil (absent) or explicitly true.
func (cq *CredentialQuery) RequiresCryptographicHolderBinding() bool {
return cq.RequireCryptographicHolderBinding == nil || *cq.RequireCryptographicHolderBinding
}
func (cq *CredentialQuery) UnmarshalJSON(data []byte) error {
type Alias CredentialQuery
aux := &struct{ *Alias }{Alias: (*Alias)(cq)}
if err := json.Unmarshal(data, aux); err != nil {
return err
}
if cq.RequireCryptographicHolderBinding == nil {
t := true
cq.RequireCryptographicHolderBinding = &t
}
return nil
}
func (cq CredentialQuery) MarshalJSON() ([]byte, error) {
type Alias CredentialQuery
if cq.RequireCryptographicHolderBinding == nil {
t := true
cq.RequireCryptographicHolderBinding = &t
}
return json.Marshal((Alias)(cq))
}
// ClaimsQuery is a query to specifies claims in the requested Credential.
type ClaimsQuery struct {
// REQUIRED if claim_sets is present in the Credential Query; OPTIONAL otherwise. A string identifying the particular claim. The value MUST be a non-empty string consisting of alphanumeric, underscore (_), or hyphen (-) characters. Within the particular claims array, the same id MUST NOT be present more than once.
Id string `json:"id,omitempty" mapstructure:"id,omitempty"`
// The value MUST be a non-empty array representing a claims path pointer that specifies the path to a claim within the Credential. See https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-claims-path-pointer
Path []interface{} `json:"path,omitempty" mapstructure:"path,omitempty"`
// A non-empty array of strings, integers or boolean values that specifies the expected values of the claim. If the values property is present, the Wallet SHOULD return the claim only if the type and value of the claim both match exactly for at least one of the elements in the array.
Values []interface{} `json:"values,omitempty" mapstructure:"values,omitempty"`
// MDoc specific parameter, ignored for all other types. The flag can be set to inform that the reader wishes to keep(store) the data. In case of false, its data is only used to be dispalyed and verified.
IntentToRetain bool `json:"intent_to_retain,omitempty" mapstructure:"intent_to_retain,omitempty"`
// MDoc specific parameter, ignored for all other types. Refers to a namespace inside an mdoc.
Namespace string `json:"namespace,omitempty" mapstructure:"namespace,omitempty"`
// MDoc specific parameter, ignored for all other types. Identifier for the data-element in the namespace.
ClaimName string `json:"claim_name,omitempty" mapstructure:"claim_name,omitempty"`
}
// MetaDataQuery defines additional properties requested by the Verifier that apply to the metadata and validity data of the Credential.
type MetaDataQuery struct {
// SD-JWT and JWT specific parameter. A non-empty array of strings that specifies allowed values for the type of the requested Verifiable Credential.The Wallet MAY return Credentials that inherit from any of the specified types, following the inheritance logic defined in https://datatracker.ietf.org/doc/html/draft-ietf-oauth-sd-jwt-vc-10
VctValues []string `json:"vct_values,omitempty" mapstructure:"vct_values,omitempty"`
// Required for MDoc. String that specifies an allowed value for the doctype of the requested Verifiable Credential. It MUST be a valid doctype identifier as defined in https://www.iso.org/standard/69084.html
DoctypeValue string `json:"doctype_value,omitempty" mapstructure:"doctype_value,omitempty"`
// Required for ldp_vc. A non-empty array of string arrays. The Type value of the credential needs to be a subset of at least one of the string-arrays.
TypeValues [][]string `json:"type_values,omitempty" mapstructure:"type_values,omitempty"`
}
// TrustedAuthorityQuery is an object representing information that helps to identify an authority or the trust framework that certifies Issuers.
type TrustedAuthorityQuery struct {
// A string uniquely identifying the type of information about the issuer trust framework.
Type string `json:"type" mapstructure:"type"`
// A non-empty array of strings, where each string (value) contains information specific to the used Trusted Authorities Query type that allows the identification of an issuer, a trust framework, or a federation that an issuer belongs to.
Values []string `json:"values" mapstructure:"values"`
}
// CredentialSetQuery is a Credential Set Query is an object representing a request for one or more Credentials to satisfy a particular use case with the Verifier.
type CredentialSetQuery struct {
// A non-empty array, where each value in the array is a list of Credential Query identifiers representing one set of Credentials that satisfies the use case. The value of each element in the options array is a non-empty array of identifiers which reference elements in credentials.
Options [][]string `json:"options,omitempty" mapstructure:"options,omitempty"`
// A boolean which indicates whether this set of Credentials is required to satisfy the particular use case at the Verifier.
Required bool `json:"required,omitempty" mapstructure:"required,omitempty"`
// A string, number or object specifying the purpose of the query. This specification does not define a specific structure or specific values for this property. The purpose is intended to be used by the Verifier to communicate the reason for the query to the Wallet. The Wallet MAY use this information to show the user the reason for the request.
Purpose interface{} `json:"purpose,omitempty" mapstructure:"purpose,omitempty"`
}
func (cs ConfiguredService) GetRequiredCredentialTypes(scope string) (types []string, err error) {
credentials, err := cs.GetCredentials(scope)
if err != nil {
return types, err
}
for _, credential := range credentials {
types = append(types, credential.Type)
}
return types, err
}
func (cs ConfiguredService) GetScope(scope string) (scopeEntry ScopeEntry, err error) {
scopeEntry, exists := cs.ServiceScopes[scope]
if !exists {
return scopeEntry, ErrorNoSuchScope
}
return scopeEntry, nil
}
func (cs ConfiguredService) GetCredentials(scope string) (credentials []Credential, err error) {
scopeEntry, err := cs.GetScope(scope)
if err != nil {
return credentials, err
}
return scopeEntry.Credentials, err
}
func (cs ConfiguredService) GetPresentationDefinition(scope string) (pd *PresentationDefinition, err error) {
scopeEntry, err := cs.GetScope(scope)
if err != nil {
return pd, err
}
return scopeEntry.PresentationDefinition, err
}
func (cs ConfiguredService) GetDcqlQuery(scope string) (dcql *DCQL, err error) {
scopeEntry, err := cs.GetScope(scope)
if err != nil {
return dcql, err
}
return scopeEntry.DCQL, err
}
func (cs ConfiguredService) GetCredential(scope, credentialType string) (Credential, bool) {
credentials, err := cs.GetCredentials(scope)
if err == nil {
for _, credential := range credentials {
if credential.Type == credentialType {
return credential, true
}
}
}
return Credential{}, false
}
func NewCCSHttpClient(configEndpoint string) (client ConfigClient, err error) {
// no need for a caching client here, since the repo handles the "caching"
httpClient := &http.Client{}
return HttpConfigClient{httpClient, getServiceUrl(configEndpoint)}, err
}
func (hcc HttpConfigClient) GetServices() (services []ConfiguredService, err error) {
var currentPage = 0
var pageSize = 100
var finished = false
services = []ConfiguredService{}
for !finished {
servicesResponse, err := hcc.getServicesPage(currentPage, pageSize)
if err != nil {
logging.Log().Warnf("Failed to receive services page %v with size %v. Err: %v", currentPage, pageSize, err)
return nil, err
}
services = append(services, servicesResponse.Services...)
// we check both, since its possible that during the iteration new services where added to old pages(total != len(services)).
// those will be retrieved on next iteration, thus can be ignored
if servicesResponse.Total == 0 || len(servicesResponse.Services) < pageSize || servicesResponse.Total == len(services) {
finished = true
}
currentPage++
}
return services, err
}
func (hcc HttpConfigClient) getServicesPage(page int, pageSize int) (servicesResponse ServicesResponse, err error) {
logging.Log().Debugf("Retrieve services from %s for page %v and size %v.", hcc.configEndpoint, page, pageSize)
resp, err := hcc.client.Get(fmt.Sprintf("%s?pageSize=%v&page=%v", hcc.configEndpoint, pageSize, page))
if err != nil {
logging.Log().Warnf("Was not able to get the services from %s. Err: %v", hcc.configEndpoint, err)
return servicesResponse, err
}
if resp == nil {
logging.Log().Warnf("Was not able to get any response for from %s.", hcc.configEndpoint)
return servicesResponse, ErrorCcsNoResponse
}
if resp.StatusCode != 200 {
logging.Log().Warnf("Was not able to get the services from %s. Stauts: %v", hcc.configEndpoint, resp.StatusCode)
return servicesResponse, ErrorCcsErrorResponse
}
if resp.Body == nil {
logging.Log().Info("Received an empty body from the ccs.")
return servicesResponse, ErrorCcsEmptyResponse
}
err = json.NewDecoder(resp.Body).Decode(&servicesResponse)
if err != nil {
logging.Log().Warn("Was not able to decode the ccs-response.")
return servicesResponse, err
}
logging.Log().Debugf("Services response was: %s.", logging.PrettyPrintObject(servicesResponse))
return servicesResponse, err
}
func getServiceUrl(endpoint string) string {
if strings.HasSuffix(endpoint, "/") {
return endpoint + SERVICES_PATH
} else {
return endpoint + "/" + SERVICES_PATH
}
}