Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,17 @@ type Verifier struct {
SessionExpiry int `mapstructure:"sessionExpiry" default:"30"`
// scope to be used in the authentication request
RequestScope string `mapstructure:"requestScope"`
// policies that shall be checked
PolicyConfig Policies `mapstructure:"policies"`
}

type Policies struct {
// policies that all credentials are checked against
DefaultPolicies PolicyMap `mapstructure:"default"`
// policies that used to check specific credential types. Key maps to the "credentialSubject.type" of the credential
CredentialTypeSpecificPolicies map[string]PolicyMap `mapstructure:"credentialTypeSpecific"`
}

type PolicyMap map[string]PolicyConfigParameters

type PolicyConfigParameters map[string]interface{}
9 changes: 8 additions & 1 deletion config/data/config_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ verifier:
did: "did:key:somekey"
tirAddress: "https://test.dev/trusted_issuer/v3/issuers/"
sessionExpiry: 30

policies:
default:
SignaturePolicy: {}
TrustedIssuerRegistryPolicy:
registryAddress: "waltId.com"
credentialTypeSpecific:
"gx:compliance":
ValidFromBeforePolicy: {}
ssiKit:
auditorURL: http://waltid:7003
13 changes: 13 additions & 0 deletions config/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ func Test_ReadConfig(t *testing.T) {
TirAddress: "https://test.dev/trusted_issuer/v3/issuers/",
SessionExpiry: 30,
RequestScope: "",
PolicyConfig: Policies{
DefaultPolicies: PolicyMap{
"SignaturePolicy": {},
"TrustedIssuerRegistryPolicy": {
"registryAddress": "waltId.com",
},
},
CredentialTypeSpecificPolicies: map[string]PolicyMap{
"gx:compliance": {
"ValidFromBeforePolicy": {},
},
},
},
}, SSIKit: SSIKit{
AuditorURL: "http://waltid:7003",
},
Expand Down
56 changes: 56 additions & 0 deletions gaiax/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package gaiax

import (
"encoding/json"
"errors"
"net/http"

"github.com/fiware/VCVerifier/logging"
)

var ErrorRegistry = errors.New("gaiax_registry_failed_to_answer_properly")

type RegistryClient interface {
// Get the list of DIDs of the trustable issuers
GetComplianceIssuers() ([]string, error)
}

type GaiaXRegistryClient struct {
endpoint string
}

func InitGaiaXRegistryVerifier(url string) RegistryClient {
return &GaiaXRegistryClient{url}
}

// TODO Could propably cache the response very generously as new issuers are not added often
func (rc *GaiaXRegistryClient) GetComplianceIssuers() ([]string, error) {
response, err := http.Get(rc.endpoint)

if err != nil {
logging.Log().Warnf("Did not receive a valid issuers list response. Err: %v", err)
return []string{}, err
}
if response == nil {
logging.Log().Warn("Did not receive any response from gaia-x registry.")
return []string{}, ErrorRegistry
}
if response.StatusCode != 200 {
logging.Log().Warnf("Did not receive an ok from the registry. Was %s", logging.PrettyPrintObject(response))
return []string{}, ErrorRegistry
}
if response.Body == nil {
logging.Log().Warn("Received an empty body for the issuers list.")
return []string{}, ErrorRegistry
}
var issuers []string

err = json.NewDecoder(response.Body).Decode(&issuers)
if err != nil {
logging.Log().Warnf("Was not able to decode the issuers list. Was %s", logging.PrettyPrintObject(response))
return []string{}, err
}
logging.Log().Infof("%d issuer dids received.", len(issuers))
logging.Log().Debugf("Issuers are %v", logging.PrettyPrintObject(issuers))
return issuers, nil
}
79 changes: 79 additions & 0 deletions gaiax/registry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package gaiax

import (
"net/http"
"net/http/httptest"
"reflect"
"testing"
)

func TestGaiaXRegistryClient_GetComplianceIssuers(t *testing.T) {

type fields struct {
response string
responseCode int
}
tests := []struct {
name string
fields fields
want []string
wantErr bool
}{
{
"Should return one did",
fields{
`["did:web:compliance.test.com"]`,
200,
},
[]string{"did:web:compliance.test.com"},
false,
},
{
"Should return multiple dids",
fields{
`["did:web:compliance.test.com","did:key:123"]`,
200,
},
[]string{"did:web:compliance.test.com", "did:key:123"},
false,
},
{
"Should return error when malformatted",
fields{
`{"someThing":"else"}`,
200,
},
[]string{},
true,
},
{
"Should return error when http error",
fields{
``,
500,
},
[]string{},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(tt.fields.responseCode)
w.Write([]byte(tt.fields.response))
}))
defer server.Close()

rc := InitGaiaXRegistryVerifier(server.URL)

got, err := rc.GetComplianceIssuers()
if (err != nil) != tt.wantErr {
t.Errorf("GaiaXRegistryClient.GetComplianceIssuers() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GaiaXRegistryClient.GetComplianceIssuers() = %v, want %v", got, tt.want)
}
})
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/hellofresh/health-go/v5 v5.0.0
github.com/lestrrat-go/jwx v1.2.25
github.com/patrickmn/go-cache v2.1.0+incompatible
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561
)

require (
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down
14 changes: 13 additions & 1 deletion server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ logging:
verifier:
tirAddress: https://tir.de
did: did:key:myverifier

policies:
default:
SignaturePolicy: {}
IssuedDateBeforePolicy: {}
ValidFromBeforePolicy: {}
ExpirationDateAfterPolicy: {}
EbsiTrustedIssuerRegistryPolicy:
registryAddress: https://tir.de
issuerType: Undefined
credentialTypeSpecific:
"gx:compliance":
GaiaXComplianceIssuer:
registryAddress: https://registry.gaia-x.fiware.dev/development/api/complianceIssuers
ssiKit:
auditorURL: http://my-auditor
18 changes: 14 additions & 4 deletions ssikit/ssikit.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"

configModel "github.com/fiware/VCVerifier/config"
Expand Down Expand Up @@ -31,12 +32,21 @@ type Policy struct {
Argument *TirArgument `json:"argument,omitempty"`
}

// TrustedIssuerRegistry Policy Argument - has to be provided to waltId
type TirArgument struct {
RegistryAddress string `json:"registryAddress"`
IssuerType string `json:"issuerType"`
// Create a policy as defined by waltId FIXME filter out policies that are not covered by waltId
func CreatePolicy(name string, arguments map[string]interface{}) (policy Policy) {
policy = Policy{name, nil}
if len(arguments) > 0 {
policy.Argument = &TirArgument{}
for name, value := range arguments {
(*policy.Argument)[name] = fmt.Sprintf("%v", value)
}
}
return
}

// TrustedIssuerRegistry Policy Argument - has to be provided to waltId
type TirArgument map[string]string

// request structure for validating VCs at waltId
type verificationRequest struct {
Policies []Policy `json:"policies"`
Expand Down
45 changes: 45 additions & 0 deletions ssikit/ssikit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"net/http"
"net/url"
"reflect"
"testing"

configModel "github.com/fiware/VCVerifier/config"
Expand Down Expand Up @@ -128,3 +129,47 @@ func getVC(id string) map[string]interface{} {
},
}
}

func TestCreatePolicy(t *testing.T) {
type args struct {
name string
arguments map[string]interface{}
}
tests := []struct {
name string
args args
wantPolicy Policy
}{
{
"Policy without arguments",
args{
"testPolicy",
map[string]interface{}{},
},
Policy{
"testPolicy",
nil,
},
},
{
"Policy with arguments",
args{
"testPolicy",
map[string]interface{}{
"arg1":"something",
},
},
Policy{
"testPolicy",
&TirArgument{"arg1":"something"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotPolicy := CreatePolicy(tt.args.name, tt.args.arguments); !reflect.DeepEqual(gotPolicy, tt.wantPolicy) {
t.Errorf("CreatePolicy() = %v, want %v", gotPolicy, tt.wantPolicy)
}
})
}
}
63 changes: 63 additions & 0 deletions verifier/gaiax.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package verifier

import (
"fmt"

configModel "github.com/fiware/VCVerifier/config"
"github.com/fiware/VCVerifier/gaiax"
"golang.org/x/exp/slices"

logging "github.com/fiware/VCVerifier/logging"
)

const gaiaxCompliancePolicy = "GaiaXComplianceIssuer"
const registryUrlPropertyName = "registryAddress"

type GaiaXRegistryVerifier struct {
validateAll bool
credentialTypesToValidate []string
// client for gaiax registry connection
gaiaxRegistryClient gaiax.RegistryClient
}

func InitGaiaXRegistryVerifier(verifierConfig *configModel.Verifier) GaiaXRegistryVerifier {
var url string
verifier := GaiaXRegistryVerifier{credentialTypesToValidate: []string{}}

for policyName, arguments := range verifierConfig.PolicyConfig.DefaultPolicies {
if policyName == gaiaxCompliancePolicy {
url = fmt.Sprintf("%v", arguments[registryUrlPropertyName])
verifier.validateAll = true
}
}
for credentialType, policies := range verifierConfig.PolicyConfig.CredentialTypeSpecificPolicies {
for policyName, arguments := range policies {
if policyName == gaiaxCompliancePolicy {
url = fmt.Sprintf("%v", arguments[registryUrlPropertyName])
verifier.credentialTypesToValidate = append(verifier.credentialTypesToValidate, credentialType)
}
}
}
if len(url) > 0 {
verifier.gaiaxRegistryClient = gaiax.InitGaiaXRegistryVerifier(url)
}
return verifier
}

func (v *GaiaXRegistryVerifier) VerifyVC(verifiableCredential VerifiableCredential) (result bool, err error) {
if v.validateAll || slices.Contains(v.credentialTypesToValidate, verifiableCredential.GetCredentialType()) {
issuerDids, err := v.gaiaxRegistryClient.GetComplianceIssuers()
if err != nil {
return false, err
}
if slices.Contains(issuerDids, verifiableCredential.GetIssuer()) {
logging.Log().Info("Credential was issued by trusted issuer")
return true, nil
} else {
logging.Log().Warnf("Failed to verify credential %s. Issuer was not in trusted issuer list", logging.PrettyPrintObject(verifiableCredential))
return false, nil
}
}
// No need to validate
return true, nil
}
Loading