From 722e7a1b540f1980b7454e1dcee55644bb008cdd Mon Sep 17 00:00:00 2001 From: reinkrul Date: Wed, 27 Sep 2023 12:50:32 +0200 Subject: [PATCH] Refactor VDR's didservice and types packages (#2513) --- api/ssi_types_test.go | 6 +- auth/api/iam/api.go | 8 +- auth/api/iam/api_test.go | 11 +- auth/api/iam/openid4vp_test.go | 4 +- auth/api/iam/s2s_vptoken.go | 4 +- auth/api/iam/s2s_vptoken_test.go | 6 +- auth/api/iam/types.go | 4 +- auth/auth.go | 10 +- auth/auth_test.go | 4 +- auth/services/notary/notary.go | 10 +- auth/services/notary/notary_test.go | 15 +- auth/services/oauth/authz_server.go | 19 +- auth/services/oauth/authz_server_test.go | 66 +- auth/services/oauth/relying_party.go | 11 +- auth/services/oauth/relying_party_test.go | 20 +- auth/test.go | 4 +- cmd/root.go | 4 +- crypto/api/v1/api.go | 14 +- crypto/api/v1/api_test.go | 28 +- didman/api/v1/api.go | 17 +- didman/api/v1/api_test.go | 51 +- didman/didman.go | 38 +- didman/didman_test.go | 85 ++- golden_hammer/module.go | 28 +- golden_hammer/module_test.go | 42 +- makefile | 7 +- network/dag/keys.go | 19 +- network/dag/keys_test.go | 6 +- network/dag/pal.go | 6 +- network/dag/pal_test.go | 18 +- network/dag/verifier.go | 4 +- network/dag/verifier_test.go | 6 +- network/network.go | 20 +- network/network_integration_test.go | 6 +- network/network_test.go | 40 +- network/transport/grpc/authenticator.go | 11 +- network/transport/grpc/authenticator_test.go | 14 +- network/transport/v2/protocol.go | 6 +- .../transport/v2/protocol_integration_test.go | 4 +- network/transport/v2/protocol_test.go | 6 +- vcr/api/openid4vci/v0/api.go | 4 +- vcr/api/openid4vci/v0/holder_test.go | 10 +- vcr/api/openid4vci/v0/issuer_test.go | 24 +- vcr/api/vcr/v2/api.go | 9 +- vcr/api/vcr/v2/api_test.go | 8 +- vcr/credential/validator.go | 4 +- vcr/holder/openid.go | 8 +- vcr/holder/openid_test.go | 10 +- vcr/holder/wallet.go | 8 +- vcr/holder/wallet_test.go | 30 +- vcr/issuer/issuer.go | 19 +- vcr/issuer/issuer_test.go | 6 +- vcr/issuer/keyresolver.go | 6 +- vcr/issuer/keyresolver_test.go | 14 +- vcr/issuer/network_publisher.go | 15 +- vcr/issuer/network_publisher_test.go | 38 +- vcr/issuer/openid.go | 11 +- vcr/issuer/openid_test.go | 12 +- vcr/openid4vci/identifiers.go | 11 +- vcr/openid4vci/identifiers_test.go | 27 +- vcr/store_test.go | 12 +- vcr/test.go | 15 +- vcr/test/openid4vci_integration_test.go | 12 +- vcr/vcr.go | 14 +- vcr/vcr_test.go | 25 +- vcr/verifier/verifier.go | 16 +- vcr/verifier/verifier_test.go | 50 +- vdr/api/v1/api.go | 23 +- vdr/api/v1/api_test.go | 60 +- vdr/api/v1/client_test.go | 6 +- vdr/api/v1/types.go | 19 +- vdr/cmd/cmd.go | 16 +- vdr/did_owner.go | 16 +- vdr/did_owner_test.go | 31 +- vdr/didjwk/resolver.go | 9 +- vdr/didnuts/ambassador.go | 18 +- vdr/didnuts/ambassador_test.go | 45 +- vdr/didnuts/creator.go | 29 +- vdr/didnuts/creator_test.go | 38 +- vdr/didnuts/didstore/finder.go | 7 +- vdr/didnuts/didstore/finder_test.go | 12 +- vdr/didnuts/didstore/integration_test.go | 12 +- vdr/didnuts/didstore/interface.go | 9 +- vdr/didnuts/didstore/metadata.go | 6 +- vdr/didnuts/didstore/mock.go | 11 +- vdr/didnuts/didstore/reader.go | 7 +- vdr/didnuts/didstore/reader_test.go | 4 +- vdr/didnuts/didstore/store.go | 19 +- vdr/didnuts/didstore/store_test.go | 34 +- vdr/didnuts/manipulator.go | 19 +- vdr/didnuts/manipulator_test.go | 35 +- vdr/didnuts/resolver.go | 23 +- vdr/didnuts/resolver_test.go | 100 +-- vdr/didnuts/validators.go | 15 +- vdr/didnuts/validators_test.go | 17 +- vdr/didservice/resolvers.go | 199 ------ vdr/didservice/resolvers_test.go | 286 -------- vdr/didservice/test.go | 29 - vdr/didservice/util.go | 103 --- vdr/didservice/util_test.go | 99 --- vdr/didweb/web.go | 8 +- vdr/integration_test.go | 11 +- vdr/interface.go | 43 ++ vdr/{didservice => management}/finder.go | 32 +- vdr/management/finder_mock.go | 96 +++ vdr/{didservice => management}/finder_test.go | 24 +- vdr/management/management.go | 112 +++ vdr/management/management_mock.go | 213 ++++++ vdr/management/management_test.go | 56 ++ vdr/mock.go | 149 ++++ vdr/{types/common.go => resolver/did.go} | 106 +-- vdr/resolver/did_mock.go | 55 ++ vdr/resolver/did_test.go | 186 +++++ vdr/resolver/key.go | 138 ++++ vdr/resolver/key_mock.go | 112 +++ vdr/resolver/key_test.go | 107 +++ vdr/resolver/service.go | 176 +++++ vdr/resolver/service_mock.go | 70 ++ vdr/resolver/service_test.go | 183 +++++ vdr/types/common_test.go | 122 ---- vdr/types/interface.go | 188 ----- vdr/types/mock.go | 646 ------------------ vdr/vdr.go | 82 +-- vdr/vdr_test.go | 70 +- 124 files changed, 2744 insertions(+), 2697 deletions(-) delete mode 100644 vdr/didservice/resolvers.go delete mode 100644 vdr/didservice/resolvers_test.go delete mode 100644 vdr/didservice/test.go delete mode 100644 vdr/didservice/util.go delete mode 100644 vdr/didservice/util_test.go create mode 100644 vdr/interface.go rename vdr/{didservice => management}/finder.go (62%) create mode 100644 vdr/management/finder_mock.go rename vdr/{didservice => management}/finder_test.go (78%) create mode 100644 vdr/management/management.go create mode 100644 vdr/management/management_mock.go create mode 100644 vdr/management/management_test.go create mode 100644 vdr/mock.go rename vdr/{types/common.go => resolver/did.go} (54%) create mode 100644 vdr/resolver/did_mock.go create mode 100644 vdr/resolver/did_test.go create mode 100644 vdr/resolver/key.go create mode 100644 vdr/resolver/key_mock.go create mode 100644 vdr/resolver/key_test.go create mode 100644 vdr/resolver/service.go create mode 100644 vdr/resolver/service_mock.go create mode 100644 vdr/resolver/service_test.go delete mode 100644 vdr/types/common_test.go delete mode 100644 vdr/types/interface.go delete mode 100644 vdr/types/mock.go diff --git a/api/ssi_types_test.go b/api/ssi_types_test.go index ad6825b3eb..35f8fc22ce 100644 --- a/api/ssi_types_test.go +++ b/api/ssi_types_test.go @@ -31,7 +31,7 @@ import ( "github.com/nuts-foundation/go-did/vc" "github.com/nuts-foundation/nuts-node/crypto/hash" vcr "github.com/nuts-foundation/nuts-node/vcr/api/vcr/v2" - vdrTypes "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) const ( @@ -183,8 +183,8 @@ func createDidDocument() did.Document { } } -func createDidDocumentMetadata() vdrTypes.DocumentMetadata { - return vdrTypes.DocumentMetadata{ +func createDidDocumentMetadata() resolver.DocumentMetadata { + return resolver.DocumentMetadata{ Created: time.Now(), Hash: hash.RandomHash(), Deactivated: true, diff --git a/auth/api/iam/api.go b/auth/api/iam/api.go index 816de10f7f..109a21089c 100644 --- a/auth/api/iam/api.go +++ b/auth/api/iam/api.go @@ -30,8 +30,8 @@ import ( "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/vcr" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "html/template" "net/http" "sync" @@ -206,7 +206,7 @@ func (r Wrapper) OAuthAuthorizationServerMetadata(ctx context.Context, request O owned, err := r.vdr.IsOwner(ctx, ownDID) if err != nil { - if didservice.IsFunctionalResolveError(err) { + if resolver.IsFunctionalResolveError(err) { return nil, core.NotFoundError("authz server metadata: %w", err) } log.Logger().WithField("did", ownDID.String()).Errorf("authz server metadata: failed to assert ownership of did: %s", err.Error()) @@ -227,7 +227,7 @@ func (r Wrapper) GetWebDID(ctx context.Context, request GetWebDIDRequestObject) document, err := r.vdr.DeriveWebDIDDocument(ctx, baseURL, ownDID) if err != nil { - if didservice.IsFunctionalResolveError(err) { + if resolver.IsFunctionalResolveError(err) { return GetWebDID404Response{}, nil } log.Logger().WithError(err).Errorf("Could not resolve Nuts DID: %s", ownDID.String()) diff --git a/auth/api/iam/api_test.go b/auth/api/iam/api_test.go index 61bd6bc3ae..3b2d25b524 100644 --- a/auth/api/iam/api_test.go +++ b/auth/api/iam/api_test.go @@ -25,7 +25,8 @@ import ( "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/auth" "github.com/nuts-foundation/nuts-node/core" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -60,7 +61,7 @@ func TestWrapper_OAuthAuthorizationServerMetadata(t *testing.T) { t.Run("error - did does not exist", func(t *testing.T) { //404 ctx := newTestClient(t) - ctx.vdr.EXPECT().IsOwner(nil, testDID).Return(false, vdr.ErrNotFound) + ctx.vdr.EXPECT().IsOwner(nil, testDID).Return(false, resolver.ErrNotFound) res, err := ctx.client.OAuthAuthorizationServerMetadata(nil, OAuthAuthorizationServerMetadataRequestObject{Id: testDID.ID}) @@ -105,7 +106,7 @@ func TestWrapper_GetWebDID(t *testing.T) { }) t.Run("unknown DID", func(t *testing.T) { test := newTestClient(t) - test.vdr.EXPECT().DeriveWebDIDDocument(ctx, *webDIDBaseURL, nutsDID).Return(nil, vdr.ErrNotFound) + test.vdr.EXPECT().DeriveWebDIDDocument(ctx, *webDIDBaseURL, nutsDID).Return(nil, resolver.ErrNotFound) response, err := test.client.GetWebDID(ctx, GetWebDIDRequestObject{nutsDID.ID}) @@ -168,7 +169,7 @@ type testCtx struct { client *Wrapper authnServices *auth.MockAuthenticationServices vdr *vdr.MockVDR - resolver *vdr.MockDIDResolver + resolver *resolver.MockDIDResolver } func newTestClient(t testing.TB) *testCtx { @@ -177,7 +178,7 @@ func newTestClient(t testing.TB) *testCtx { ctrl := gomock.NewController(t) authnServices := auth.NewMockAuthenticationServices(ctrl) authnServices.EXPECT().PublicURL().Return(publicURL).AnyTimes() - resolver := vdr.NewMockDIDResolver(ctrl) + resolver := resolver.NewMockDIDResolver(ctrl) vdr := vdr.NewMockVDR(ctrl) vdr.EXPECT().Resolver().Return(resolver).AnyTimes() return &testCtx{ diff --git a/auth/api/iam/openid4vp_test.go b/auth/api/iam/openid4vp_test.go index 32ff266848..effc889e6d 100644 --- a/auth/api/iam/openid4vp_test.go +++ b/auth/api/iam/openid4vp_test.go @@ -29,7 +29,7 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/credential" "github.com/nuts-foundation/nuts-node/vcr/holder" "github.com/nuts-foundation/nuts-node/vcr/pe" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -93,7 +93,7 @@ func TestWrapper_handlePresentationRequest(t *testing.T) { ctrl := gomock.NewController(t) peStore := &pe.DefinitionResolver{} _ = peStore.LoadFromFile("test/presentation_definition_mapping.json") - mockVDR := types.NewMockVDR(ctrl) + mockVDR := vdr.NewMockVDR(ctrl) mockVCR := vcr.NewMockVCR(ctrl) mockWallet := holder.NewMockWallet(ctrl) mockVCR.EXPECT().Wallet().Return(mockWallet) diff --git a/auth/api/iam/s2s_vptoken.go b/auth/api/iam/s2s_vptoken.go index 2cc37a1b56..65fa221404 100644 --- a/auth/api/iam/s2s_vptoken.go +++ b/auth/api/iam/s2s_vptoken.go @@ -24,7 +24,7 @@ import ( "github.com/labstack/echo/v4" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/core" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" ) @@ -89,7 +89,7 @@ func (r Wrapper) RequestAccessToken(ctx context.Context, request RequestAccessTo } _, _, err = r.vdr.Resolver().Resolve(*requestVerifier, nil) if err != nil { - if errors.Is(err, types.ErrNotFound) { + if errors.Is(err, resolver.ErrNotFound) { return nil, core.InvalidInputError("verifier not found: %w", err) } return nil, err diff --git a/auth/api/iam/s2s_vptoken_test.go b/auth/api/iam/s2s_vptoken_test.go index e61e982c6d..e438585179 100644 --- a/auth/api/iam/s2s_vptoken_test.go +++ b/auth/api/iam/s2s_vptoken_test.go @@ -19,7 +19,7 @@ package iam import ( - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "testing" "github.com/nuts-foundation/go-did/did" @@ -35,7 +35,7 @@ func TestWrapper_RequestAccessToken(t *testing.T) { t.Run("ok", func(t *testing.T) { ctx := newTestClient(t) ctx.vdr.EXPECT().IsOwner(nil, walletDID).Return(true, nil) - ctx.resolver.EXPECT().Resolve(verifierDID, nil).Return(&did.Document{}, &types.DocumentMetadata{}, nil) + ctx.resolver.EXPECT().Resolve(verifierDID, nil).Return(&did.Document{}, &resolver.DocumentMetadata{}, nil) _, err := ctx.client.RequestAccessToken(nil, RequestAccessTokenRequestObject{Did: walletDID.String(), Body: body}) @@ -78,7 +78,7 @@ func TestWrapper_RequestAccessToken(t *testing.T) { t.Run("verifier not found", func(t *testing.T) { ctx := newTestClient(t) ctx.vdr.EXPECT().IsOwner(nil, walletDID).Return(true, nil) - ctx.resolver.EXPECT().Resolve(verifierDID, nil).Return(nil, nil, types.ErrNotFound) + ctx.resolver.EXPECT().Resolve(verifierDID, nil).Return(nil, nil, resolver.ErrNotFound) _, err := ctx.client.RequestAccessToken(nil, RequestAccessTokenRequestObject{Did: walletDID.String(), Body: body}) diff --git a/auth/api/iam/types.go b/auth/api/iam/types.go index 4a85bce4d5..901000ab96 100644 --- a/auth/api/iam/types.go +++ b/auth/api/iam/types.go @@ -20,14 +20,14 @@ package iam import ( "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) // DIDDocument is an alias type DIDDocument = did.Document // DIDDocumentMetadata is an alias -type DIDDocumentMetadata = types.DocumentMetadata +type DIDDocumentMetadata = resolver.DocumentMetadata const ( // responseTypeParam is the name of the response_type parameter. diff --git a/auth/auth.go b/auth/auth.go index ebca45a765..62e010a726 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -22,8 +22,8 @@ import ( "errors" "fmt" "github.com/nuts-foundation/nuts-node/vcr/pe" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/url" "path" "time" @@ -55,7 +55,7 @@ type Auth struct { vcr vcr.VCR pkiProvider pki.Provider shutdownFunc func() - vdrInstance types.VDR + vdrInstance vdr.VDR publicURL *url.URL presentationDefinitions *pe.DefinitionResolver } @@ -90,7 +90,7 @@ func (auth *Auth) PresentationDefinitions() *pe.DefinitionResolver { } // NewAuthInstance accepts a Config with several Nuts Engines and returns an instance of Auth -func NewAuthInstance(config Config, vdrInstance types.VDR, vcr vcr.VCR, keyStore crypto.KeyStore, serviceResolver didman.CompoundServiceResolver, jsonldManager jsonld.JSONLD, pkiProvider pki.Provider) *Auth { +func NewAuthInstance(config Config, vdrInstance vdr.VDR, vcr vcr.VCR, keyStore crypto.KeyStore, serviceResolver didman.CompoundServiceResolver, jsonldManager jsonld.JSONLD, pkiProvider pki.Provider) *Auth { return &Auth{ config: config, jsonldManager: jsonldManager, @@ -145,7 +145,7 @@ func (auth *Auth) Configure(config core.ServerConfig) error { ContractValidators: auth.config.ContractValidators, ContractValidity: contractValidity, StrictMode: config.Strictmode, - }, auth.vcr, didservice.KeyResolver{Resolver: auth.vdrInstance.Resolver()}, auth.keyStore, auth.jsonldManager, auth.pkiProvider) + }, auth.vcr, resolver.DIDKeyResolver{Resolver: auth.vdrInstance.Resolver()}, auth.keyStore, auth.jsonldManager, auth.pkiProvider) tlsEnabled := config.TLS.Enabled() if config.Strictmode && !tlsEnabled { diff --git a/auth/auth_test.go b/auth/auth_test.go index 285e54b5dc..e980e2fc3d 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -22,7 +22,7 @@ import ( "github.com/nuts-foundation/nuts-node/crypto" "github.com/nuts-foundation/nuts-node/pki" "github.com/nuts-foundation/nuts-node/vcr" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr" "go.uber.org/mock/gomock" "testing" @@ -45,7 +45,7 @@ func TestAuth_Configure(t *testing.T) { pkiMock := pki.NewMockProvider(ctrl) pkiMock.EXPECT().AddTruststore(gomock.Any()) // uzi pkiMock.EXPECT().CreateTLSConfig(gomock.Any()) // tlsConfig - vdrInstance := types.NewMockVDR(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().Resolver().AnyTimes() i := NewAuthInstance(config, vdrInstance, vcr.NewTestVCRInstance(t), crypto.NewMemoryCryptoInstance(), nil, nil, pkiMock) diff --git a/auth/services/notary/notary.go b/auth/services/notary/notary.go index 739b485548..6bc457a912 100644 --- a/auth/services/notary/notary.go +++ b/auth/services/notary/notary.go @@ -22,6 +22,7 @@ import ( "context" "errors" "fmt" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/nuts-foundation/nuts-node/core" "reflect" @@ -42,7 +43,6 @@ import ( "github.com/nuts-foundation/nuts-node/jsonld" "github.com/nuts-foundation/nuts-node/pki" "github.com/nuts-foundation/nuts-node/vcr" - "github.com/nuts-foundation/nuts-node/vdr/types" ) // ErrMissingOrganizationKey is used to indicate that this node has no private key of the indicated organization. @@ -78,7 +78,7 @@ func (c Config) hasContractValidator(cv string) bool { type notary struct { config Config jsonldManager jsonld.JSONLD - keyResolver types.KeyResolver + keyResolver resolver.KeyResolver privateKeyStore crypto.KeyStore verifiers map[string]contract.VPVerifier signers map[string]contract.Signer @@ -90,7 +90,7 @@ type notary struct { var timeNow = time.Now // NewNotary accepts the registry and crypto Nuts engines and returns a ContractNotary -func NewNotary(config Config, vcr vcr.VCR, keyResolver types.KeyResolver, keyStore crypto.KeyStore, jsonldManager jsonld.JSONLD, pkiValidator pki.Validator) services.ContractNotary { +func NewNotary(config Config, vcr vcr.VCR, keyResolver resolver.KeyResolver, keyStore crypto.KeyStore, jsonldManager jsonld.JSONLD, pkiValidator pki.Validator) services.ContractNotary { return ¬ary{ config: config, jsonldManager: jsonldManager, @@ -107,8 +107,8 @@ func NewNotary(config Config, vcr vcr.VCR, keyResolver types.KeyResolver, keySto // If the duration is 0 than the default duration is used. func (n *notary) DrawUpContract(ctx context.Context, template contract.Template, orgID did.DID, validFrom time.Time, validDuration time.Duration, organizationCredential *vc.VerifiableCredential) (*contract.Contract, error) { // Test if the org in managed by this node: - signingKeyID, _, err := n.keyResolver.ResolveKey(orgID, &validFrom, types.NutsSigningKeyType) - if errors.Is(err, types.ErrNotFound) { + signingKeyID, _, err := n.keyResolver.ResolveKey(orgID, &validFrom, resolver.NutsSigningKeyType) + if errors.Is(err, resolver.ErrNotFound) { return nil, services.InvalidContractRequestError{Message: "no valid organization credential at provided validFrom date"} } else if err != nil { return nil, fmt.Errorf("could not draw up contract: %w", err) diff --git a/auth/services/notary/notary_test.go b/auth/services/notary/notary_test.go index c2fb61d6c2..baaadc33f5 100644 --- a/auth/services/notary/notary_test.go +++ b/auth/services/notary/notary_test.go @@ -22,7 +22,7 @@ import ( "context" "encoding/json" "errors" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "testing" "time" @@ -35,7 +35,6 @@ import ( "github.com/nuts-foundation/nuts-node/jsonld" "github.com/nuts-foundation/nuts-node/vcr" "github.com/nuts-foundation/nuts-node/vdr" - "github.com/nuts-foundation/nuts-node/vdr/types" irma "github.com/privacybydesign/irmago" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -68,7 +67,7 @@ func TestContract_DrawUpContract(t *testing.T) { t.Run("draw up valid contract", func(t *testing.T) { test := buildContext(t) - test.keyResolver.EXPECT().ResolveKey(orgID, &validFrom, types.NutsSigningKeyType).Return(keyID, nil, nil) + test.keyResolver.EXPECT().ResolveKey(orgID, &validFrom, resolver.NutsSigningKeyType).Return(keyID, nil, nil) test.keyStore.EXPECT().Exists(ctx, keyID.String()).Return(true) test.vcr.EXPECT().Search(context.Background(), searchTerms, false, nil).Return([]vc.VerifiableCredential{testCredential}, nil) @@ -83,7 +82,7 @@ func TestContract_DrawUpContract(t *testing.T) { test := buildContext(t) defer test.ctrl.Finish() - test.keyResolver.EXPECT().ResolveKey(orgID, gomock.Any(), types.NutsSigningKeyType).Return(keyID, nil, nil) + test.keyResolver.EXPECT().ResolveKey(orgID, gomock.Any(), resolver.NutsSigningKeyType).Return(keyID, nil, nil) test.keyStore.EXPECT().Exists(ctx, keyID.String()).Return(true) drawnUpContract, err := test.notary.DrawUpContract(ctx, template, orgID, validFrom, duration, &testCredential) @@ -128,7 +127,7 @@ func TestContract_DrawUpContract(t *testing.T) { t.Run("nok - unknown organization", func(t *testing.T) { test := buildContext(t) - test.keyResolver.EXPECT().ResolveKey(orgID, &validFrom, gomock.Any()).Return(ssi.URI{}, nil, types.ErrNotFound) + test.keyResolver.EXPECT().ResolveKey(orgID, &validFrom, gomock.Any()).Return(ssi.URI{}, nil, resolver.ErrNotFound) drawnUpContract, err := test.notary.DrawUpContract(ctx, template, orgID, validFrom, duration, nil) @@ -239,7 +238,7 @@ func TestNewContractNotary(t *testing.T) { ContractValidity: 60 * time.Minute, }, vcr.NewTestVCRInstance(t), - didservice.KeyResolver{}, + resolver.DIDKeyResolver{}, crypto.NewMemoryCryptoInstance(), nil, nil, @@ -372,7 +371,7 @@ type testContext struct { signerMock *contract.MockSigner vcr *vcr.MockVCR - keyResolver *types.MockKeyResolver + keyResolver *resolver.MockKeyResolver keyStore *crypto.MockKeyStore notary notary } @@ -388,7 +387,7 @@ func buildContext(t *testing.T) *testContext { ctx := &testContext{ ctrl: ctrl, vcr: vcr.NewMockVCR(ctrl), - keyResolver: types.NewMockKeyResolver(ctrl), + keyResolver: resolver.NewMockKeyResolver(ctrl), keyStore: crypto.NewMockKeyStore(ctrl), signerMock: signerMock, } diff --git a/auth/services/oauth/authz_server.go b/auth/services/oauth/authz_server.go index f818247849..9bc72a9536 100644 --- a/auth/services/oauth/authz_server.go +++ b/auth/services/oauth/authz_server.go @@ -24,7 +24,7 @@ import ( "encoding/json" "errors" "fmt" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "time" "github.com/lestrrat-go/jwx/jwt" @@ -40,7 +40,6 @@ import ( "github.com/nuts-foundation/nuts-node/vcr" "github.com/nuts-foundation/nuts-node/vcr/credential" "github.com/nuts-foundation/nuts-node/vcr/verifier" - "github.com/nuts-foundation/nuts-node/vdr/types" ) const errInvalidIssuerFmt = "invalid jwt.issuer: %w" @@ -76,7 +75,7 @@ func (e ErrorResponse) Error() string { type authzServer struct { vcFinder vcr.Finder vcVerifier verifier.Verifier - keyResolver types.KeyResolver + keyResolver resolver.KeyResolver privateKeyStore nutsCrypto.KeyStore contractNotary services.ContractNotary serviceResolver didman.CompoundServiceResolver @@ -168,11 +167,11 @@ func (c validationContext) verifiableCredentials() ([]vc2.VerifiableCredential, // NewAuthorizationServer accepts a vendorID, and several Nuts engines and returns an implementation of services.AuthorizationServer func NewAuthorizationServer( - didResolver types.DIDResolver, vcFinder vcr.Finder, vcVerifier verifier.Verifier, + didResolver resolver.DIDResolver, vcFinder vcr.Finder, vcVerifier verifier.Verifier, serviceResolver didman.CompoundServiceResolver, privateKeyStore nutsCrypto.KeyStore, contractNotary services.ContractNotary, jsonldManager jsonld.JSONLD, accessTokenLifeSpan time.Duration) AuthorizationServer { return &authzServer{ - keyResolver: didservice.KeyResolver{Resolver: didResolver}, + keyResolver: resolver.DIDKeyResolver{Resolver: didResolver}, serviceResolver: serviceResolver, contractNotary: contractNotary, jsonldManager: jsonldManager, @@ -369,7 +368,7 @@ func (s *authzServer) validateIssuer(vContext *validationContext) error { } validationTime := vContext.jwtBearerToken.IssuedAt() - if _, err := s.keyResolver.ResolveKeyByID(vContext.kid, &validationTime, types.NutsSigningKeyType); err != nil { + if _, err := s.keyResolver.ResolveKeyByID(vContext.kid, &validationTime, resolver.NutsSigningKeyType); err != nil { return fmt.Errorf(errInvalidIssuerKeyFmt, err) } @@ -421,7 +420,7 @@ func (s *authzServer) validateSubject(ctx context.Context, validationCtx *valida validationCtx.authorizer = subject iat := validationCtx.jwtBearerToken.IssuedAt() - signingKeyID, _, err := s.keyResolver.ResolveKey(*subject, &iat, types.NutsSigningKeyType) + signingKeyID, _, err := s.keyResolver.ResolveKey(*subject, &iat, resolver.NutsSigningKeyType) if err != nil { return err } @@ -490,7 +489,7 @@ func (s *authzServer) parseAndValidateJwtBearerToken(context *validationContext) var kidHdr string token, err := nutsCrypto.ParseJWT(context.rawJwtBearerToken, func(kid string) (crypto.PublicKey, error) { kidHdr = kid - return s.keyResolver.ResolveKeyByID(kid, nil, types.NutsSigningKeyType) + return s.keyResolver.ResolveKeyByID(kid, nil, resolver.NutsSigningKeyType) }, jwt.WithAcceptableSkew(s.clockSkew)) if err != nil { return err @@ -508,7 +507,7 @@ func (s *authzServer) IntrospectAccessToken(ctx context.Context, accessToken str if !s.privateKeyStore.Exists(ctx, kid) { return nil, fmt.Errorf("JWT signing key not present on this node (kid=%s)", kid) } - return s.keyResolver.ResolveKeyByID(kid, nil, types.NutsSigningKeyType) + return s.keyResolver.ResolveKeyByID(kid, nil, resolver.NutsSigningKeyType) }, jwt.WithAcceptableSkew(s.clockSkew)) if err != nil { return nil, err @@ -569,7 +568,7 @@ func (s *authzServer) buildAccessToken(ctx context.Context, requester did.DID, a } // Sign with the private key of the issuer - signingKeyID, _, err := s.keyResolver.ResolveKey(authorizer, &issueTime, types.NutsSigningKeyType) + signingKeyID, _, err := s.keyResolver.ResolveKey(authorizer, &issueTime, resolver.NutsSigningKeyType) if err != nil { return "", accessToken, err } diff --git a/auth/services/oauth/authz_server_test.go b/auth/services/oauth/authz_server_test.go index ce071ec411..33c06ce382 100644 --- a/auth/services/oauth/authz_server_test.go +++ b/auth/services/oauth/authz_server_test.go @@ -28,6 +28,7 @@ import ( "errors" "fmt" "github.com/nuts-foundation/nuts-node/audit" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "testing" "time" @@ -47,7 +48,6 @@ import ( vcrTypes "github.com/nuts-foundation/nuts-node/vcr/types" verifier2 "github.com/nuts-foundation/nuts-node/vcr/verifier" "github.com/nuts-foundation/nuts-node/vdr" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -124,8 +124,8 @@ func TestAuth_CreateAccessToken(t *testing.T) { ctx.nameResolver.EXPECT().Search(context.Background(), searchTerms, false, gomock.Any()).Return([]vc.VerifiableCredential{testCredential}, nil) ctx.contractNotary.EXPECT().VerifyVP(gomock.Any(), nil).Return(nil, errors.New("identity validation failed")) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) - ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) ctx.keyStore.EXPECT().Exists(ctx.audit, authorizerSigningKeyID.String()).Return(true) tokenCtx := validContext() signToken(tokenCtx) @@ -139,7 +139,7 @@ func TestAuth_CreateAccessToken(t *testing.T) { t.Run("JWT validity too long", func(t *testing.T) { ctx := createContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) tokenCtx := validContext() tokenCtx.jwtBearerToken.Set(jwt.ExpirationKey, time.Now().Add(10*time.Second)) @@ -156,8 +156,8 @@ func TestAuth_CreateAccessToken(t *testing.T) { ctx.nameResolver.EXPECT().Search(context.Background(), searchTerms, false, gomock.Any()).Return([]vc.VerifiableCredential{testCredential}, nil) ctx.keyStore.EXPECT().Exists(ctx.audit, authorizerSigningKeyID.String()).Return(true) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) - ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) ctx.contractNotary.EXPECT().VerifyVP(gomock.Any(), nil).Return(services.TestVPVerificationResult{Val: contract.Invalid, FailureReason: "because of reasons"}, nil) tokenCtx := validContext() @@ -171,7 +171,7 @@ func TestAuth_CreateAccessToken(t *testing.T) { t.Run("error detail masking", func(t *testing.T) { setup := func(ctx *testContext) *testContext { - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil).AnyTimes() + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil).AnyTimes() ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), gomock.Any()).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil).AnyTimes() ctx.nameResolver.EXPECT().Search(context.Background(), searchTerms, false, gomock.Any()).Return([]vc.VerifiableCredential{testCredential}, nil).AnyTimes() ctx.didResolver.EXPECT().Resolve(authorizerDID, gomock.Any()).Return(getAuthorizerDIDDocument(), nil, nil).AnyTimes() @@ -217,8 +217,8 @@ func TestAuth_CreateAccessToken(t *testing.T) { t.Run("valid - without user identity", func(t *testing.T) { testCtx := createContext(t) - testCtx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) - testCtx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) + testCtx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) + testCtx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) testCtx.nameResolver.EXPECT().Search(context.Background(), searchTerms, false, gomock.Any()).Return([]vc.VerifiableCredential{testCredential}, nil) testCtx.didResolver.EXPECT().Resolve(authorizerDID, gomock.Any()).Return(getAuthorizerDIDDocument(), nil, nil).AnyTimes() testCtx.serviceResolver.EXPECT().GetCompoundServiceEndpoint(authorizerDID, expectedService, services.OAuthEndpointType, true).Return(expectedAudience, nil) @@ -239,8 +239,8 @@ func TestAuth_CreateAccessToken(t *testing.T) { t.Run("valid - all fields", func(t *testing.T) { ctx := createContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) - ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) ctx.nameResolver.EXPECT().Search(context.Background(), searchTerms, false, gomock.Any()).Return([]vc.VerifiableCredential{testCredential}, nil) ctx.didResolver.EXPECT().Resolve(authorizerDID, gomock.Any()).Return(getAuthorizerDIDDocument(), nil, nil).AnyTimes() ctx.serviceResolver.EXPECT().GetCompoundServiceEndpoint(authorizerDID, expectedService, services.OAuthEndpointType, true).Return(expectedAudience, nil) @@ -264,7 +264,7 @@ func TestAuth_CreateAccessToken(t *testing.T) { t.Run("missing organization credential", func(t *testing.T) { ctx := createContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) ctx.nameResolver.EXPECT().Search(context.Background(), searchTerms, false, gomock.Any()).Return([]vc.VerifiableCredential{}, nil) ctx.didResolver.EXPECT().Resolve(authorizerDID, gomock.Any()).Return(getAuthorizerDIDDocument(), nil, nil).AnyTimes() @@ -289,7 +289,7 @@ func TestService_validateIssuer(t *testing.T) { ctx := createContext(t) tokenCtx := validContext() - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) ctx.nameResolver.EXPECT().Search(context.Background(), searchTerms, false, gomock.Any()).Return([]vc.VerifiableCredential{testCredential}, nil) err := ctx.oauthService.validateIssuer(tokenCtx) @@ -301,7 +301,7 @@ func TestService_validateIssuer(t *testing.T) { ctx := createContext(t) tokenCtx := validContext() - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) ctx.nameResolver.EXPECT().Search(context.Background(), searchTerms, false, gomock.Any()).Return([]vc.VerifiableCredential{testCredential, testCredential}, nil) err := ctx.oauthService.validateIssuer(tokenCtx) @@ -321,7 +321,7 @@ func TestService_validateIssuer(t *testing.T) { ctx := createContext(t) tokenCtx := validContext() - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).Return(requesterSigningKey.Public(), nil) ctx.nameResolver.EXPECT().Search(context.Background(), searchTerms, false, gomock.Any()).Return(nil, errors.New("error occurred")) err := ctx.oauthService.validateIssuer(tokenCtx) @@ -331,7 +331,7 @@ func TestService_validateIssuer(t *testing.T) { ctx := createContext(t) tokenCtx := validContext() - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).Return(requesterSigningKey.Public(), nil) ctx.nameResolver.EXPECT().Search(context.Background(), searchTerms, false, gomock.Any()).Return([]vc.VerifiableCredential{}, nil) err := ctx.oauthService.validateIssuer(tokenCtx) @@ -342,7 +342,7 @@ func TestService_validateIssuer(t *testing.T) { tokenCtx := validContext() - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(nil, fmt.Errorf("not found")) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(nil, fmt.Errorf("not found")) err := ctx.oauthService.validateIssuer(tokenCtx) assert.EqualError(t, err, "invalid jwt.issuer key ID: not found") @@ -356,7 +356,7 @@ func TestService_validateSubject(t *testing.T) { tokenCtx := validContext() tokenCtx.jwtBearerToken.Set(jwt.SubjectKey, authorizerDID.String()) - ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) + ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) ctx.keyStore.EXPECT().Exists(ctx.audit, authorizerSigningKeyID.String()).Return(true) err := ctx.oauthService.validateSubject(ctx.audit, tokenCtx) @@ -386,7 +386,7 @@ func TestService_validateSubject(t *testing.T) { tokenCtx := validContext() tokenCtx.jwtBearerToken.Set(jwt.SubjectKey, authorizerDID.String()) - ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) + ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) ctx.keyStore.EXPECT().Exists(ctx.audit, authorizerSigningKeyID.String()).Return(false) err := ctx.oauthService.validateSubject(ctx.audit, tokenCtx) @@ -453,11 +453,11 @@ func TestService_validateAud(t *testing.T) { t.Run("error - endpoint resolve returns error", func(t *testing.T) { ctx := createContext(t) tokenCtx := validContext() - ctx.serviceResolver.EXPECT().GetCompoundServiceEndpoint(authorizerDID, expectedService, services.OAuthEndpointType, true).Return("", types.ErrNotFound) + ctx.serviceResolver.EXPECT().GetCompoundServiceEndpoint(authorizerDID, expectedService, services.OAuthEndpointType, true).Return("", resolver.ErrNotFound) err := ctx.oauthService.validateAudience(tokenCtx) - assert.Equal(t, types.ErrNotFound, err) + assert.Equal(t, resolver.ErrNotFound, err) }) t.Run("error - wrong audience", func(t *testing.T) { @@ -586,7 +586,7 @@ func TestService_parseAndValidateJwtBearerToken(t *testing.T) { keyID := "did:nuts:somedid#key-id" - ctx.keyResolver.EXPECT().ResolveKeyByID(keyID, nil, types.NutsSigningKeyType).Return(privateKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(keyID, nil, resolver.NutsSigningKeyType).Return(privateKey.Public(), nil) // alg: RS256 token := jwt.New() @@ -607,7 +607,7 @@ func TestService_parseAndValidateJwtBearerToken(t *testing.T) { tokenCtx := validContext() signToken(tokenCtx) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, types.NutsSigningKeyType).Return(requesterSigningKey.PublicKey, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, resolver.NutsSigningKeyType).Return(requesterSigningKey.PublicKey, nil) err := ctx.oauthService.parseAndValidateJwtBearerToken(tokenCtx) assert.NoError(t, err) @@ -624,7 +624,7 @@ func TestService_parseAndValidateJwtBearerToken(t *testing.T) { tokenCtx.jwtBearerToken.Set(jwt.ExpirationKey, time.Now().Add(-4*time.Minute)) signToken(tokenCtx) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, types.NutsSigningKeyType).Return(requesterSigningKey.PublicKey, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, resolver.NutsSigningKeyType).Return(requesterSigningKey.PublicKey, nil) err := ctx.oauthService.parseAndValidateJwtBearerToken(tokenCtx) assert.NoError(t, err) @@ -639,7 +639,7 @@ func TestService_parseAndValidateJwtBearerToken(t *testing.T) { tokenCtx.jwtBearerToken.Set(jwt.ExpirationKey, time.Now().Add(-4*time.Minute)) signToken(tokenCtx) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, types.NutsSigningKeyType).Return(requesterSigningKey.PublicKey, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, resolver.NutsSigningKeyType).Return(requesterSigningKey.PublicKey, nil) err := ctx.oauthService.parseAndValidateJwtBearerToken(tokenCtx) assert.EqualError(t, err, "exp not satisfied") @@ -652,7 +652,7 @@ func TestService_buildAccessToken(t *testing.T) { ctx.oauthService.accessTokenLifeSpan = secureAccessTokenLifeSpan * 10 // ignored by secureMode == true ctx.oauthService.Configure(5000, true) - ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), types.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) + ctx.keyResolver.EXPECT().ResolveKey(authorizerDID, gomock.Any(), resolver.NutsSigningKeyType).MinTimes(1).Return(authorizerSigningKeyID, authorizerSigningKey, nil) var actualClaims map[string]interface{} ctx.keyStore.EXPECT().SignJWT(gomock.Any(), gomock.Any(), nil, gomock.Any()). @@ -692,7 +692,7 @@ func TestService_IntrospectAccessToken(t *testing.T) { t.Run("validate access token", func(t *testing.T) { ctx := createContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) ctx.keyStore.EXPECT().Exists(ctx.audit, requesterSigningKeyID.String()).Return(true) // First build an access token @@ -713,7 +713,7 @@ func TestService_IntrospectAccessToken(t *testing.T) { t.Run("invalid signature", func(t *testing.T) { ctx := createContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKey.Public(), nil) ctx.keyStore.EXPECT().Exists(ctx.audit, requesterSigningKeyID.String()).Return(true) // First build an access token @@ -746,7 +746,7 @@ func TestService_IntrospectAccessToken(t *testing.T) { ctx := createContext(t) ctx.keyStore.EXPECT().Exists(ctx.audit, requesterSigningKeyID.String()).Return(true) - ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, types.NutsSigningKeyType).MinTimes(1).Return(nil, types.ErrNotFound) + ctx.keyResolver.EXPECT().ResolveKeyByID(requesterSigningKeyID.String(), nil, resolver.NutsSigningKeyType).MinTimes(1).Return(nil, resolver.ErrNotFound) // First build an access token tokenCtx := validContext() @@ -854,8 +854,8 @@ type testContext struct { contractNotary *services.MockContractNotary keyStore *crypto.MockKeyStore nameResolver *vcr.MockFinder - didResolver *types.MockDIDResolver - keyResolver *types.MockKeyResolver + didResolver *resolver.MockDIDResolver + keyResolver *resolver.MockKeyResolver serviceResolver *didman.MockCompoundServiceResolver oauthService *authzServer verifier *verifier2.MockVerifier @@ -868,9 +868,9 @@ var createContext = func(t *testing.T) *testContext { contractNotaryMock := services.NewMockContractNotary(ctrl) privateKeyStore := crypto.NewMockKeyStore(ctrl) nameResolver := vcr.NewMockFinder(ctrl) - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) serviceResolver := didman.NewMockCompoundServiceResolver(ctrl) - didResolver := types.NewMockDIDResolver(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) verifier := verifier2.NewMockVerifier(ctrl) return &testContext{ diff --git a/auth/services/oauth/relying_party.go b/auth/services/oauth/relying_party.go index 992f07ef9f..94c5849aec 100644 --- a/auth/services/oauth/relying_party.go +++ b/auth/services/oauth/relying_party.go @@ -22,7 +22,7 @@ import ( "context" "crypto/tls" "fmt" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "net/url" "strings" @@ -36,13 +36,12 @@ import ( nutsCrypto "github.com/nuts-foundation/nuts-node/crypto" "github.com/nuts-foundation/nuts-node/didman" "github.com/nuts-foundation/nuts-node/vcr/credential" - "github.com/nuts-foundation/nuts-node/vdr/types" ) var _ RelyingParty = (*relyingParty)(nil) type relyingParty struct { - keyResolver types.KeyResolver + keyResolver resolver.KeyResolver privateKeyStore nutsCrypto.KeyStore serviceResolver didman.CompoundServiceResolver secureMode bool @@ -52,10 +51,10 @@ type relyingParty struct { // NewRelyingParty returns an implementation of RelyingParty func NewRelyingParty( - didResolver types.DIDResolver, serviceResolver didman.CompoundServiceResolver, privateKeyStore nutsCrypto.KeyStore, + didResolver resolver.DIDResolver, serviceResolver didman.CompoundServiceResolver, privateKeyStore nutsCrypto.KeyStore, httpClientTimeout time.Duration, httpClientTLS *tls.Config) RelyingParty { return &relyingParty{ - keyResolver: didservice.KeyResolver{Resolver: didResolver}, + keyResolver: resolver.DIDKeyResolver{Resolver: didResolver}, serviceResolver: serviceResolver, privateKeyStore: privateKeyStore, httpClientTimeout: httpClientTimeout, @@ -117,7 +116,7 @@ func (s *relyingParty) CreateJwtGrant(ctx context.Context, request services.Crea keyVals := claimsFromRequest(request, endpointURL) - signingKeyID, _, err := s.keyResolver.ResolveKey(*requester, nil, types.NutsSigningKeyType) + signingKeyID, _, err := s.keyResolver.ResolveKey(*requester, nil, resolver.NutsSigningKeyType) if err != nil { return nil, err } diff --git a/auth/services/oauth/relying_party_test.go b/auth/services/oauth/relying_party_test.go index ec63dbc85e..7cd31b6837 100644 --- a/auth/services/oauth/relying_party_test.go +++ b/auth/services/oauth/relying_party_test.go @@ -25,6 +25,7 @@ import ( "fmt" "github.com/nuts-foundation/nuts-node/audit" http2 "github.com/nuts-foundation/nuts-node/test/http" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "net/http/httptest" "net/url" @@ -39,7 +40,6 @@ import ( "github.com/nuts-foundation/nuts-node/didman" "github.com/nuts-foundation/nuts-node/vcr/credential" "github.com/nuts-foundation/nuts-node/vdr" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -150,7 +150,7 @@ func TestService_CreateJwtBearerToken(t *testing.T) { ctx.didResolver.EXPECT().Resolve(authorizerDID, gomock.Any()).Return(authorizerDIDDocument, nil, nil).AnyTimes() ctx.serviceResolver.EXPECT().GetCompoundServiceEndpoint(authorizerDID, expectedService, services.OAuthEndpointType, true).Return(expectedAudience, nil) - ctx.keyResolver.EXPECT().ResolveKey(requesterDID, nil, types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKeyID, requesterSigningKey, nil) + ctx.keyResolver.EXPECT().ResolveKey(requesterDID, nil, resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKeyID, requesterSigningKey, nil) ctx.keyStore.EXPECT().SignJWT(gomock.Any(), gomock.Any(), nil, requesterSigningKeyID.String()).Return("token", nil) token, err := ctx.relyingParty.CreateJwtGrant(ctx.audit, request) @@ -167,7 +167,7 @@ func TestService_CreateJwtBearerToken(t *testing.T) { ctx.didResolver.EXPECT().Resolve(authorizerDID, gomock.Any()).Return(authorizerDIDDocument, nil, nil).AnyTimes() ctx.serviceResolver.EXPECT().GetCompoundServiceEndpoint(authorizerDID, expectedService, services.OAuthEndpointType, true).Return(expectedAudience, nil) - ctx.keyResolver.EXPECT().ResolveKey(requesterDID, nil, types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKeyID, requesterSigningKey, nil) + ctx.keyResolver.EXPECT().ResolveKey(requesterDID, nil, resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKeyID, requesterSigningKey, nil) ctx.keyStore.EXPECT().SignJWT(gomock.Any(), gomock.Any(), nil, requesterSigningKeyID.String()).Return("token", nil) validRequest := request @@ -201,12 +201,12 @@ func TestService_CreateJwtBearerToken(t *testing.T) { document := getAuthorizerDIDDocument() document.Service = []did.Service{} - ctx.serviceResolver.EXPECT().GetCompoundServiceEndpoint(authorizerDID, expectedService, services.OAuthEndpointType, true).Return("", types.ErrServiceNotFound) + ctx.serviceResolver.EXPECT().GetCompoundServiceEndpoint(authorizerDID, expectedService, services.OAuthEndpointType, true).Return("", resolver.ErrServiceNotFound) token, err := ctx.relyingParty.CreateJwtGrant(ctx.audit, request) assert.Empty(t, token) - assert.ErrorIs(t, err, types.ErrServiceNotFound) + assert.ErrorIs(t, err, resolver.ErrServiceNotFound) }) t.Run("request without authorizer", func(t *testing.T) { @@ -228,7 +228,7 @@ func TestService_CreateJwtBearerToken(t *testing.T) { ctx.didResolver.EXPECT().Resolve(authorizerDID, gomock.Any()).Return(authorizerDIDDocument, nil, nil).AnyTimes() ctx.serviceResolver.EXPECT().GetCompoundServiceEndpoint(authorizerDID, expectedService, services.OAuthEndpointType, true).Return(expectedAudience, nil) - ctx.keyResolver.EXPECT().ResolveKey(requesterDID, nil, types.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKeyID, requesterSigningKey, nil) + ctx.keyResolver.EXPECT().ResolveKey(requesterDID, nil, resolver.NutsSigningKeyType).MinTimes(1).Return(requesterSigningKeyID, requesterSigningKey, nil) ctx.keyStore.EXPECT().SignJWT(gomock.Any(), gomock.Any(), nil, requesterSigningKeyID.String()).Return("", errors.New("boom!")) token, err := ctx.relyingParty.CreateJwtGrant(ctx.audit, request) @@ -251,8 +251,8 @@ func TestRelyingParty_Configure(t *testing.T) { type rpTestContext struct { ctrl *gomock.Controller keyStore *crypto.MockKeyStore - didResolver *types.MockDIDResolver - keyResolver *types.MockKeyResolver + didResolver *resolver.MockDIDResolver + keyResolver *resolver.MockKeyResolver serviceResolver *didman.MockCompoundServiceResolver relyingParty *relyingParty audit context.Context @@ -262,9 +262,9 @@ var createRPContext = func(t *testing.T) *rpTestContext { ctrl := gomock.NewController(t) privateKeyStore := crypto.NewMockKeyStore(ctrl) - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) serviceResolver := didman.NewMockCompoundServiceResolver(ctrl) - didResolver := types.NewMockDIDResolver(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) return &rpTestContext{ ctrl: ctrl, diff --git a/auth/test.go b/auth/test.go index 664a662b89..bfe96fb526 100644 --- a/auth/test.go +++ b/auth/test.go @@ -19,7 +19,7 @@ package auth import ( - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr" "testing" "github.com/nuts-foundation/nuts-node/crypto" @@ -42,7 +42,7 @@ func testInstance(t *testing.T, cfg Config) *Auth { pkiMock := pki.NewMockProvider(ctrl) pkiMock.EXPECT().AddTruststore(gomock.Any()).AnyTimes() pkiMock.EXPECT().CreateTLSConfig(gomock.Any()).AnyTimes() - vdrInstance := types.NewMockVDR(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().Resolver().AnyTimes() return NewAuthInstance(cfg, vdrInstance, vcrInstance, cryptoInstance, nil, nil, pkiMock) } diff --git a/cmd/root.go b/cmd/root.go index ec8869fd16..a01eaaa9f6 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -23,12 +23,12 @@ import ( "context" "errors" "fmt" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/nuts-foundation/nuts-node/golden_hammer" goldenHammerCmd "github.com/nuts-foundation/nuts-node/golden_hammer/cmd" "github.com/nuts-foundation/nuts-node/vdr/didnuts" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/didservice" "io" "os" "runtime/pprof" @@ -199,7 +199,7 @@ func CreateSystem(shutdownCallback context.CancelFunc) *core.System { // Register HTTP routes system.RegisterRoutes(&core.LandingPage{}) - system.RegisterRoutes(&cryptoAPI.Wrapper{C: cryptoInstance, K: didservice.KeyResolver{Resolver: vdrInstance.Resolver()}}) + system.RegisterRoutes(&cryptoAPI.Wrapper{C: cryptoInstance, K: resolver.DIDKeyResolver{Resolver: vdrInstance.Resolver()}}) system.RegisterRoutes(&networkAPI.Wrapper{Service: networkInstance}) system.RegisterRoutes(&vdrAPI.Wrapper{VDR: vdrInstance, DocManipulator: &didnuts.Manipulator{ KeyCreator: cryptoInstance, diff --git a/crypto/api/v1/api.go b/crypto/api/v1/api.go index e01a5ab97a..b81442ab28 100644 --- a/crypto/api/v1/api.go +++ b/crypto/api/v1/api.go @@ -23,6 +23,7 @@ import ( crypt "crypto" "errors" "fmt" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "time" @@ -34,7 +35,6 @@ import ( "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/crypto" - "github.com/nuts-foundation/nuts-node/vdr/types" ) var _ StrictServerInterface = (*Wrapper)(nil) @@ -43,15 +43,15 @@ var _ core.ErrorStatusCodeResolver = (*Wrapper)(nil) // Wrapper implements the generated interface from oapi-codegen type Wrapper struct { C crypto.KeyStore - K types.KeyResolver + K resolver.KeyResolver } // ResolveStatusCode maps errors returned by this API to specific HTTP status codes. func (w *Wrapper) ResolveStatusCode(err error) int { return core.ResolveStatusCode(err, map[error]int{ crypto.ErrPrivateKeyNotFound: http.StatusBadRequest, - types.ErrNotFound: http.StatusNotFound, - types.ErrKeyNotFound: http.StatusNotFound, + resolver.ErrNotFound: http.StatusNotFound, + resolver.ErrKeyNotFound: http.StatusNotFound, }) } @@ -173,7 +173,7 @@ func (w *Wrapper) EncryptJwe(ctx context.Context, request EncryptJweRequestObjec } key, keyID, err := w.resolvePublicKey(id) if err != nil { - if errors.Is(err, types.ErrNotFound) || errors.Is(err, types.ErrKeyNotFound) { + if errors.Is(err, resolver.ErrNotFound) || errors.Is(err, resolver.ErrKeyNotFound) { return nil, core.InvalidInputError("unable to locate receiver %s: %w", receiver, err) } return nil, core.InvalidInputError("invalid receiver: %w", err) @@ -195,14 +195,14 @@ func (w *Wrapper) resolvePublicKey(id *did.DID) (key crypt.PublicKey, keyID ssi. if id.IsURL() { // Assume it is a keyId now := time.Now() - key, err = w.K.ResolveKeyByID(id.String(), &now, types.KeyAgreement) + key, err = w.K.ResolveKeyByID(id.String(), &now, resolver.KeyAgreement) if err != nil { return nil, ssi.URI{}, err } keyID = id.URI() } else { // Assume it is a DID - keyID, key, err = w.K.ResolveKey(*id, nil, types.KeyAgreement) + keyID, key, err = w.K.ResolveKey(*id, nil, resolver.KeyAgreement) if err != nil { return nil, ssi.URI{}, err } diff --git a/crypto/api/v1/api_test.go b/crypto/api/v1/api_test.go index 82ddd24462..969fd80bb1 100644 --- a/crypto/api/v1/api_test.go +++ b/crypto/api/v1/api_test.go @@ -24,7 +24,7 @@ import ( ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/core" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "testing" @@ -252,7 +252,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { Payload: payload, Headers: headers, } - ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, types.KeyAgreement).Return(ssi.URI{}, nil, errors.New("FAIL")) + ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, resolver.KeyAgreement).Return(ssi.URI{}, nil, errors.New("FAIL")) jwe, err := ctx.client.EncryptJwe(nil, EncryptJweRequestObject{Body: &request}) assert.EqualError(t, err, "invalid receiver: FAIL") @@ -267,7 +267,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { Payload: payload, Headers: headers, } - ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, types.KeyAgreement).Return(ssi.URI{}, nil, errors.New("FAIL")) + ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, resolver.KeyAgreement).Return(ssi.URI{}, nil, errors.New("FAIL")) jwe, err := ctx.client.EncryptJwe(nil, EncryptJweRequestObject{Body: &request}) assert.EqualError(t, err, "invalid receiver: FAIL") @@ -282,7 +282,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { Payload: payload, Headers: headers, } - ctx.keyResolver.EXPECT().ResolveKeyByID(gomock.Any(), gomock.Any(), types.KeyAgreement).Return(ssi.URI{}, errors.New("FAIL")) + ctx.keyResolver.EXPECT().ResolveKeyByID(gomock.Any(), gomock.Any(), resolver.KeyAgreement).Return(ssi.URI{}, errors.New("FAIL")) jwe, err := ctx.client.EncryptJwe(nil, EncryptJweRequestObject{Body: &request}) assert.EqualError(t, err, "invalid receiver: FAIL") @@ -297,7 +297,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { Payload: payload, Headers: headers, } - ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, types.KeyAgreement).Return(ssi.URI{}, nil, types.ErrNotFound) + ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, resolver.KeyAgreement).Return(ssi.URI{}, nil, resolver.ErrNotFound) jwe, err := ctx.client.EncryptJwe(nil, EncryptJweRequestObject{Body: &request}) assert.EqualError(t, err, "unable to locate receiver did:nuts:12345: unable to find the DID document") @@ -312,7 +312,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { Payload: payload, Headers: headers, } - ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, types.KeyAgreement).Return(ssi.URI{}, nil, types.ErrNotFound) + ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, resolver.KeyAgreement).Return(ssi.URI{}, nil, resolver.ErrNotFound) jwe, err := ctx.client.EncryptJwe(nil, EncryptJweRequestObject{Body: &request}) assert.EqualError(t, err, "unable to locate receiver did:nuts:12345: unable to find the DID document") @@ -327,7 +327,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { Payload: payload, Headers: headers, } - ctx.keyResolver.EXPECT().ResolveKeyByID(gomock.Any(), gomock.Any(), types.KeyAgreement).Return(ssi.URI{}, types.ErrNotFound) + ctx.keyResolver.EXPECT().ResolveKeyByID(gomock.Any(), gomock.Any(), resolver.KeyAgreement).Return(ssi.URI{}, resolver.ErrNotFound) jwe, err := ctx.client.EncryptJwe(nil, EncryptJweRequestObject{Body: &request}) assert.EqualError(t, err, "unable to locate receiver did:nuts:12345#key-1: unable to find the DID document") @@ -359,7 +359,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { did, _ := ssi.ParseURI("did:nuts:12345") ctx.keyStore.EXPECT().EncryptJWE(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("", errors.New("b00m!")) - ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, types.KeyAgreement).Return(*did, nil, nil) + ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, resolver.KeyAgreement).Return(*did, nil, nil) jwe, err := ctx.client.EncryptJwe(audit.TestContext(), EncryptJweRequestObject{Body: &request}) @@ -377,7 +377,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { Payload: payload, } ctx.keyStore.EXPECT().EncryptJWE(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("jwe", nil) - ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, types.KeyAgreement).Return(*did, nil, nil) + ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, resolver.KeyAgreement).Return(*did, nil, nil) resp, err := ctx.client.EncryptJwe(nil, EncryptJweRequestObject{Body: &request}) @@ -395,7 +395,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { Payload: payload, } ctx.keyStore.EXPECT().EncryptJWE(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("jwe", nil) - ctx.keyResolver.EXPECT().ResolveKeyByID(gomock.Any(), gomock.Any(), types.KeyAgreement).Return(*did, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(gomock.Any(), gomock.Any(), resolver.KeyAgreement).Return(*did, nil) resp, err := ctx.client.EncryptJwe(nil, EncryptJweRequestObject{Body: &request}) @@ -413,7 +413,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { } did, _ := ssi.ParseURI(kid) ctx.keyStore.EXPECT().EncryptJWE(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("jwe", nil) - ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, types.KeyAgreement).Return(*did, nil, nil) + ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, resolver.KeyAgreement).Return(*did, nil, nil) resp, err := ctx.client.EncryptJwe(nil, EncryptJweRequestObject{Body: &request}) @@ -429,7 +429,7 @@ func TestWrapper_EncryptJwe(t *testing.T) { Receiver: did.String(), } ctx.keyStore.EXPECT().EncryptJWE(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("jwe", nil) - ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, types.KeyAgreement).Return(*did, nil, nil) + ctx.keyResolver.EXPECT().ResolveKey(gomock.Any(), nil, resolver.KeyAgreement).Return(*did, nil, nil) resp, err := ctx.client.EncryptJwe(nil, EncryptJweRequestObject{Body: &request}) @@ -474,14 +474,14 @@ func TestWrapper_DecryptJwe(t *testing.T) { type mockContext struct { ctrl *gomock.Controller keyStore *crypto.MockKeyStore - keyResolver *types.MockKeyResolver + keyResolver *resolver.MockKeyResolver client *Wrapper } func newMockContext(t *testing.T) mockContext { ctrl := gomock.NewController(t) keyStore := crypto.NewMockKeyStore(ctrl) - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) client := &Wrapper{C: keyStore, K: keyResolver} return mockContext{ diff --git a/didman/api/v1/api.go b/didman/api/v1/api.go index 41c465e36a..c1360ab2c7 100644 --- a/didman/api/v1/api.go +++ b/didman/api/v1/api.go @@ -25,7 +25,7 @@ import ( "github.com/labstack/echo/v4" "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/vdr/didnuts" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "net/url" "strings" @@ -34,7 +34,6 @@ import ( "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/didman" - "github.com/nuts-foundation/nuts-node/vdr/types" ) var _ StrictServerInterface = (*Wrapper)(nil) @@ -50,25 +49,25 @@ func (w *Wrapper) ResolveStatusCode(err error) int { switch { case errors.Is(err, did.ErrInvalidDID): return http.StatusBadRequest - case errors.Is(err, types.ErrNotFound): + case errors.Is(err, resolver.ErrNotFound): return http.StatusNotFound - case errors.Is(err, types.ErrDIDNotManagedByThisNode): + case errors.Is(err, resolver.ErrDIDNotManagedByThisNode): return http.StatusBadRequest - case errors.Is(err, types.ErrDeactivated): + case errors.Is(err, resolver.ErrDeactivated): return http.StatusConflict - case errors.Is(err, types.ErrDuplicateService): + case errors.Is(err, resolver.ErrDuplicateService): return http.StatusConflict case errors.Is(err, didman.ErrServiceInUse): return http.StatusConflict case errors.Is(err, didnuts.ErrInvalidOptions): return http.StatusBadRequest - case errors.Is(err, types.ErrServiceNotFound): + case errors.Is(err, resolver.ErrServiceNotFound): return http.StatusNotFound case errors.As(err, new(didnuts.InvalidServiceError)): return http.StatusBadRequest - case errors.As(err, new(didservice.ServiceQueryError)): + case errors.As(err, new(resolver.ServiceQueryError)): return http.StatusBadRequest - case errors.Is(err, types.ErrServiceReferenceToDeep): + case errors.Is(err, resolver.ErrServiceReferenceToDeep): return http.StatusNotAcceptable case errors.As(err, new(didman.ErrReferencedServiceNotAnEndpoint)): return http.StatusNotAcceptable diff --git a/didman/api/v1/api_test.go b/didman/api/v1/api_test.go index 86be9d35df..2878660c8d 100644 --- a/didman/api/v1/api_test.go +++ b/didman/api/v1/api_test.go @@ -23,7 +23,7 @@ import ( "errors" "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/vdr/didnuts" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "net/url" "testing" @@ -32,7 +32,6 @@ import ( "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/didman" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -109,44 +108,44 @@ func TestWrapper_AddEndpoint(t *testing.T) { t.Run("error - AddEndpoint fails", func(t *testing.T) { test := newMockContext(t) - test.didman.EXPECT().AddEndpoint(audit.ContextWithAuditInfo(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, types.ErrNotFound) + test.didman.EXPECT().AddEndpoint(audit.ContextWithAuditInfo(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, resolver.ErrNotFound) response, err := test.wrapper.AddEndpoint(ctx, request) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Equal(t, http.StatusNotFound, test.wrapper.ResolveStatusCode(err)) assert.Nil(t, response) }) t.Run("error - deactivated", func(t *testing.T) { test := newMockContext(t) - test.didman.EXPECT().AddEndpoint(audit.ContextWithAuditInfo(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, types.ErrDeactivated) + test.didman.EXPECT().AddEndpoint(audit.ContextWithAuditInfo(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, resolver.ErrDeactivated) response, err := test.wrapper.AddEndpoint(ctx, request) - assert.ErrorIs(t, err, types.ErrDeactivated) + assert.ErrorIs(t, err, resolver.ErrDeactivated) assert.Equal(t, http.StatusConflict, test.wrapper.ResolveStatusCode(err)) assert.Nil(t, response) }) t.Run("error - not managed", func(t *testing.T) { test := newMockContext(t) - test.didman.EXPECT().AddEndpoint(audit.ContextWithAuditInfo(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, types.ErrDIDNotManagedByThisNode) + test.didman.EXPECT().AddEndpoint(audit.ContextWithAuditInfo(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, resolver.ErrDIDNotManagedByThisNode) response, err := test.wrapper.AddEndpoint(ctx, request) - assert.ErrorIs(t, err, types.ErrDIDNotManagedByThisNode) + assert.ErrorIs(t, err, resolver.ErrDIDNotManagedByThisNode) assert.Equal(t, http.StatusBadRequest, test.wrapper.ResolveStatusCode(err)) assert.Nil(t, response) }) t.Run("error - duplicate", func(t *testing.T) { test := newMockContext(t) - test.didman.EXPECT().AddEndpoint(audit.ContextWithAuditInfo(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, types.ErrDuplicateService) + test.didman.EXPECT().AddEndpoint(audit.ContextWithAuditInfo(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, resolver.ErrDuplicateService) response, err := test.wrapper.AddEndpoint(ctx, request) - assert.ErrorIs(t, err, types.ErrDuplicateService) + assert.ErrorIs(t, err, resolver.ErrDuplicateService) assert.Equal(t, http.StatusConflict, test.wrapper.ResolveStatusCode(err)) assert.Nil(t, response) }) @@ -281,9 +280,9 @@ func TestWrapper_DeleteEndpointsByType(t *testing.T) { t.Run("error - didman.DeleteEndpointsByType returns error", func(t *testing.T) { test := newMockContext(t) - test.didman.EXPECT().DeleteEndpointsByType(audit.ContextWithAuditInfo(), *parsedID, endpointType).Return(types.ErrNotFound) + test.didman.EXPECT().DeleteEndpointsByType(audit.ContextWithAuditInfo(), *parsedID, endpointType).Return(resolver.ErrNotFound) response, err := test.wrapper.DeleteEndpointsByType(ctx, request) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Nil(t, response) }) } @@ -431,10 +430,10 @@ func TestWrapper_GetCompoundServices(t *testing.T) { }) t.Run("error - DID not found", func(t *testing.T) { test := newMockContext(t) - test.didman.EXPECT().GetCompoundServices(targetDID).Return(nil, types.ErrNotFound) + test.didman.EXPECT().GetCompoundServices(targetDID).Return(nil, resolver.ErrNotFound) response, err := test.wrapper.GetCompoundServices(ctx, request) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Equal(t, http.StatusNotFound, test.wrapper.ResolveStatusCode(err)) assert.Nil(t, response) }) @@ -496,11 +495,11 @@ func TestWrapper_GetCompoundServiceEndpoint(t *testing.T) { }) t.Run("error mapping", func(t *testing.T) { ctx := newMockContext(t) - assert.Equal(t, http.StatusNotFound, ctx.wrapper.ResolveStatusCode(types.ErrServiceNotFound)) - assert.Equal(t, http.StatusBadRequest, ctx.wrapper.ResolveStatusCode(didservice.ServiceQueryError{errors.New("arbitrary")})) - assert.Equal(t, http.StatusNotAcceptable, ctx.wrapper.ResolveStatusCode(types.ErrServiceReferenceToDeep)) + assert.Equal(t, http.StatusNotFound, ctx.wrapper.ResolveStatusCode(resolver.ErrServiceNotFound)) + assert.Equal(t, http.StatusBadRequest, ctx.wrapper.ResolveStatusCode(resolver.ServiceQueryError{errors.New("arbitrary")})) + assert.Equal(t, http.StatusNotAcceptable, ctx.wrapper.ResolveStatusCode(resolver.ErrServiceReferenceToDeep)) assert.Equal(t, http.StatusNotAcceptable, ctx.wrapper.ResolveStatusCode(didman.ErrReferencedServiceNotAnEndpoint{})) - assert.Equal(t, http.StatusNotFound, ctx.wrapper.ResolveStatusCode(types.ErrNotFound)) + assert.Equal(t, http.StatusNotFound, ctx.wrapper.ResolveStatusCode(resolver.ErrNotFound)) }) } @@ -533,11 +532,11 @@ func TestWrapper_DeleteService(t *testing.T) { t.Run("error - service fails", func(t *testing.T) { test := newMockContext(t) - test.didman.EXPECT().DeleteService(audit.ContextWithAuditInfo(), gomock.Any()).Return(types.ErrNotFound) + test.didman.EXPECT().DeleteService(audit.ContextWithAuditInfo(), gomock.Any()).Return(resolver.ErrNotFound) response, err := test.wrapper.DeleteService(ctx, request) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Equal(t, http.StatusNotFound, test.wrapper.ResolveStatusCode(err)) assert.Nil(t, response) }) @@ -577,11 +576,11 @@ func TestWrapper_UpdateContactInformation(t *testing.T) { t.Run("error - service fails DID", func(t *testing.T) { test := newMockContext(t) - test.didman.EXPECT().UpdateContactInformation(audit.ContextWithAuditInfo(), targetDID, info).Return(nil, types.ErrNotFound) + test.didman.EXPECT().UpdateContactInformation(audit.ContextWithAuditInfo(), targetDID, info).Return(nil, resolver.ErrNotFound) response, err := test.wrapper.UpdateContactInformation(ctx, request) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Equal(t, http.StatusNotFound, test.wrapper.ResolveStatusCode(err)) assert.Nil(t, response) }) @@ -617,10 +616,10 @@ func TestWrapper_GetContactInformation(t *testing.T) { }) t.Run("error - service fails", func(t *testing.T) { test := newMockContext(t) - test.didman.EXPECT().GetContactInformation(targetDID).Return(nil, types.ErrNotFound) + test.didman.EXPECT().GetContactInformation(targetDID).Return(nil, resolver.ErrNotFound) response, err := test.wrapper.GetContactInformation(ctx, request) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Nil(t, response) }) t.Run("error - contact information not found", func(t *testing.T) { @@ -670,14 +669,14 @@ func TestWrapper_SearchOrganizations(t *testing.T) { }) t.Run("error - service fails", func(t *testing.T) { test := newMockContext(t) - test.didman.EXPECT().SearchOrganizations(gomock.Any(), "query", nil).Return(nil, types.ErrNotFound) + test.didman.EXPECT().SearchOrganizations(gomock.Any(), "query", nil).Return(nil, resolver.ErrNotFound) response, err := test.wrapper.SearchOrganizations(ctx, SearchOrganizationsRequestObject{ Params: SearchOrganizationsParams{ Query: "query", }, }) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Nil(t, response) }) } diff --git a/didman/didman.go b/didman/didman.go index 8b78f1f293..11822bd95b 100644 --- a/didman/didman.go +++ b/didman/didman.go @@ -25,7 +25,8 @@ import ( "encoding/json" "errors" "fmt" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/url" "sync" @@ -37,7 +38,6 @@ import ( "github.com/nuts-foundation/nuts-node/jsonld" "github.com/nuts-foundation/nuts-node/vcr" "github.com/nuts-foundation/nuts-node/vcr/credential" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/shengdoushi/base58" ) @@ -87,14 +87,14 @@ func (e ErrReferencedServiceNotAnEndpoint) Is(other error) bool { type didman struct { jsonldManager jsonld.JSONLD - vdr types.VDR + vdr vdr.VDR vcr vcr.Finder // callSerializer can be used to (un)lock a resource such as a DID to prevent parallel updates callSerializer keyedMutex } // NewDidmanInstance creates a new didman instance with services set -func NewDidmanInstance(vdr types.VDR, vcr vcr.Finder, jsonldManager jsonld.JSONLD) Didman { +func NewDidmanInstance(vdr vdr.VDR, vcr vcr.Finder, jsonldManager jsonld.JSONLD) Didman { return &didman{ vdr: vdr, vcr: vcr, @@ -166,7 +166,7 @@ func (d *didman) DeleteEndpointsByType(ctx context.Context, id did.DID, serviceT } } if !found { - return types.ErrServiceNotFound + return resolver.ErrServiceNotFound } return nil } @@ -246,8 +246,8 @@ func (d *didman) GetCompoundServiceEndpoint(id did.DID, compoundServiceType stri documentsCache := map[string]*did.Document{document.ID.String(): document} // First, resolve the compound endpoint - serviceResolver := didservice.ServiceResolver{Resolver: d.vdr.Resolver()} - compoundService, err := serviceResolver.ResolveEx(didservice.MakeServiceReference(id, compoundServiceType), referenceDepth, didservice.DefaultMaxServiceReferenceDepth, documentsCache) + serviceResolver := resolver.DIDServiceResolver{Resolver: d.vdr.Resolver()} + compoundService, err := serviceResolver.ResolveEx(resolver.MakeServiceReference(id, compoundServiceType), referenceDepth, resolver.DefaultMaxServiceReferenceDepth, documentsCache) if err != nil { return "", ErrReferencedServiceNotAnEndpoint{Cause: fmt.Errorf("unable to resolve compound service: %w", err)} } @@ -260,15 +260,15 @@ func (d *didman) GetCompoundServiceEndpoint(id did.DID, compoundServiceType stri } endpoint := endpoints[endpointType] if endpoint == "" { - return "", types.ErrServiceNotFound + return "", resolver.ErrServiceNotFound } - if resolveReferences && didservice.IsServiceReference(endpoint) { + if resolveReferences && resolver.IsServiceReference(endpoint) { endpointURI, err := ssi.ParseURI(endpoint) if err != nil { // Not sure when this could ever happen return "", err } - resolvedEndpoint, err := serviceResolver.ResolveEx(*endpointURI, referenceDepth, didservice.DefaultMaxServiceReferenceDepth, documentsCache) + resolvedEndpoint, err := serviceResolver.ResolveEx(*endpointURI, referenceDepth, resolver.DefaultMaxServiceReferenceDepth, documentsCache) if err != nil { return "", err } @@ -282,7 +282,7 @@ func (d *didman) GetCompoundServiceEndpoint(id did.DID, compoundServiceType stri } func (d *didman) DeleteService(ctx context.Context, serviceID ssi.URI) error { - id, err := didservice.GetDIDFromURL(serviceID.String()) + id, err := resolver.GetDIDFromURL(serviceID.String()) if err != nil { return err } @@ -298,7 +298,7 @@ func (d *didman) deleteService(ctx context.Context, serviceID ssi.URI) error { log.Logger(). WithField(core.LogFieldServiceID, serviceID.String()). Debug("Deleting service") - id, err := didservice.GetDIDFromURL(serviceID.String()) + id, err := resolver.GetDIDFromURL(serviceID.String()) if err != nil { return err } @@ -316,11 +316,11 @@ func (d *didman) deleteService(ctx context.Context, serviceID ssi.URI) error { } } if service == nil { - return types.ErrServiceNotFound + return resolver.ErrServiceNotFound } // check for existing use on this document - if referencedService(doc, didservice.MakeServiceReference(doc.ID, service.Type).String()) { + if referencedService(doc, resolver.MakeServiceReference(doc.ID, service.Type).String()) { return ErrServiceInUse } @@ -333,7 +333,7 @@ func (d *didman) deleteService(ctx context.Context, serviceID ssi.URI) error { } } if j == len(doc.Service) { - return types.ErrServiceNotFound + return resolver.ErrServiceNotFound } doc.Service = doc.Service[:j] @@ -468,7 +468,7 @@ func (d *didman) resolveOrganizationDIDDocuments(organizations []vc.VerifiableCr j := 0 for i, organization := range organizations { document, organizationDID, err := d.resolveOrganizationDIDDocument(organization) - if didservice.IsFunctionalResolveError(err) { + if resolver.IsFunctionalResolveError(err) { // Just ignore deactivated DID documents or VCs that don't refer to an existing DID document. // Log it on debug, because it might be useful for finding VCs that need to be revoked (since they're invalid). log.Logger(). @@ -551,7 +551,7 @@ func (d *didman) addService(ctx context.Context, id did.DID, serviceType string, // check for duplicate service type for _, s := range doc.Service { if s.Type == serviceType { - return nil, types.ErrDuplicateService + return nil, resolver.ErrDuplicateService } } @@ -589,7 +589,7 @@ func (d *didman) updateService(ctx context.Context, id did.DID, serviceType stri } } if !serviceToBeUpdatedFound { - return nil, types.ErrServiceNotFound + return nil, resolver.ErrServiceNotFound } if err = d.vdr.Update(ctx, id, *doc); err != nil { return nil, err @@ -611,7 +611,7 @@ func referencedService(doc *did.Document, serviceRef string) bool { for _, s := range doc.Service { switch s.ServiceEndpoint.(type) { case map[string]interface{}: // compound service - cs := types.CompoundService{} + cs := map[string]string{} _ = s.UnmarshalServiceEndpoint(&cs) for _, ref := range cs { if ref == serviceRef { diff --git a/didman/didman_test.go b/didman/didman_test.go index e7c49a11ad..ea587ac405 100644 --- a/didman/didman_test.go +++ b/didman/didman_test.go @@ -26,7 +26,7 @@ import ( "fmt" "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/vdr/didnuts" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "io" "net/url" "strings" @@ -41,7 +41,6 @@ import ( "github.com/nuts-foundation/nuts-node/jsonld" "github.com/nuts-foundation/nuts-node/vcr" "github.com/nuts-foundation/nuts-node/vdr" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -67,7 +66,7 @@ func TestNewDidmanInstance(t *testing.T) { func TestDidman_AddEndpoint(t *testing.T) { u, _ := url.Parse("https://api.example.com/v1") - meta := &types.DocumentMetadata{Hash: hash.EmptyHash()} + meta := &resolver.DocumentMetadata{Hash: hash.EmptyHash()} t.Run("ok", func(t *testing.T) { ctx := newMockContext(t) @@ -117,23 +116,23 @@ func TestDidman_AddEndpoint(t *testing.T) { _, _ = ctx.instance.AddEndpoint(ctx.audit, testDIDA, "type", *u) _, err := ctx.instance.AddEndpoint(ctx.audit, testDIDA, "type", *u) - assert.ErrorIs(t, err, types.ErrDuplicateService) + assert.ErrorIs(t, err, resolver.ErrDuplicateService) }) t.Run("error - DID not found", func(t *testing.T) { ctx := newMockContext(t) - ctx.didResolver.EXPECT().Resolve(testDIDA, nil).Return(nil, nil, types.ErrNotFound) + ctx.didResolver.EXPECT().Resolve(testDIDA, nil).Return(nil, nil, resolver.ErrNotFound) _, err := ctx.instance.AddEndpoint(ctx.audit, testDIDA, "type", *u) - assert.Equal(t, types.ErrNotFound, err) + assert.Equal(t, resolver.ErrNotFound, err) }) } func TestDidman_UpdateEndpoint(t *testing.T) { endpoint, _ := url.Parse("https://api.example.com/v1") newEndpoint, _ := url.Parse("https://api.example.com/v2") - meta := &types.DocumentMetadata{Hash: hash.EmptyHash()} + meta := &resolver.DocumentMetadata{Hash: hash.EmptyHash()} const serviceType = "type" service := did.Service{ Type: serviceType, @@ -184,26 +183,26 @@ func TestDidman_UpdateEndpoint(t *testing.T) { _, err := ctx.instance.UpdateEndpoint(ctx.audit, testDIDA, "some-other-type", *endpoint) - assert.Equal(t, types.ErrServiceNotFound, err) + assert.Equal(t, resolver.ErrServiceNotFound, err) }) t.Run("error - DID not found", func(t *testing.T) { ctx := newMockContext(t) - ctx.didResolver.EXPECT().Resolve(testDIDA, nil).Return(nil, nil, types.ErrNotFound) + ctx.didResolver.EXPECT().Resolve(testDIDA, nil).Return(nil, nil, resolver.ErrNotFound) _, err := ctx.instance.AddEndpoint(ctx.audit, testDIDA, serviceType, *endpoint) - assert.Equal(t, types.ErrNotFound, err) + assert.Equal(t, resolver.ErrNotFound, err) }) } func TestDidman_AddCompoundService(t *testing.T) { - meta := &types.DocumentMetadata{Hash: hash.EmptyHash()} + meta := &resolver.DocumentMetadata{Hash: hash.EmptyHash()} - helloServiceQuery := didservice.MakeServiceReference(testDIDA, "hello") - worldServiceQuery := didservice.MakeServiceReference(testDIDB, "world") - universeServiceQuery := didservice.MakeServiceReference(testDIDB, "universe") - universeNestedServiceQuery := didservice.MakeServiceReference(testDIDB, "universe-ref") + helloServiceQuery := resolver.MakeServiceReference(testDIDA, "hello") + worldServiceQuery := resolver.MakeServiceReference(testDIDB, "world") + universeServiceQuery := resolver.MakeServiceReference(testDIDB, "universe") + universeNestedServiceQuery := resolver.MakeServiceReference(testDIDB, "universe-ref") references := make(map[string]ssi.URI, 0) references["hello"] = helloServiceQuery references["world"] = worldServiceQuery @@ -255,7 +254,7 @@ func TestDidman_AddCompoundService(t *testing.T) { ctx.vdr.EXPECT().Update(ctx.audit, testDIDA, gomock.Any()).DoAndReturn( func(_ context.Context, _ interface{}, doc did.Document) error { newDoc = doc - return didnuts.ManagedDocumentValidator(didservice.ServiceResolver{Resolver: ctx.didResolver}).Validate(newDoc) + return didnuts.ManagedDocumentValidator(resolver.DIDServiceResolver{Resolver: ctx.didResolver}).Validate(newDoc) }) _, err := ctx.instance.AddCompoundService(ctx.audit, testDIDA, "helloworld", references) @@ -287,11 +286,11 @@ func TestDidman_AddCompoundService(t *testing.T) { } func TestDidman_UpdateCompoundService(t *testing.T) { - meta := &types.DocumentMetadata{Hash: hash.EmptyHash()} + meta := &resolver.DocumentMetadata{Hash: hash.EmptyHash()} - helloServiceQuery := didservice.MakeServiceReference(testDIDA, "hello") - worldServiceQuery := didservice.MakeServiceReference(testDIDB, "world") - universeServiceQuery := didservice.MakeServiceReference(testDIDB, "universe") + helloServiceQuery := resolver.MakeServiceReference(testDIDA, "hello") + worldServiceQuery := resolver.MakeServiceReference(testDIDB, "world") + universeServiceQuery := resolver.MakeServiceReference(testDIDB, "universe") references := make(map[string]ssi.URI, 0) references["hello"] = helloServiceQuery references["world"] = worldServiceQuery @@ -347,7 +346,7 @@ func TestDidman_DeleteService(t *testing.T) { } id, _ := did.ParseDID("did:nuts:123") uri := ssi.MustParseURI("did:nuts:123#1") - meta := &types.DocumentMetadata{Hash: hash.EmptyHash()} + meta := &resolver.DocumentMetadata{Hash: hash.EmptyHash()} t.Run("ok", func(t *testing.T) { ctx := newMockContext(t) @@ -373,7 +372,7 @@ func TestDidman_DeleteService(t *testing.T) { err := ctx.instance.DeleteService(ctx.audit, nonExistingID) - assert.Equal(t, types.ErrServiceNotFound, err) + assert.Equal(t, resolver.ErrServiceNotFound, err) }) t.Run("error - in use", func(t *testing.T) { @@ -401,11 +400,11 @@ func TestDidman_DeleteService(t *testing.T) { t.Run("error - DID not found", func(t *testing.T) { ctx := newMockContext(t) - ctx.didResolver.EXPECT().Resolve(*id, nil).Return(nil, nil, types.ErrNotFound) + ctx.didResolver.EXPECT().Resolve(*id, nil).Return(nil, nil, resolver.ErrNotFound) err := ctx.instance.DeleteService(ctx.audit, uri) - assert.Equal(t, types.ErrNotFound, err) + assert.Equal(t, resolver.ErrNotFound, err) }) } @@ -413,7 +412,7 @@ func TestDidman_UpdateContactInformation(t *testing.T) { didDoc := didnuts.CreateDocument() id, _ := did.ParseDID("did:nuts:123") didDoc.ID = *id - meta := &types.DocumentMetadata{Hash: hash.EmptyHash()} + meta := &resolver.DocumentMetadata{Hash: hash.EmptyHash()} expected := ContactInformation{ Email: "email", Name: "name", @@ -479,7 +478,7 @@ func TestDidman_UpdateContactInformation(t *testing.T) { func TestDidman_GetContactInformation(t *testing.T) { id, _ := did.ParseDID("did:nuts:123") - meta := &types.DocumentMetadata{Hash: hash.EmptyHash()} + meta := &resolver.DocumentMetadata{Hash: hash.EmptyHash()} expected := ContactInformation{ Email: "email", Name: "name", @@ -549,7 +548,7 @@ func TestDidman_DeleteEndpointsByType(t *testing.T) { ServiceEndpoint: map[string]interface{}{"foo": "http://example.org"}, }} - meta := &types.DocumentMetadata{Hash: hash.EmptyHash()} + meta := &resolver.DocumentMetadata{Hash: hash.EmptyHash()} didDoc := &did.Document{ID: *id, Service: endpoints} t.Run("ok - it deletes the service", func(t *testing.T) { @@ -591,15 +590,15 @@ func TestDidman_DeleteEndpointsByType(t *testing.T) { // not in use by any other document ctx.didResolver.EXPECT().Resolve(*id, gomock.Any()).Return(didDoc, meta, nil) err := ctx.instance.DeleteEndpointsByType(ctx.audit, *id, "unknown type") - assert.ErrorIs(t, err, types.ErrServiceNotFound) + assert.ErrorIs(t, err, resolver.ErrServiceNotFound) }) t.Run("error - DID document", func(t *testing.T) { ctx := newMockContext(t) // not in use by any other document - ctx.didResolver.EXPECT().Resolve(*id, gomock.Any()).Return(nil, nil, types.ErrNotFound) + ctx.didResolver.EXPECT().Resolve(*id, gomock.Any()).Return(nil, nil, resolver.ErrNotFound) err := ctx.instance.DeleteEndpointsByType(ctx.audit, *id, endpointType) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) }) t.Run("error - in use by other services", func(t *testing.T) { @@ -608,7 +607,7 @@ func TestDidman_DeleteEndpointsByType(t *testing.T) { { ID: ssi.MustParseURI(id.String() + "#123"), Type: "refType", - ServiceEndpoint: didservice.MakeServiceReference(*id, endpointType), + ServiceEndpoint: resolver.MakeServiceReference(*id, endpointType), }, } didDocErrServiceInUse := &did.Document{ID: *id, Service: endpointsWithSelfReference} @@ -651,7 +650,7 @@ func TestDidman_GetCompoundServices(t *testing.T) { }) t.Run("error - unknown DID", func(t *testing.T) { ctx := newMockContext(t) - ctx.didResolver.EXPECT().Resolve(*id, nil).Return(nil, nil, types.ErrNotFound) + ctx.didResolver.EXPECT().Resolve(*id, nil).Return(nil, nil, resolver.ErrNotFound) actual, err := ctx.instance.GetCompoundServices(*id) assert.EqualError(t, err, "unable to find the DID document") assert.Nil(t, actual) @@ -722,23 +721,23 @@ func TestDidman_GetCompoundServiceEndpoint(t *testing.T) { }) t.Run("error - unknown DID", func(t *testing.T) { ctx := newMockContext(t) - ctx.didResolver.EXPECT().Resolve(*id, nil).Return(nil, nil, types.ErrNotFound) + ctx.didResolver.EXPECT().Resolve(*id, nil).Return(nil, nil, resolver.ErrNotFound) actual, err := ctx.instance.GetCompoundServiceEndpoint(*id, "csType", "eType", false) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Empty(t, actual) }) t.Run("error - unknown compound service", func(t *testing.T) { ctx := newMockContext(t) ctx.didResolver.EXPECT().Resolve(*id, nil).Return(didDoc, nil, nil) actual, err := ctx.instance.GetCompoundServiceEndpoint(*id, "non-existent", "eType", false) - assert.Contains(t, err.Error(), types.ErrServiceNotFound.Error()) + assert.Contains(t, err.Error(), resolver.ErrServiceNotFound.Error()) assert.Empty(t, actual) }) t.Run("error - unknown endpoint", func(t *testing.T) { ctx := newMockContext(t) ctx.didResolver.EXPECT().Resolve(*id, nil).Return(didDoc, nil, nil) actual, err := ctx.instance.GetCompoundServiceEndpoint(*id, "csType", "non-existent", false) - assert.ErrorIs(t, err, types.ErrServiceNotFound) + assert.ErrorIs(t, err, resolver.ErrServiceNotFound) assert.Empty(t, actual) }) t.Run("error - endpoint is not an URL", func(t *testing.T) { @@ -836,7 +835,7 @@ func TestDidman_SearchOrganizations(t *testing.T) { t.Run("ok - DID document not found (omits result)", func(t *testing.T) { ctx := newMockContext(t) ctx.vcr.EXPECT().Search(reqCtx, searchTerms, false, nil).Return([]vc.VerifiableCredential{testCredential}, nil) - ctx.didResolver.EXPECT().Resolve(testDIDB, nil).Return(nil, nil, types.ErrNotFound) + ctx.didResolver.EXPECT().Resolve(testDIDB, nil).Return(nil, nil, resolver.ErrNotFound) actual, err := ctx.instance.SearchOrganizations(reqCtx, "query", nil) @@ -847,7 +846,7 @@ func TestDidman_SearchOrganizations(t *testing.T) { t.Run("ok - DID document deactivated (omits result)", func(t *testing.T) { ctx := newMockContext(t) ctx.vcr.EXPECT().Search(reqCtx, searchTerms, false, nil).Return([]vc.VerifiableCredential{testCredential}, nil) - ctx.didResolver.EXPECT().Resolve(testDIDB, nil).Return(nil, nil, types.ErrDeactivated) + ctx.didResolver.EXPECT().Resolve(testDIDB, nil).Return(nil, nil, resolver.ErrDeactivated) actual, err := ctx.instance.SearchOrganizations(reqCtx, "query", nil) @@ -944,7 +943,7 @@ func TestReferencedService(t *testing.T) { trueDID := did.MustParseDID("did:nuts:123") falseDID := did.MustParseDID("did:nuts:abc") serviceType := "RefType" - serviceRef := didservice.MakeServiceReference(trueDID, serviceType).String() + serviceRef := resolver.MakeServiceReference(trueDID, serviceType).String() compoundDocStr := `{"service":[{"id":"did:nuts:123#1","serviceEndpoint":{"nested":"%s/serviceEndpoint?type=RefType"},"type":"OtherType"}]}` endpointDocStr := `{"service":[{"id":"did:nuts:123#1","serviceEndpoint":"%s/serviceEndpoint?type=RefType","type":"OtherType"}]}` didDoc := &did.Document{} @@ -972,17 +971,17 @@ func TestReferencedService(t *testing.T) { type mockContext struct { ctrl *gomock.Controller - vdr *types.MockVDR + vdr *vdr.MockVDR vcr *vcr.MockFinder - didResolver *types.MockDIDResolver + didResolver *resolver.MockDIDResolver instance Didman audit context.Context } func newMockContext(t *testing.T) mockContext { ctrl := gomock.NewController(t) - didResolver := types.NewMockDIDResolver(ctrl) - mockVDR := types.NewMockVDR(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) + mockVDR := vdr.NewMockVDR(ctrl) mockVDR.EXPECT().Resolver().Return(didResolver).AnyTimes() mockVCR := vcr.NewMockFinder(ctrl) instance := NewDidmanInstance(mockVDR, mockVCR, jsonld.NewTestJSONLDManager(t)).(*didman) diff --git a/golden_hammer/module.go b/golden_hammer/module.go index 3827f62f62..b38b967860 100644 --- a/golden_hammer/module.go +++ b/golden_hammer/module.go @@ -29,8 +29,8 @@ import ( "github.com/nuts-foundation/nuts-node/network/transport" "github.com/nuts-foundation/nuts-node/vcr" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/url" "sort" "strings" @@ -43,7 +43,7 @@ var _ core.Named = (*GoldenHammer)(nil) var _ core.Configurable = (*GoldenHammer)(nil) var _ core.Injectable = (*GoldenHammer)(nil) -func New(vdrInstance types.VDR, didmanAPI didman.Didman) *GoldenHammer { +func New(vdrInstance vdr.VDR, didmanAPI didman.Didman) *GoldenHammer { return &GoldenHammer{ routines: &sync.WaitGroup{}, vdrInstance: vdrInstance, @@ -62,7 +62,7 @@ type GoldenHammer struct { cancelFunc context.CancelFunc routines *sync.WaitGroup didmanAPI didman.Didman - vdrInstance types.VDR + vdrInstance vdr.VDR fixedDocumentDIDs map[string]bool tlsConfig *tls.Config } @@ -139,9 +139,9 @@ func (h *GoldenHammer) registerServiceBaseURLs() error { for _, document := range documents { var endpointToRegister url.URL serviceEndpoint := getServiceEndpoint(document, transport.NutsCommServiceType) - if didservice.IsServiceReference(serviceEndpoint) { + if resolver.IsServiceReference(serviceEndpoint) { // Care organization DID document, register service pointing to vendor DID. - parentDID, err := didservice.GetDIDFromURL(serviceEndpoint) + parentDID, err := resolver.GetDIDFromURL(serviceEndpoint) if err != nil { // Invalid NutsComm reference, skip log.Logger().WithError(err). @@ -149,12 +149,12 @@ func (h *GoldenHammer) registerServiceBaseURLs() error { continue } // Only if the referenced document actually contains the service - if !h.resolveContainsService(parentDID, types.BaseURLServiceType) { - log.Logger().Debugf("Could not resolve '%s' service in referenced (NutsComm) DID document (did=%s), skipping fix for DID: %s", types.BaseURLServiceType, parentDID.String(), document.ID) + if !h.resolveContainsService(parentDID, resolver.BaseURLServiceType) { + log.Logger().Debugf("Could not resolve '%s' service in referenced (NutsComm) DID document (did=%s), skipping fix for DID: %s", resolver.BaseURLServiceType, parentDID.String(), document.ID) continue } // All us OK - endpointToRegister = didservice.MakeServiceReference(parentDID, types.BaseURLServiceType).URL + endpointToRegister = resolver.MakeServiceReference(parentDID, resolver.BaseURLServiceType).URL } else { // Vendor DID document, register resolved identifier identifier, err := h.tryResolveURL(document.ID) @@ -165,7 +165,7 @@ func (h *GoldenHammer) registerServiceBaseURLs() error { } endpointToRegister = *identifier } - _, err := h.didmanAPI.AddEndpoint(h.ctx, document.ID, types.BaseURLServiceType, endpointToRegister) + _, err := h.didmanAPI.AddEndpoint(h.ctx, document.ID, resolver.BaseURLServiceType, endpointToRegister) if err != nil { log.Logger().WithError(err). Warnf("Unable to register DID services base URL (did=%s): %s", document.ID, endpointToRegister.String()) @@ -193,7 +193,7 @@ func (h *GoldenHammer) listDocumentToFix() ([]did.Document, error) { } document, _, err := h.vdrInstance.Resolver().Resolve(id, nil) if err != nil { - if !didservice.IsFunctionalResolveError(err) { + if !resolver.IsFunctionalResolveError(err) { log.Logger().WithError(err).Infof("Can't resolve DID document, skipping fix (did=%s)", id) } continue @@ -204,7 +204,7 @@ func (h *GoldenHammer) listDocumentToFix() ([]did.Document, error) { // so we skip them. continue } - if containsService(*document, types.BaseURLServiceType) { + if containsService(*document, resolver.BaseURLServiceType) { h.fixedDocumentDIDs[id.String()] = true continue } @@ -215,7 +215,7 @@ func (h *GoldenHammer) listDocumentToFix() ([]did.Document, error) { // vendor DIDs should be fixed first. Meaning DIDs with NutsComm URL (instead of a reference). sort.SliceStable(documents, func(i, j int) bool { endpoint := getServiceEndpoint(documents[i], transport.NutsCommServiceType) - return !didservice.IsServiceReference(endpoint) + return !resolver.IsServiceReference(endpoint) }) return documents, nil } @@ -237,7 +237,7 @@ func (h *GoldenHammer) tryResolveURL(id did.DID) (*url.URL, error) { // resolveContainsService returns whether 1. given DID document can be resolved, and 2. it contains the specified service. func (h *GoldenHammer) resolveContainsService(id did.DID, serviceType string) bool { document, _, err := h.vdrInstance.Resolver().Resolve(id, nil) - if didservice.IsFunctionalResolveError(err) { + if resolver.IsFunctionalResolveError(err) { // Unresolvable DID document, nothing to do return false } diff --git a/golden_hammer/module_test.go b/golden_hammer/module_test.go index 5e095cb58c..45c8a975f3 100644 --- a/golden_hammer/module_test.go +++ b/golden_hammer/module_test.go @@ -29,9 +29,9 @@ import ( "github.com/nuts-foundation/nuts-node/test" "github.com/nuts-foundation/nuts-node/test/pki" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" + "github.com/nuts-foundation/nuts-node/vdr" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" @@ -56,12 +56,12 @@ func TestGoldenHammer_Fix(t *testing.T) { ID: vendorDID, Service: []did.Service{ { - Type: types.BaseURLServiceType, + Type: resolver.BaseURLServiceType, ServiceEndpoint: "https://example.com", }, { Type: transport.NutsCommServiceType, - ServiceEndpoint: didservice.MakeServiceReference(vendorDID, transport.NutsCommServiceType), + ServiceEndpoint: resolver.MakeServiceReference(vendorDID, transport.NutsCommServiceType), }, }, } @@ -78,19 +78,19 @@ func TestGoldenHammer_Fix(t *testing.T) { Service: []did.Service{ { Type: transport.NutsCommServiceType, - ServiceEndpoint: didservice.MakeServiceReference(vendorDID, transport.NutsCommServiceType), + ServiceEndpoint: resolver.MakeServiceReference(vendorDID, transport.NutsCommServiceType), }, }, } var clientDocumentWithBaseURL = did.Document{ Service: []did.Service{ { - Type: types.BaseURLServiceType, - ServiceEndpoint: didservice.MakeServiceReference(vendorDID, types.BaseURLServiceType), + Type: resolver.BaseURLServiceType, + ServiceEndpoint: resolver.MakeServiceReference(vendorDID, resolver.BaseURLServiceType), }, { Type: transport.NutsCommServiceType, - ServiceEndpoint: didservice.MakeServiceReference(vendorDID, transport.NutsCommServiceType), + ServiceEndpoint: resolver.MakeServiceReference(vendorDID, transport.NutsCommServiceType), }, }, } @@ -109,11 +109,11 @@ func TestGoldenHammer_Fix(t *testing.T) { t.Run("nothing to fix", func(t *testing.T) { // vendor and care organization DIDs already have the required service, so there's nothing to fix ctrl := gomock.NewController(t) - didResolver := types.NewMockDIDResolver(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) didResolver.EXPECT().Resolve(vendorDID, gomock.Any()).Return(&vendorDocumentWithBaseURL, nil, nil).MinTimes(1) didResolver.EXPECT().Resolve(clientADID, gomock.Any()).Return(&clientDocumentWithBaseURL, nil, nil).MinTimes(1) didResolver.EXPECT().Resolve(clientBDID, gomock.Any()).Return(&clientDocumentWithBaseURL, nil, nil).MinTimes(1) - vdr := types.NewMockVDR(ctrl) + vdr := vdr.NewMockVDR(ctrl) vdr.EXPECT().Resolver().Return(didResolver).MinTimes(1) vdr.EXPECT().ListOwned(gomock.Any()).Return([]did.DID{vendorDID, clientADID, clientBDID}, nil) service := New(vdr, nil) @@ -138,7 +138,7 @@ func TestGoldenHammer_Fix(t *testing.T) { docClientB := clientDocumentWithoutBaseURL docClientB.ID = clientBDID - vdr := types.NewMockVDR(ctrl) + vdr := vdr.NewMockVDR(ctrl) vdr.EXPECT().Resolver().Return(didResolver).MinTimes(1) // Order DIDs such that care organization DID is first, to test ordering vdr.EXPECT().ListOwned(gomock.Any()).Return([]did.DID{clientADID, vendorDID, clientBDID}, nil) @@ -150,15 +150,15 @@ func TestGoldenHammer_Fix(t *testing.T) { didResolver.EXPECT().Resolve(clientBDID, gomock.Any()).Return(&docClientB, nil, nil), // Vendor document is fixed first - didmanAPI.EXPECT().AddEndpoint(gomock.Any(), vendorDID, types.BaseURLServiceType, *expectedBaseURL).Return(nil, nil), + didmanAPI.EXPECT().AddEndpoint(gomock.Any(), vendorDID, resolver.BaseURLServiceType, *expectedBaseURL).Return(nil, nil), // Then client A didResolver.EXPECT().Resolve(vendorDID, gomock.Any()).Return(&vendorDocumentWithBaseURL, nil, nil), - didmanAPI.EXPECT().AddEndpoint(gomock.Any(), clientADID, types.BaseURLServiceType, *serviceRef).Return(nil, nil), + didmanAPI.EXPECT().AddEndpoint(gomock.Any(), clientADID, resolver.BaseURLServiceType, *serviceRef).Return(nil, nil), // Then client B didResolver.EXPECT().Resolve(vendorDID, gomock.Any()).Return(&vendorDocumentWithBaseURL, nil, nil), - didmanAPI.EXPECT().AddEndpoint(gomock.Any(), clientBDID, types.BaseURLServiceType, *serviceRef).Return(nil, nil), + didmanAPI.EXPECT().AddEndpoint(gomock.Any(), clientBDID, resolver.BaseURLServiceType, *serviceRef).Return(nil, nil), ) service := New(vdr, didmanAPI) service.tlsConfig = tlsServer.TLS @@ -173,7 +173,7 @@ func TestGoldenHammer_Fix(t *testing.T) { ctrl := gomock.NewController(t) didResolver := didstore.NewMockStore(ctrl) didResolver.EXPECT().Resolve(vendorDID, gomock.Any()).Return(&vendorDocumentWithoutBaseURL, nil, nil).MinTimes(1) - vdr := types.NewMockVDR(ctrl) + vdr := vdr.NewMockVDR(ctrl) vdr.EXPECT().Resolver().Return(didResolver).MinTimes(1) vdr.EXPECT().ListOwned(gomock.Any()).Return([]did.DID{vendorDID}, nil) service := New(vdr, nil) @@ -199,14 +199,14 @@ func TestGoldenHammer_Fix(t *testing.T) { didResolver.EXPECT().Resolve(clientBDID, gomock.Any()).Return(&docClientB, nil, nil).MinTimes(1) // Client C is owned, but not linked to the vendor (via NutsComm service), so do not register the service on that one didResolver.EXPECT().Resolve(clientCDID, gomock.Any()).Return(&did.Document{ID: clientCDID}, nil, nil).MinTimes(1) - vdr := types.NewMockVDR(ctrl) + vdr := vdr.NewMockVDR(ctrl) vdr.EXPECT().Resolver().Return(didResolver).MinTimes(1) vdr.EXPECT().ListOwned(gomock.Any()).Return([]did.DID{vendorDID, clientADID, clientBDID, clientCDID}, nil) didmanAPI := didman.NewMockDidman(ctrl) // AddEndpoint is not called for vendor DID (URL already present), but for client DIDs. // Not for clientC, since it's not linked to the vendor (doesn't have a NutsComm endpoint). - didmanAPI.EXPECT().AddEndpoint(gomock.Any(), clientADID, types.BaseURLServiceType, *serviceRef).Return(nil, nil) - didmanAPI.EXPECT().AddEndpoint(gomock.Any(), clientBDID, types.BaseURLServiceType, *serviceRef).Return(nil, nil) + didmanAPI.EXPECT().AddEndpoint(gomock.Any(), clientADID, resolver.BaseURLServiceType, *serviceRef).Return(nil, nil) + didmanAPI.EXPECT().AddEndpoint(gomock.Any(), clientBDID, resolver.BaseURLServiceType, *serviceRef).Return(nil, nil) service := New(vdr, didmanAPI) service.tlsConfig = tlsServer.TLS service.tlsConfig.InsecureSkipVerify = true @@ -218,9 +218,9 @@ func TestGoldenHammer_Fix(t *testing.T) { }) t.Run("resolve error", func(t *testing.T) { ctrl := gomock.NewController(t) - didResolver := types.NewMockDIDResolver(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) didResolver.EXPECT().Resolve(vendorDID, gomock.Any()).Return(nil, nil, fmt.Errorf("resolve error")) - vdr := types.NewMockVDR(ctrl) + vdr := vdr.NewMockVDR(ctrl) vdr.EXPECT().Resolver().Return(didResolver) vdr.EXPECT().ListOwned(gomock.Any()).Return([]did.DID{vendorDID}, nil) service := New(vdr, nil) @@ -241,7 +241,7 @@ func TestGoldenHammer_Lifecycle(t *testing.T) { fixCalled := &atomic.Int64{} ctrl := gomock.NewController(t) - vdr := types.NewMockVDR(ctrl) + vdr := vdr.NewMockVDR(ctrl) vdr.EXPECT().ListOwned(gomock.Any()).DoAndReturn(func(_ context.Context) ([]did.DID, error) { fixCalled.Add(1) return []did.DID{}, nil diff --git a/makefile b/makefile index 505d59b9ec..dcb4e8df8a 100644 --- a/makefile +++ b/makefile @@ -48,7 +48,12 @@ gen-mocks: mockgen -destination=vcr/verifier/mock.go -package=verifier -source=vcr/verifier/interface.go mockgen -destination=vdr/didnuts/ambassador_mock.go -package=didnuts -source=vdr/didnuts/ambassador.go mockgen -destination=vdr/didnuts/didstore/mock.go -package=didstore -source=vdr/didnuts/didstore/interface.go - mockgen -destination=vdr/types/mock.go -package=types -source=vdr/types/interface.go -self_package github.com/nuts-foundation/nuts-node/vdr/types --imports did=github.com/nuts-foundation/go-did/did + mockgen -destination=vdr/mock.go -package=vdr -source=vdr/interface.go + mockgen -destination=vdr/resolver/did_mock.go -package=resolver -source=vdr/resolver/did.go + mockgen -destination=vdr/resolver/service_mock.go -package=resolver -source=vdr/resolver/service.go + mockgen -destination=vdr/resolver/key_mock.go -package=resolver -source=vdr/resolver/key.go + mockgen -destination=vdr/management/management_mock.go -package=management -source=vdr/management/management.go + mockgen -destination=vdr/management/finder_mock.go -package=management -source=vdr/management/finder.go gen-api: oapi-codegen --config codegen/configs/common_ssi_types.yaml docs/_static/common/ssi_types.yaml | gofmt > api/ssi_types.go diff --git a/network/dag/keys.go b/network/dag/keys.go index d2249c12da..d1b4ad3de2 100644 --- a/network/dag/keys.go +++ b/network/dag/keys.go @@ -22,46 +22,45 @@ import ( "fmt" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) // SourceTXKeyResolver implements the SourceTXKeyResolver interface. type SourceTXKeyResolver struct { - Resolver types.DIDResolver + Resolver resolver.DIDResolver } func (r SourceTXKeyResolver) ResolvePublicKey(kid string, sourceTransactionsRefs []hash.SHA256Hash) (crypto.PublicKey, error) { // try all keys, continue when err == types.ErrNotFound for _, h := range sourceTransactionsRefs { - publicKey, err := resolvePublicKey(r.Resolver, kid, types.ResolveMetadata{ + publicKey, err := resolvePublicKey(r.Resolver, kid, resolver.ResolveMetadata{ SourceTransaction: &h, }) if err == nil { return publicKey, nil } - if err != types.ErrNotFound { + if err != resolver.ErrNotFound { return nil, err } } - return nil, types.ErrNotFound + return nil, resolver.ErrNotFound } -func resolvePublicKey(resolver types.DIDResolver, kid string, metadata types.ResolveMetadata) (crypto.PublicKey, error) { +func resolvePublicKey(didResolver resolver.DIDResolver, kid string, metadata resolver.ResolveMetadata) (crypto.PublicKey, error) { id, err := did.ParseDIDURL(kid) if err != nil { return nil, fmt.Errorf("invalid key ID (id=%s): %w", kid, err) } - holder, _ := didservice.GetDIDFromURL(kid) // can't fail, already parsed - doc, _, err := resolver.Resolve(holder, &metadata) + holder, _ := resolver.GetDIDFromURL(kid) // can't fail, already parsed + doc, _, err := didResolver.Resolve(holder, &metadata) if err != nil { return nil, err } vm := doc.VerificationMethod.FindByID(*id) if vm == nil { - return nil, types.ErrKeyNotFound + return nil, resolver.ErrKeyNotFound } return vm.PublicKey() diff --git a/network/dag/keys_test.go b/network/dag/keys_test.go index 3777611985..89d8bf56e3 100644 --- a/network/dag/keys_test.go +++ b/network/dag/keys_test.go @@ -24,7 +24,7 @@ import ( ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -33,7 +33,7 @@ import ( func TestNutsKeyResolver_ResolvePublicKey(t *testing.T) { ctrl := gomock.NewController(t) - didResolver := types.NewMockDIDResolver(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) keyResolver := SourceTXKeyResolver{Resolver: didResolver} pk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) doc := &did.Document{ @@ -47,7 +47,7 @@ func TestNutsKeyResolver_ResolvePublicKey(t *testing.T) { t.Run("ok by hash", func(t *testing.T) { didResolver.EXPECT().Resolve(doc.ID, gomock.Any()).Do(func(arg0 interface{}, arg1 interface{}) { - resolveMetadata := arg1.(*types.ResolveMetadata) + resolveMetadata := arg1.(*resolver.ResolveMetadata) assert.Equal(t, hash.EmptyHash(), *resolveMetadata.SourceTransaction) }).Return(doc, nil, nil) diff --git a/network/dag/pal.go b/network/dag/pal.go index 750d3cef6e..dd04449d3a 100644 --- a/network/dag/pal.go +++ b/network/dag/pal.go @@ -25,12 +25,12 @@ import ( "errors" "fmt" "github.com/nuts-foundation/nuts-node/core" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "strings" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto" "github.com/nuts-foundation/nuts-node/network/log" - "github.com/nuts-foundation/nuts-node/vdr/types" ) // palHeaderDIDSeparator holds the character(s) that separate DID entries in the PAL header, before being encrypted. @@ -53,12 +53,12 @@ func (pal PAL) Contains(id did.DID) bool { // Encrypt encodes and encrypts the given participant DIDs. // It uses the given types.KeyResolver to look up the public encryption key for each participant, // and then encrypts the PAL header using each. -func (pal PAL) Encrypt(keyResolver types.KeyResolver) (EncryptedPAL, error) { +func (pal PAL) Encrypt(keyResolver resolver.KeyResolver) (EncryptedPAL, error) { var encryptionKeys []*ecdsa.PublicKey var recipients [][]byte for _, recipient := range pal { recipients = append(recipients, []byte(recipient.String())) - _, rawKak, err := keyResolver.ResolveKey(recipient, nil, types.KeyAgreement) + _, rawKak, err := keyResolver.ResolveKey(recipient, nil, resolver.KeyAgreement) if err != nil { return nil, fmt.Errorf("unable to resolve keyAgreement key (recipient=%s): %w", recipient, err) } diff --git a/network/dag/pal_test.go b/network/dag/pal_test.go index c5e78c3a0c..97ca7a5d97 100644 --- a/network/dag/pal_test.go +++ b/network/dag/pal_test.go @@ -25,12 +25,12 @@ import ( "crypto/rand" "crypto/rsa" ssi "github.com/nuts-foundation/go-did" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "testing" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -46,9 +46,9 @@ func TestEncryptPal(t *testing.T) { // Encrypt ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKey(*pA, nil, types.KeyAgreement).Return(ssi.URI{}, pkA.Public(), nil) - keyResolver.EXPECT().ResolveKey(*pB, nil, types.KeyAgreement).Return(ssi.URI{}, pkB.Public(), nil) + keyResolver := resolver.NewMockKeyResolver(ctrl) + keyResolver.EXPECT().ResolveKey(*pA, nil, resolver.KeyAgreement).Return(ssi.URI{}, pkA.Public(), nil) + keyResolver.EXPECT().ResolveKey(*pB, nil, resolver.KeyAgreement).Return(ssi.URI{}, pkB.Public(), nil) expected := PAL{*pA, *pB} pal, err := expected.Encrypt(keyResolver) require.NoError(t, err) @@ -63,23 +63,23 @@ func TestEncryptPal(t *testing.T) { }) t.Run("ok - empty input yields empty output", func(t *testing.T) { ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) pal, err := PAL{}.Encrypt(keyResolver) assert.Nil(t, pal) assert.NoError(t, err) }) t.Run("error - keyAgreement key type is not supported", func(t *testing.T) { ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKey(*pA, nil, types.KeyAgreement).Return(ssi.URI{}, &rsa.PublicKey{}, nil) + keyResolver := resolver.NewMockKeyResolver(ctrl) + keyResolver.EXPECT().ResolveKey(*pA, nil, resolver.KeyAgreement).Return(ssi.URI{}, &rsa.PublicKey{}, nil) pal, err := PAL{*pA}.Encrypt(keyResolver) assert.Nil(t, pal) assert.EqualError(t, err, "resolved keyAgreement key is not an elliptic curve key (recipient=did:nuts:A)") }) t.Run("error - no keyAgreements", func(t *testing.T) { ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKey(*pA, nil, types.KeyAgreement).Return(ssi.URI{}, nil, types.ErrKeyNotFound) + keyResolver := resolver.NewMockKeyResolver(ctrl) + keyResolver.EXPECT().ResolveKey(*pA, nil, resolver.KeyAgreement).Return(ssi.URI{}, nil, resolver.ErrKeyNotFound) pal, err := PAL{*pA}.Encrypt(keyResolver) assert.Nil(t, pal) assert.EqualError(t, err, "unable to resolve keyAgreement key (recipient=did:nuts:A): key not found in DID document") diff --git a/network/dag/verifier.go b/network/dag/verifier.go index 54b1f4e7d6..f41deb6219 100644 --- a/network/dag/verifier.go +++ b/network/dag/verifier.go @@ -23,10 +23,10 @@ import ( "errors" "fmt" "github.com/nuts-foundation/go-stoabs" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/lestrrat-go/jwx/jwa" "github.com/lestrrat-go/jwx/jws" - "github.com/nuts-foundation/nuts-node/vdr/types" ) // ErrPreviousTransactionMissing indicates one or more of the previous transactions (which the transaction refers to) @@ -41,7 +41,7 @@ type Verifier func(tx stoabs.ReadTx, transaction Transaction) error // NewTransactionSignatureVerifier creates a transaction verifier that checks the signature of the transaction. // It uses the given KeyResolver to resolves keys that aren't embedded in the transaction. -func NewTransactionSignatureVerifier(resolver types.NutsKeyResolver) Verifier { +func NewTransactionSignatureVerifier(resolver resolver.NutsKeyResolver) Verifier { return func(_ stoabs.ReadTx, transaction Transaction) error { var signingKey crypto2.PublicKey if transaction.SigningKey() != nil { diff --git a/network/dag/verifier_test.go b/network/dag/verifier_test.go index 8b3ba0e28d..867948d26e 100644 --- a/network/dag/verifier_test.go +++ b/network/dag/verifier_test.go @@ -26,6 +26,7 @@ import ( "crypto/rand" "errors" "github.com/nuts-foundation/nuts-node/audit" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "testing" "time" @@ -33,7 +34,6 @@ import ( "github.com/nuts-foundation/go-stoabs" nutsCrypto "github.com/nuts-foundation/nuts-node/crypto" "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -124,7 +124,7 @@ func TestTransactionSignatureVerifier(t *testing.T) { t.Run("unable to resolve key by hash", func(t *testing.T) { d := CreateSignedTestTransaction(1, time.Now(), nil, "foo/bar", false) ctrl := gomock.NewController(t) - keyResolver := types.NewMockNutsKeyResolver(ctrl) + keyResolver := resolver.NewMockNutsKeyResolver(ctrl) keyResolver.EXPECT().ResolvePublicKey(gomock.Any(), gomock.Any()).Return(nil, errors.New("failed")) err := NewTransactionSignatureVerifier(keyResolver)(nil, d) @@ -133,7 +133,7 @@ func TestTransactionSignatureVerifier(t *testing.T) { }) } -var _ types.NutsKeyResolver = &staticKeyResolver{} +var _ resolver.NutsKeyResolver = &staticKeyResolver{} type staticKeyResolver struct { Key crypto.PublicKey diff --git a/network/network.go b/network/network.go index 3d579c0247..c5da1db41d 100644 --- a/network/network.go +++ b/network/network.go @@ -26,7 +26,8 @@ import ( "errors" "fmt" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net" "strings" "sync/atomic" @@ -45,7 +46,6 @@ import ( "github.com/nuts-foundation/nuts-node/network/transport/v2" "github.com/nuts-foundation/nuts-node/pki" "github.com/nuts-foundation/nuts-node/storage" - "github.com/nuts-foundation/nuts-node/vdr/types" "go.etcd.io/bbolt" ) @@ -80,13 +80,13 @@ type Network struct { connectionManager transport.ConnectionManager state dag.State keyStore crypto.KeyStore - keyResolver types.KeyResolver + keyResolver resolver.KeyResolver startTime atomic.Pointer[time.Time] peerID transport.PeerID nodeDID did.DID didStore didstore.Store - didDocumentFinder types.DocFinder - serviceResolver types.ServiceResolver + didDocumentFinder management.DocFinder + serviceResolver resolver.ServiceResolver eventPublisher events.Event storeProvider storage.Provider pkiValidator pki.Validator @@ -149,11 +149,11 @@ func NewNetworkInstance( ) *Network { return &Network{ config: config, - keyResolver: didservice.KeyResolver{Resolver: store}, + keyResolver: resolver.DIDKeyResolver{Resolver: store}, keyStore: keyStore, didStore: store, didDocumentFinder: didstore.Finder{Store: store}, - serviceResolver: didservice.ServiceResolver{Resolver: store}, + serviceResolver: resolver.DIDServiceResolver{Resolver: store}, eventPublisher: eventPublisher, storeProvider: storeProvider, pkiValidator: pkiValidator, @@ -390,7 +390,7 @@ func (n *Network) connectToKnownNodes(nodeDID did.DID) error { } // start connecting to published NutsComm addresses - otherNodes, err := n.didDocumentFinder.Find(didservice.IsActive(), didservice.ValidAt(time.Now()), didservice.ByServiceType(transport.NutsCommServiceType)) + otherNodes, err := n.didDocumentFinder.Find(management.IsActive(), management.ValidAt(time.Now()), management.ByServiceType(transport.NutsCommServiceType)) if err != nil { return err } @@ -477,8 +477,8 @@ func (n *Network) checkNodeDIDHealth(ctx context.Context, nodeDID did.DID) core. } // Check if the DID document has a resolvable and valid NutsComm endpoint - serviceRef := didservice.MakeServiceReference(nodeDID, transport.NutsCommServiceType) - nutsCommService, err := n.serviceResolver.Resolve(serviceRef, didservice.DefaultMaxServiceReferenceDepth) + serviceRef := resolver.MakeServiceReference(nodeDID, transport.NutsCommServiceType) + nutsCommService, err := n.serviceResolver.Resolve(serviceRef, resolver.DefaultMaxServiceReferenceDepth) if err != nil { // Non-existing NutsComm results in HealthStatusUnknown to make it easier to fix the issue (HealthStatusDown kills the node in certain environments) return core.Health{ diff --git a/network/network_integration_test.go b/network/network_integration_test.go index a855bd7353..3e463b7e73 100644 --- a/network/network_integration_test.go +++ b/network/network_integration_test.go @@ -25,7 +25,7 @@ import ( "fmt" testPKI "github.com/nuts-foundation/nuts-node/test/pki" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "hash/crc32" "math/rand" "net/url" @@ -1110,8 +1110,8 @@ func startNode(t *testing.T, name string, testDirectory string, opts ...func(ser config: config, didStore: didStore, keyStore: keyStore, - keyResolver: didservice.KeyResolver{Resolver: didStore}, - serviceResolver: didservice.ServiceResolver{Resolver: didStore}, + keyResolver: resolver.DIDKeyResolver{Resolver: didStore}, + serviceResolver: resolver.DIDServiceResolver{Resolver: didStore}, eventPublisher: eventPublisher, storeProvider: &storeProvider, pkiValidator: pkiValidator, diff --git a/network/network_test.go b/network/network_test.go index 25a26e494f..9bc73af825 100644 --- a/network/network_test.go +++ b/network/network_test.go @@ -30,7 +30,8 @@ import ( ssi "github.com/nuts-foundation/go-did" testPKI "github.com/nuts-foundation/nuts-node/test/pki" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "math" "net/http" "net/http/httptest" @@ -54,7 +55,6 @@ import ( "github.com/nuts-foundation/nuts-node/pki" "github.com/nuts-foundation/nuts-node/storage" "github.com/nuts-foundation/nuts-node/test/io" - vdrTypes "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -69,9 +69,9 @@ type networkTestContext struct { didStore *didstore.MockStore keyStore crypto.KeyStore keyStorage spi.Storage - keyResolver *vdrTypes.MockKeyResolver + keyResolver *resolver.MockKeyResolver protocol *transport.MockProtocol - docFinder *vdrTypes.MockDocFinder + docFinder *management.MockDocFinder eventPublisher *events.MockEvent pkiValidator *pki.MockValidator } @@ -464,8 +464,8 @@ func TestNetwork_CreateTransaction(t *testing.T) { cxt.state.EXPECT().Head(gomock.Any()) cxt.state.EXPECT().Add(gomock.Any(), gomock.Any(), payload) - cxt.keyResolver.EXPECT().ResolveKey(*sender, nil, vdrTypes.KeyAgreement).Return(ssi.MustParseURI("sender"), senderKey.Public(), nil) - cxt.keyResolver.EXPECT().ResolveKey(*receiver, nil, vdrTypes.KeyAgreement).Return(ssi.MustParseURI("receiver"), receiverKey.Public(), nil) + cxt.keyResolver.EXPECT().ResolveKey(*sender, nil, resolver.KeyAgreement).Return(ssi.MustParseURI("sender"), senderKey.Public(), nil) + cxt.keyResolver.EXPECT().ResolveKey(*receiver, nil, resolver.KeyAgreement).Return(ssi.MustParseURI("receiver"), receiverKey.Public(), nil) _, err = cxt.network.CreateTransaction(ctx, TransactionTemplate(payloadType, payload, key).WithPrivate([]did.DID{*sender, *receiver})) assert.NoError(t, err) @@ -666,7 +666,7 @@ func TestNetwork_validateNodeDID(t *testing.T) { cxt := createNetwork(t, ctrl) cxt.network.strictMode = true cxt.network.nodeDID = *nodeDID - cxt.didStore.EXPECT().Resolve(*nodeDID, nil).Return(&did.Document{}, &vdrTypes.DocumentMetadata{}, nil) + cxt.didStore.EXPECT().Resolve(*nodeDID, nil).Return(&did.Document{}, &resolver.DocumentMetadata{}, nil) health := cxt.network.checkNodeDIDHealth(ctx, *nodeDID) @@ -678,7 +678,7 @@ func TestNetwork_validateNodeDID(t *testing.T) { cxt := createNetwork(t, ctrl) cxt.network.strictMode = true cxt.network.nodeDID = *nodeDID - cxt.didStore.EXPECT().Resolve(*nodeDID, nil).Return(completeDocument, &vdrTypes.DocumentMetadata{}, nil) + cxt.didStore.EXPECT().Resolve(*nodeDID, nil).Return(completeDocument, &resolver.DocumentMetadata{}, nil) health := cxt.network.checkNodeDIDHealth(ctx, *nodeDID) @@ -691,7 +691,7 @@ func TestNetwork_validateNodeDID(t *testing.T) { _ = cxt.keyStorage.SavePrivateKey(ctx, keyID.String(), key) cxt.network.strictMode = true cxt.network.nodeDID = *nodeDID - cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(documentWithoutNutsCommService, &vdrTypes.DocumentMetadata{}, nil) + cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(documentWithoutNutsCommService, &resolver.DocumentMetadata{}, nil) health := cxt.network.checkNodeDIDHealth(ctx, *nodeDID) @@ -709,7 +709,7 @@ func TestNetwork_validateNodeDID(t *testing.T) { cxt.network.strictMode = true cxt.network.certificate = certificate cxt.network.nodeDID = *nodeDID - cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &vdrTypes.DocumentMetadata{}, nil) + cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &resolver.DocumentMetadata{}, nil) health := cxt.network.checkNodeDIDHealth(ctx, *nodeDID) @@ -727,7 +727,7 @@ func TestNetwork_validateNodeDID(t *testing.T) { cxt.network.strictMode = true cxt.network.certificate = certificate cxt.network.nodeDID = *nodeDID - cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &vdrTypes.DocumentMetadata{}, nil) + cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &resolver.DocumentMetadata{}, nil) health := cxt.network.checkNodeDIDHealth(ctx, *nodeDID) @@ -740,7 +740,7 @@ func TestNetwork_validateNodeDID(t *testing.T) { _ = cxt.keyStorage.SavePrivateKey(ctx, keyID.String(), key) cxt.network.strictMode = true cxt.network.nodeDID = *nodeDID - cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &vdrTypes.DocumentMetadata{}, nil) + cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &resolver.DocumentMetadata{}, nil) health := cxt.network.checkNodeDIDHealth(ctx, *nodeDID) @@ -758,7 +758,7 @@ func TestNetwork_validateNodeDID(t *testing.T) { cxt.network.strictMode = true cxt.network.certificate = certificate cxt.network.nodeDID = *nodeDID - cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &vdrTypes.DocumentMetadata{}, nil) + cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &resolver.DocumentMetadata{}, nil) health := cxt.network.checkNodeDIDHealth(ctx, *nodeDID) @@ -771,7 +771,7 @@ func TestNetwork_validateNodeDID(t *testing.T) { _ = cxt.keyStorage.SavePrivateKey(ctx, keyID.String(), certificate.PrivateKey) cxt.network.certificate = certificate cxt.network.nodeDID = *nodeDID - cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &vdrTypes.DocumentMetadata{}, nil) + cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &resolver.DocumentMetadata{}, nil) cxt.network.selfTestDialer.NetDialer.Timeout = 10 * time.Millisecond health := cxt.network.checkNodeDIDHealth(ctx, *nodeDID) @@ -1082,7 +1082,7 @@ func Test_connectToKnownNodes(t *testing.T) { // Use actual test instance because the unit test's createNetwork mocks too much for us network := NewTestNetworkInstance(t) - docFinder := vdrTypes.NewMockDocFinder(ctrl) + docFinder := management.NewMockDocFinder(ctrl) network.didDocumentFinder = docFinder network.config.EnableDiscovery = true @@ -1107,7 +1107,7 @@ func Test_connectToKnownNodes(t *testing.T) { // Use actual test instance because the unit test's createNetwork mocks too much for us network := NewTestNetworkInstance(t) - docFinder := vdrTypes.NewMockDocFinder(ctrl) + docFinder := management.NewMockDocFinder(ctrl) network.didDocumentFinder = docFinder network.config.EnableDiscovery = true connectionManager := transport.NewMockConnectionManager(ctrl) @@ -1274,7 +1274,7 @@ func TestNetwork_checkHealth(t *testing.T) { cxt.network.trustStore = trustStore cxt.network.certificate = certificate cxt.network.nodeDID = *nodeDID - cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &vdrTypes.DocumentMetadata{}, nil) + cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &resolver.DocumentMetadata{}, nil) cxt.pkiValidator.EXPECT().Validate([]*x509.Certificate{certificate.Leaf}) health := cxt.network.CheckHealth() @@ -1319,8 +1319,8 @@ func createNetwork(t *testing.T, ctrl *gomock.Controller, cfgFn ...func(config * } keyStorage := crypto.NewMemoryStorage() keyStore := crypto.NewTestCryptoInstance(keyStorage) - keyResolver := vdrTypes.NewMockKeyResolver(ctrl) - docFinder := vdrTypes.NewMockDocFinder(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) + docFinder := management.NewMockDocFinder(ctrl) didStore := didstore.NewMockStore(ctrl) eventPublisher := events.NewMockEvent(ctrl) storageEngine := storage.NewTestStorageEngine(t) @@ -1333,7 +1333,7 @@ func createNetwork(t *testing.T, ctrl *gomock.Controller, cfgFn ...func(config * network := NewNetworkInstance(networkConfig, didStore, keyStore, eventPublisher, storageEngine.GetProvider(ModuleName), pkiMock) network.keyResolver = keyResolver network.didStore = didStore - network.serviceResolver = didservice.ServiceResolver{Resolver: didStore} + network.serviceResolver = resolver.DIDServiceResolver{Resolver: didStore} network.didDocumentFinder = docFinder network.state = state network.connectionManager = connectionManager diff --git a/network/transport/grpc/authenticator.go b/network/transport/grpc/authenticator.go index 7eac0c3013..185b393772 100644 --- a/network/transport/grpc/authenticator.go +++ b/network/transport/grpc/authenticator.go @@ -25,8 +25,7 @@ import ( "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/network/log" "github.com/nuts-foundation/nuts-node/network/transport" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/url" "strings" ) @@ -40,12 +39,12 @@ type Authenticator interface { } // NewTLSAuthenticator creates an Authenticator that verifies node identities using TLS certificates. -func NewTLSAuthenticator(serviceResolver types.ServiceResolver) Authenticator { +func NewTLSAuthenticator(serviceResolver resolver.ServiceResolver) Authenticator { return &tlsAuthenticator{serviceResolver: serviceResolver} } type tlsAuthenticator struct { - serviceResolver types.ServiceResolver + serviceResolver resolver.ServiceResolver } func (t tlsAuthenticator) Authenticate(nodeDID did.DID, peer transport.Peer) (transport.Peer, error) { @@ -55,7 +54,7 @@ func (t tlsAuthenticator) Authenticate(nodeDID did.DID, peer transport.Peer) (tr } // Resolve NutsComm endpoint of contained in DID document associated with node DID - nutsCommService, err := t.serviceResolver.Resolve(didservice.MakeServiceReference(nodeDID, transport.NutsCommServiceType), didservice.DefaultMaxServiceReferenceDepth) + nutsCommService, err := t.serviceResolver.Resolve(resolver.MakeServiceReference(nodeDID, transport.NutsCommServiceType), resolver.DefaultMaxServiceReferenceDepth) var nutsCommURL *url.URL if err == nil { var nutsCommURLStr string @@ -84,7 +83,7 @@ func (t tlsAuthenticator) Authenticate(nodeDID did.DID, peer transport.Peer) (tr } // NewDummyAuthenticator creates an Authenticator that does not verify node identities -func NewDummyAuthenticator(_ types.ServiceResolver) Authenticator { +func NewDummyAuthenticator(_ resolver.ServiceResolver) Authenticator { return &dummyAuthenticator{} } diff --git a/network/transport/grpc/authenticator_test.go b/network/transport/grpc/authenticator_test.go index 0ae5b306cb..889c25fedf 100644 --- a/network/transport/grpc/authenticator_test.go +++ b/network/transport/grpc/authenticator_test.go @@ -20,6 +20,7 @@ package grpc import ( "crypto/x509" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "os" "testing" @@ -27,7 +28,6 @@ import ( ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/network/transport" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -48,7 +48,7 @@ func Test_tlsAuthenticator_Authenticate(t *testing.T) { t.Run("ok", func(t *testing.T) { ctrl := gomock.NewController(t) - serviceResolver := types.NewMockServiceResolver(ctrl) + serviceResolver := resolver.NewMockServiceResolver(ctrl) serviceResolver.EXPECT().Resolve(query, gomock.Any()).Return(did.Service{ServiceEndpoint: "grpc://nuts.nl:5555"}, nil) authenticator := NewTLSAuthenticator(serviceResolver) @@ -59,7 +59,7 @@ func Test_tlsAuthenticator_Authenticate(t *testing.T) { }) t.Run("ok - case insensitive comparison", func(t *testing.T) { ctrl := gomock.NewController(t) - serviceResolver := types.NewMockServiceResolver(ctrl) + serviceResolver := resolver.NewMockServiceResolver(ctrl) serviceResolver.EXPECT().Resolve(query, gomock.Any()).Return(did.Service{ServiceEndpoint: "grpc://Nuts.nl:5555"}, nil) authenticator := NewTLSAuthenticator(serviceResolver) @@ -70,7 +70,7 @@ func Test_tlsAuthenticator_Authenticate(t *testing.T) { }) t.Run("ok - wildcard comparison", func(t *testing.T) { ctrl := gomock.NewController(t) - serviceResolver := types.NewMockServiceResolver(ctrl) + serviceResolver := resolver.NewMockServiceResolver(ctrl) serviceResolver.EXPECT().Resolve(query, gomock.Any()).Return(did.Service{ServiceEndpoint: "grpc://node.nuts.nl:5555"}, nil) authenticator := NewTLSAuthenticator(serviceResolver) expectedPeer := transport.Peer{ @@ -88,7 +88,7 @@ func Test_tlsAuthenticator_Authenticate(t *testing.T) { transportPeer := transport.Peer{ID: "peer", Certificate: cert} t.Run("DNS names do not match", func(t *testing.T) { ctrl := gomock.NewController(t) - serviceResolver := types.NewMockServiceResolver(ctrl) + serviceResolver := resolver.NewMockServiceResolver(ctrl) serviceResolver.EXPECT().Resolve(query, gomock.Any()).Return(did.Service{ServiceEndpoint: "grpc://nootjes.nl:5555"}, nil) authenticator := NewTLSAuthenticator(serviceResolver) @@ -105,8 +105,8 @@ func Test_tlsAuthenticator_Authenticate(t *testing.T) { }) t.Run("DID document not found", func(t *testing.T) { ctrl := gomock.NewController(t) - serviceResolver := types.NewMockServiceResolver(ctrl) - serviceResolver.EXPECT().Resolve(query, gomock.Any()).Return(did.Service{}, types.ErrNotFound) + serviceResolver := resolver.NewMockServiceResolver(ctrl) + serviceResolver.EXPECT().Resolve(query, gomock.Any()).Return(did.Service{}, resolver.ErrNotFound) authenticator := NewTLSAuthenticator(serviceResolver) authenticatedPeer, err := authenticator.Authenticate(nodeDID, transportPeer) diff --git a/network/transport/v2/protocol.go b/network/transport/v2/protocol.go index 8f4ffc0a8b..7636a70b5f 100644 --- a/network/transport/v2/protocol.go +++ b/network/transport/v2/protocol.go @@ -24,6 +24,7 @@ import ( "fmt" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-stoabs" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "strings" "sync" "time" @@ -38,7 +39,6 @@ import ( "github.com/nuts-foundation/nuts-node/network/dag" "github.com/nuts-foundation/nuts-node/network/transport" "github.com/nuts-foundation/nuts-node/network/transport/grpc" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" ) var _ grpc.Protocol = (*protocol)(nil) @@ -74,7 +74,7 @@ func New( config Config, nodeDID did.DID, state dag.State, - didResolver vdr.DIDResolver, + didResolver resolver.DIDResolver, decrypter crypto.Decrypter, diagnosticsProvider func() transport.Diagnostics, dagStore stoabs.KVStore, @@ -101,7 +101,7 @@ type protocol struct { state dag.State ctx context.Context routines *sync.WaitGroup - didResolver vdr.DIDResolver + didResolver resolver.DIDResolver privatePayloadReceiver dag.Notifier decrypter crypto.Decrypter connectionList grpc.ConnectionList diff --git a/network/transport/v2/protocol_integration_test.go b/network/transport/v2/protocol_integration_test.go index 5db5a034db..02c06e0c73 100644 --- a/network/transport/v2/protocol_integration_test.go +++ b/network/transport/v2/protocol_integration_test.go @@ -23,7 +23,7 @@ import ( "context" "fmt" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "hash/crc32" "path" "sync" @@ -162,7 +162,7 @@ func startNode(t *testing.T, name string, configurers ...func(config *Config)) * listenAddress := fmt.Sprintf("localhost:%d", nameToPort(name)) ctx.protocol = New(*cfg, did.DID{}, ctx.state, didResolver, keyStore, nil, bboltStore).(*protocol) - authenticator := grpc.NewTLSAuthenticator(didservice.ServiceResolver{Resolver: didResolver}) + authenticator := grpc.NewTLSAuthenticator(resolver.DIDServiceResolver{Resolver: didResolver}) connectionsStore, _ := storageClient.GetProvider("network").GetKVStore("connections", storage.VolatileStorageClass) grpcCfg, err := grpc.NewConfig(listenAddress, peerID) require.NoError(t, err) diff --git a/network/transport/v2/protocol_test.go b/network/transport/v2/protocol_test.go index 68f40993a7..1912e6b273 100644 --- a/network/transport/v2/protocol_test.go +++ b/network/transport/v2/protocol_test.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "github.com/nuts-foundation/go-stoabs" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "go.uber.org/goleak" "strings" @@ -43,14 +44,13 @@ import ( "github.com/nuts-foundation/nuts-node/network/dag" "github.com/nuts-foundation/nuts-node/network/transport" "github.com/nuts-foundation/nuts-node/network/transport/grpc" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" ) type protocolMocks struct { Controller *gomock.Controller State *dag.MockState PayloadScheduler *dag.MockNotifier - DIDResolver *vdr.MockDIDResolver + DIDResolver *resolver.MockDIDResolver Decrypter *crypto.MockDecrypter Gossip *gossip.MockManager ConnectionList *grpc.MockConnectionList @@ -79,7 +79,7 @@ func newTestProtocol(t *testing.T, nodeDID *did.DID) (*protocol, protocolMocks) ctrl := gomock.NewController(t) dirname := io.TestDirectory(t) - didResolver := vdr.NewMockDIDResolver(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) decrypter := crypto.NewMockDecrypter(ctrl) state := dag.NewMockState(ctrl) gMan := gossip.NewMockManager(ctrl) diff --git a/vcr/api/openid4vci/v0/api.go b/vcr/api/openid4vci/v0/api.go index 06f3d20566..432a5e4c7b 100644 --- a/vcr/api/openid4vci/v0/api.go +++ b/vcr/api/openid4vci/v0/api.go @@ -28,7 +28,7 @@ import ( "github.com/nuts-foundation/nuts-node/vcr" "github.com/nuts-foundation/nuts-node/vcr/log" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" "net/http" ) @@ -84,7 +84,7 @@ var _ StrictServerInterface = (*Wrapper)(nil) // Wrapper wraps the OpenID4VCI API type Wrapper struct { VCR vcr.VCR - DocumentOwner types.DocumentOwner + DocumentOwner management.DocumentOwner } // Routes registers the API routes diff --git a/vcr/api/openid4vci/v0/holder_test.go b/vcr/api/openid4vci/v0/holder_test.go index ae4433397c..622d9e044c 100644 --- a/vcr/api/openid4vci/v0/holder_test.go +++ b/vcr/api/openid4vci/v0/holder_test.go @@ -26,7 +26,7 @@ import ( "github.com/nuts-foundation/nuts-node/vcr" "github.com/nuts-foundation/nuts-node/vcr/holder" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -41,7 +41,7 @@ func TestWrapper_GetOAuth2ClientMetadata(t *testing.T) { ctrl := gomock.NewController(t) wallet := holder.NewMockOpenIDHandler(ctrl) wallet.EXPECT().Metadata().Return(openid4vci.OAuth2ClientMetadata{CredentialOfferEndpoint: "endpoint"}) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil) service := vcr.NewMockVCR(ctrl) service.EXPECT().GetOpenIDHolder(gomock.Any(), holderDID).Return(wallet, nil) @@ -57,7 +57,7 @@ func TestWrapper_GetOAuth2ClientMetadata(t *testing.T) { }) t.Run("unknown tenant", func(t *testing.T) { ctrl := gomock.NewController(t) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(false, nil) api := Wrapper{DocumentOwner: documentOwner} @@ -74,7 +74,7 @@ func TestWrapper_HandleCredentialOffer(t *testing.T) { ctrl := gomock.NewController(t) wallet := holder.NewMockOpenIDHandler(ctrl) wallet.EXPECT().HandleCredentialOffer(gomock.Any(), gomock.Any()) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil) service := vcr.NewMockVCR(ctrl) service.EXPECT().GetOpenIDHolder(gomock.Any(), holderDID).Return(wallet, nil) @@ -113,7 +113,7 @@ func TestWrapper_HandleCredentialOffer(t *testing.T) { t.Run("unknown tenant", func(t *testing.T) { ctrl := gomock.NewController(t) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(false, nil) api := Wrapper{DocumentOwner: documentOwner} diff --git a/vcr/api/openid4vci/v0/issuer_test.go b/vcr/api/openid4vci/v0/issuer_test.go index 5c77853944..1758789dbd 100644 --- a/vcr/api/openid4vci/v0/issuer_test.go +++ b/vcr/api/openid4vci/v0/issuer_test.go @@ -25,7 +25,7 @@ import ( "github.com/nuts-foundation/nuts-node/vcr" "github.com/nuts-foundation/nuts-node/vcr/issuer" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -43,7 +43,7 @@ func TestWrapper_GetOpenID4VCIIssuerMetadata(t *testing.T) { oidcIssuer.EXPECT().Metadata().Return(openid4vci.CredentialIssuerMetadata{ CredentialIssuer: issuerDID.String(), }) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil) service := vcr.NewMockVCR(ctrl) service.EXPECT().GetOpenIDIssuer(gomock.Any(), issuerDID).Return(oidcIssuer, nil) @@ -56,7 +56,7 @@ func TestWrapper_GetOpenID4VCIIssuerMetadata(t *testing.T) { }) t.Run("unknown tenant", func(t *testing.T) { ctrl := gomock.NewController(t) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(false, nil) api := Wrapper{DocumentOwner: documentOwner} @@ -73,7 +73,7 @@ func TestWrapper_GetOIDCProviderMetadata(t *testing.T) { oidcIssuer.EXPECT().ProviderMetadata().Return(openid4vci.ProviderMetadata{ Issuer: issuerDID.String(), }) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil) service := vcr.NewMockVCR(ctrl) service.EXPECT().GetOpenIDIssuer(gomock.Any(), issuerDID).Return(oidcIssuer, nil) @@ -86,7 +86,7 @@ func TestWrapper_GetOIDCProviderMetadata(t *testing.T) { }) t.Run("unknown tenant", func(t *testing.T) { ctrl := gomock.NewController(t) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(false, nil) api := Wrapper{DocumentOwner: documentOwner} @@ -101,7 +101,7 @@ func TestWrapper_RequestAccessToken(t *testing.T) { ctrl := gomock.NewController(t) oidcIssuer := issuer.NewMockOpenIDHandler(ctrl) oidcIssuer.EXPECT().HandleAccessTokenRequest(gomock.Any(), "code").Return("access-token", "c_nonce", nil) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil) service := vcr.NewMockVCR(ctrl) service.EXPECT().GetOpenIDIssuer(gomock.Any(), issuerDID).Return(oidcIssuer, nil) @@ -120,7 +120,7 @@ func TestWrapper_RequestAccessToken(t *testing.T) { }) t.Run("unknown tenant", func(t *testing.T) { ctrl := gomock.NewController(t) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(false, nil) api := Wrapper{DocumentOwner: documentOwner} @@ -133,7 +133,7 @@ func TestWrapper_RequestAccessToken(t *testing.T) { t.Run("unsupported grant type", func(t *testing.T) { ctrl := gomock.NewController(t) oidcIssuer := issuer.NewMockOpenIDHandler(ctrl) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil) service := vcr.NewMockVCR(ctrl) service.EXPECT().GetOpenIDIssuer(gomock.Any(), issuerDID).Return(oidcIssuer, nil) @@ -159,7 +159,7 @@ func TestWrapper_RequestCredential(t *testing.T) { ctrl := gomock.NewController(t) oidcIssuer := issuer.NewMockOpenIDHandler(ctrl) oidcIssuer.EXPECT().HandleCredentialRequest(gomock.Any(), gomock.Any(), "access-token").Return(&vc.VerifiableCredential{}, nil) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil) service := vcr.NewMockVCR(ctrl) service.EXPECT().GetOpenIDIssuer(gomock.Any(), issuerDID).Return(oidcIssuer, nil) @@ -183,7 +183,7 @@ func TestWrapper_RequestCredential(t *testing.T) { }) t.Run("unknown tenant", func(t *testing.T) { ctrl := gomock.NewController(t) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(false, nil) api := Wrapper{DocumentOwner: documentOwner} @@ -196,7 +196,7 @@ func TestWrapper_RequestCredential(t *testing.T) { t.Run("error - no authorization header", func(t *testing.T) { ctrl := gomock.NewController(t) oidcIssuer := issuer.NewMockOpenIDHandler(ctrl) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil) service := vcr.NewMockVCR(ctrl) service.EXPECT().GetOpenIDIssuer(gomock.Any(), issuerDID).Return(oidcIssuer, nil) @@ -219,7 +219,7 @@ func TestWrapper_RequestCredential(t *testing.T) { t.Run("error - invalid authorization header", func(t *testing.T) { ctrl := gomock.NewController(t) oidcIssuer := issuer.NewMockOpenIDHandler(ctrl) - documentOwner := types.NewMockDocumentOwner(ctrl) + documentOwner := management.NewMockDocumentOwner(ctrl) documentOwner.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil) service := vcr.NewMockVCR(ctrl) service.EXPECT().GetOpenIDIssuer(gomock.Any(), issuerDID).Return(oidcIssuer, nil) diff --git a/vcr/api/vcr/v2/api.go b/vcr/api/vcr/v2/api.go index 545700ef3d..a7d9204289 100644 --- a/vcr/api/vcr/v2/api.go +++ b/vcr/api/vcr/v2/api.go @@ -24,14 +24,13 @@ import ( "errors" "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/vcr/holder" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "github.com/nuts-foundation/nuts-node/jsonld" "github.com/nuts-foundation/nuts-node/vcr/credential" vcrTypes "github.com/nuts-foundation/nuts-node/vcr/types" "github.com/nuts-foundation/nuts-node/vcr/verifier" - vdrTypes "github.com/nuts-foundation/nuts-node/vdr/types" - "time" "github.com/labstack/echo/v4" @@ -77,10 +76,10 @@ func (w *Wrapper) Routes(router core.EchoRouter) { func (w *Wrapper) ResolveStatusCode(err error) int { return core.ResolveStatusCode(err, map[error]int{ vcrTypes.ErrNotFound: http.StatusNotFound, - vdrTypes.ErrServiceNotFound: http.StatusPreconditionFailed, + resolver.ErrServiceNotFound: http.StatusPreconditionFailed, vcrTypes.ErrRevoked: http.StatusConflict, - vdrTypes.ErrNotFound: http.StatusBadRequest, - vdrTypes.ErrKeyNotFound: http.StatusBadRequest, + resolver.ErrNotFound: http.StatusBadRequest, + resolver.ErrKeyNotFound: http.StatusBadRequest, did.ErrInvalidDID: http.StatusBadRequest, }) } diff --git a/vcr/api/vcr/v2/api_test.go b/vcr/api/vcr/v2/api_test.go index db2eba5739..33eb6f98b9 100644 --- a/vcr/api/vcr/v2/api_test.go +++ b/vcr/api/vcr/v2/api_test.go @@ -23,6 +23,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "testing" "time" @@ -39,7 +40,6 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/issuer" "github.com/nuts-foundation/nuts-node/vcr/signature/proof" "github.com/nuts-foundation/nuts-node/vcr/verifier" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -248,17 +248,17 @@ func TestWrapper_IssueVC(t *testing.T) { }, { name: "missing service", - err: fmt.Errorf("nested error for: %w", types.ErrServiceNotFound), + err: fmt.Errorf("nested error for: %w", resolver.ErrServiceNotFound), statusCode: http.StatusPreconditionFailed, }, { name: "did not found", - err: fmt.Errorf("nested error for: %w", types.ErrNotFound), + err: fmt.Errorf("nested error for: %w", resolver.ErrNotFound), statusCode: http.StatusBadRequest, }, { name: "key not found", - err: fmt.Errorf("nested error for: %w", types.ErrKeyNotFound), + err: fmt.Errorf("nested error for: %w", resolver.ErrKeyNotFound), statusCode: http.StatusBadRequest, }, } diff --git a/vcr/credential/validator.go b/vcr/credential/validator.go index 1bfae34b06..7aa72d7266 100644 --- a/vcr/credential/validator.go +++ b/vcr/credential/validator.go @@ -24,7 +24,7 @@ import ( "errors" "fmt" "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/piprate/json-gold/ld" "strings" @@ -238,7 +238,7 @@ func validOperation(operation string) bool { } func validateNutsCredentialID(credential vc.VerifiableCredential) error { - id, err := didservice.GetDIDFromURL(credential.ID.String()) + id, err := resolver.GetDIDFromURL(credential.ID.String()) if err != nil { return err } diff --git a/vcr/holder/openid.go b/vcr/holder/openid.go index c2a9b53db5..aa4c3ce927 100644 --- a/vcr/holder/openid.go +++ b/vcr/holder/openid.go @@ -22,6 +22,7 @@ import ( "context" "errors" "fmt" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "time" @@ -34,7 +35,6 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/log" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" vcrTypes "github.com/nuts-foundation/nuts-node/vcr/types" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" ) // OpenIDHandler is the interface for handling issuer operations using OpenID4VCI. @@ -50,7 +50,7 @@ var nowFunc = time.Now var _ OpenIDHandler = (*openidHandler)(nil) // NewOpenIDHandler creates an OpenIDHandler that tries to retrieve offered credentials, to store it in the given credential store. -func NewOpenIDHandler(did did.DID, identifier string, httpClient core.HTTPRequestDoer, credentialStore vcrTypes.Writer, signer crypto.JWTSigner, resolver vdr.KeyResolver) OpenIDHandler { +func NewOpenIDHandler(did did.DID, identifier string, httpClient core.HTTPRequestDoer, credentialStore vcrTypes.Writer, signer crypto.JWTSigner, resolver resolver.KeyResolver) OpenIDHandler { return &openidHandler{ did: did, identifier: identifier, @@ -67,7 +67,7 @@ type openidHandler struct { identifier string credentialStore vcrTypes.Writer signer crypto.JWTSigner - resolver vdr.KeyResolver + resolver resolver.KeyResolver issuerClientCreator func(ctx context.Context, httpClient core.HTTPRequestDoer, credentialIssuerIdentifier string) (openid4vci.IssuerAPIClient, error) httpClient core.HTTPRequestDoer jsonldReader jsonld.Reader @@ -193,7 +193,7 @@ func getPreAuthorizedCodeFromOffer(offer openid4vci.CredentialOffer) string { } func (h *openidHandler) retrieveCredential(ctx context.Context, issuerClient openid4vci.IssuerAPIClient, offer *openid4vci.CredentialDefinition, tokenResponse *openid4vci.TokenResponse) (*vc.VerifiableCredential, error) { - keyID, _, err := h.resolver.ResolveKey(h.did, nil, vdr.NutsSigningKeyType) + keyID, _, err := h.resolver.ResolveKey(h.did, nil, resolver.NutsSigningKeyType) headers := map[string]interface{}{ "typ": openid4vci.JWTTypeOpenID4VCIProof, // MUST be openid4vci-proof+jwt, which explicitly types the proof JWT as recommended in Section 3.11 of [RFC8725]. "kid": keyID.String(), // JOSE Header containing the key ID. If the Credential shall be bound to a DID, the kid refers to a DID URL which identifies a particular key in the DID Document that the Credential shall be bound to. diff --git a/vcr/holder/openid_test.go b/vcr/holder/openid_test.go index fdd4818bb4..a7bca1967a 100644 --- a/vcr/holder/openid_test.go +++ b/vcr/holder/openid_test.go @@ -29,7 +29,7 @@ import ( "github.com/nuts-foundation/nuts-node/crypto" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" "github.com/nuts-foundation/nuts-node/vcr/types" - vdrTypes "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -97,8 +97,8 @@ func Test_wallet_HandleCredentialOffer(t *testing.T) { "iat": int64(1735689600), "nonce": nonce, }, gomock.Any(), "key-id").Return("signed-jwt", nil) - keyResolver := vdrTypes.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKey(holderDID, nil, vdrTypes.NutsSigningKeyType).Return(ssi.MustParseURI("key-id"), nil, nil) + keyResolver := resolver.NewMockKeyResolver(ctrl) + keyResolver.EXPECT().ResolveKey(holderDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI("key-id"), nil, nil) nowFunc = func() time.Time { return time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) @@ -237,8 +237,8 @@ func Test_wallet_HandleCredentialOffer(t *testing.T) { }, nil) jwtSigner := crypto.NewMockJWTSigner(ctrl) jwtSigner.EXPECT().SignJWT(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) - keyResolver := vdrTypes.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKey(holderDID, nil, vdrTypes.NutsSigningKeyType) + keyResolver := resolver.NewMockKeyResolver(ctrl) + keyResolver.EXPECT().ResolveKey(holderDID, nil, resolver.NutsSigningKeyType) w := NewOpenIDHandler(holderDID, "https://holder.example.com", &http.Client{}, nil, jwtSigner, keyResolver).(*openidHandler) w.issuerClientCreator = func(_ context.Context, _ core.HTTPRequestDoer, _ string) (openid4vci.IssuerAPIClient, error) { diff --git a/vcr/holder/wallet.go b/vcr/holder/wallet.go index aeae34d9ca..24d2657939 100644 --- a/vcr/holder/wallet.go +++ b/vcr/holder/wallet.go @@ -35,7 +35,7 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/signature" "github.com/nuts-foundation/nuts-node/vcr/signature/proof" "github.com/nuts-foundation/nuts-node/vcr/verifier" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) const statsShelf = "stats" @@ -43,7 +43,7 @@ const statsShelf = "stats" var credentialCountStatsKey = stoabs.BytesKey("credential_count") type wallet struct { - keyResolver vdr.KeyResolver + keyResolver resolver.KeyResolver keyStore crypto.KeyStore verifier verifier.Verifier jsonldManager jsonld.JSONLD @@ -52,7 +52,7 @@ type wallet struct { // New creates a new Wallet. func New( - keyResolver vdr.KeyResolver, keyStore crypto.KeyStore, verifier verifier.Verifier, jsonldManager jsonld.JSONLD, + keyResolver resolver.KeyResolver, keyStore crypto.KeyStore, verifier verifier.Verifier, jsonldManager jsonld.JSONLD, walletStore stoabs.KVStore) Wallet { return &wallet{ keyResolver: keyResolver, @@ -72,7 +72,7 @@ func (h wallet) BuildPresentation(ctx context.Context, credentials []vc.Verifiab } } - kid, _, err := h.keyResolver.ResolveKey(*signerDID, nil, vdr.NutsSigningKeyType) + kid, _, err := h.keyResolver.ResolveKey(*signerDID, nil, resolver.NutsSigningKeyType) if err != nil { return nil, fmt.Errorf("unable to resolve assertion key for signing VP (did=%s): %w", *signerDID, err) } diff --git a/vcr/holder/wallet_test.go b/vcr/holder/wallet_test.go index c19328786a..a2b9dfca02 100644 --- a/vcr/holder/wallet_test.go +++ b/vcr/holder/wallet_test.go @@ -28,6 +28,7 @@ import ( "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/storage" "github.com/nuts-foundation/nuts-node/vcr/credential" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "testing" "time" @@ -39,7 +40,6 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/signature/proof" "github.com/nuts-foundation/nuts-node/vcr/verifier" "github.com/nuts-foundation/nuts-node/vdr" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -63,8 +63,8 @@ func TestWallet_BuildPresentation(t *testing.T) { t.Run("ok - one VC", func(t *testing.T) { ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKey(testDID, nil, types.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) + keyResolver := resolver.NewMockKeyResolver(ctrl) + keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) w := New(keyResolver, keyStore, nil, jsonldManager, nil) @@ -83,9 +83,9 @@ func TestWallet_BuildPresentation(t *testing.T) { ProofPurpose: "authentication", }, } - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKey(testDID, nil, types.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) + keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) w := New(keyResolver, keyStore, nil, jsonldManager, nil) @@ -102,9 +102,9 @@ func TestWallet_BuildPresentation(t *testing.T) { t.Run("ok - multiple VCs", func(t *testing.T) { ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKey(testDID, nil, types.NutsSigningKeyType).Return(vdr.TestMethodDIDA.URI(), key.Public(), nil) + keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(vdr.TestMethodDIDA.URI(), key.Public(), nil) w := New(keyResolver, keyStore, nil, jsonldManager, nil) @@ -120,11 +120,11 @@ func TestWallet_BuildPresentation(t *testing.T) { t.Run("ok", func(t *testing.T) { ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) mockVerifier := verifier.NewMockVerifier(ctrl) mockVerifier.EXPECT().Validate(testCredential, &created) - keyResolver.EXPECT().ResolveKey(testDID, nil, types.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) + keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) w := New(keyResolver, keyStore, mockVerifier, jsonldManager, nil) @@ -136,11 +136,11 @@ func TestWallet_BuildPresentation(t *testing.T) { t.Run("error", func(t *testing.T) { ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) mockVerifier := verifier.NewMockVerifier(ctrl) mockVerifier.EXPECT().Validate(testCredential, &created).Return(errors.New("failed")) - keyResolver.EXPECT().ResolveKey(testDID, nil, types.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) + keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) w := New(keyResolver, keyStore, mockVerifier, jsonldManager, nil) @@ -156,9 +156,9 @@ func TestWallet_BuildPresentation(t *testing.T) { t.Run("ok", func(t *testing.T) { ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKey(testDID, nil, types.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) + keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) w := New(keyResolver, keyStore, nil, jsonldManager, nil) @@ -173,7 +173,7 @@ func TestWallet_BuildPresentation(t *testing.T) { ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) w := New(keyResolver, keyStore, nil, jsonldManager, nil) @@ -188,7 +188,7 @@ func TestWallet_BuildPresentation(t *testing.T) { ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) w := New(keyResolver, keyStore, nil, jsonldManager, nil) diff --git a/vcr/issuer/issuer.go b/vcr/issuer/issuer.go index 3e5f0f796e..100c6062e0 100644 --- a/vcr/issuer/issuer.go +++ b/vcr/issuer/issuer.go @@ -23,7 +23,7 @@ import ( "encoding/json" "fmt" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "time" "github.com/google/uuid" @@ -39,7 +39,6 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/signature/proof" "github.com/nuts-foundation/nuts-node/vcr/trust" "github.com/nuts-foundation/nuts-node/vcr/types" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" ) // TimeFunc is a function that returns the time used, for e.g. signing time. It can be set for testing purposes. @@ -53,10 +52,10 @@ var TimeFunc = time.Now // See https://github.com/nuts-foundation/nuts-node/issues/2063 func NewIssuer(store Store, vcrStore types.Writer, networkPublisher Publisher, openidHandlerFn func(ctx context.Context, id did.DID) (OpenIDHandler, error), - didResolver vdr.DIDResolver, keyStore crypto.KeyStore, jsonldManager jsonld.JSONLD, trustConfig *trust.Config, + didResolver resolver.DIDResolver, keyStore crypto.KeyStore, jsonldManager jsonld.JSONLD, trustConfig *trust.Config, ) Issuer { - resolver := vdrKeyResolver{ - publicKeyResolver: didservice.KeyResolver{Resolver: didResolver}, + keyResolver := vdrKeyResolver{ + publicKeyResolver: resolver.DIDKeyResolver{Resolver: didResolver}, privateKeyResolver: keyStore, } return &issuer{ @@ -64,9 +63,9 @@ func NewIssuer(store Store, vcrStore types.Writer, networkPublisher Publisher, networkPublisher: networkPublisher, openidHandlerFn: openidHandlerFn, walletResolver: openid4vci.DIDIdentifierResolver{ - ServiceResolver: didservice.ServiceResolver{Resolver: didResolver}, + ServiceResolver: resolver.DIDServiceResolver{Resolver: didResolver}, }, - keyResolver: resolver, + keyResolver: keyResolver, keyStore: keyStore, jsonldManager: jsonldManager, trustConfig: trustConfig, @@ -78,7 +77,7 @@ type issuer struct { store Store networkPublisher Publisher openidHandlerFn func(ctx context.Context, id did.DID) (OpenIDHandler, error) - serviceResolver vdr.ServiceResolver + serviceResolver resolver.ServiceResolver keyResolver keyResolver keyStore crypto.KeyStore trustConfig *trust.Config @@ -194,7 +193,7 @@ func (i issuer) buildVC(ctx context.Context, credentialOptions vc.VerifiableCred if err != nil { const errString = "failed to sign credential: could not resolve an assertionKey for issuer: %w" // Differentiate between a DID document not found and some other error: - if didservice.IsFunctionalResolveError(err) { + if resolver.IsFunctionalResolveError(err) { return nil, core.InvalidInputError(errString, err) } return nil, fmt.Errorf(errString, err) @@ -298,7 +297,7 @@ func (i issuer) buildRevocation(ctx context.Context, credentialID ssi.URI) (*cre if err != nil { const errString = "failed to revoke credential (%s): could not resolve an assertionKey for issuer: %w" // Differentiate between a DID document not found and some other error: - if didservice.IsFunctionalResolveError(err) { + if resolver.IsFunctionalResolveError(err) { return nil, core.InvalidInputError(errString, credentialID, err) } return nil, fmt.Errorf(errString, credentialID, err) diff --git a/vcr/issuer/issuer_test.go b/vcr/issuer/issuer_test.go index 9cb1306448..ab5f4b3569 100644 --- a/vcr/issuer/issuer_test.go +++ b/vcr/issuer/issuer_test.go @@ -26,6 +26,7 @@ import ( "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "path" "testing" @@ -45,7 +46,6 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/signature" "github.com/nuts-foundation/nuts-node/vcr/trust" vcr "github.com/nuts-foundation/nuts-node/vcr/types" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" ) func Test_issuer_buildVC(t *testing.T) { @@ -158,7 +158,7 @@ func Test_issuer_buildVC(t *testing.T) { ctrl := gomock.NewController(t) keyResolverMock := NewMockkeyResolver(ctrl) - keyResolverMock.EXPECT().ResolveAssertionKey(ctx, *issuerDID).Return(nil, vdr.ErrNotFound) + keyResolverMock.EXPECT().ResolveAssertionKey(ctx, *issuerDID).Return(nil, resolver.ErrNotFound) sut := issuer{keyResolver: keyResolverMock} credentialOptions := vc.VerifiableCredential{ @@ -479,7 +479,7 @@ _:c14n0 . ctrl := gomock.NewController(t) keyResolverMock := NewMockkeyResolver(ctrl) - keyResolverMock.EXPECT().ResolveAssertionKey(ctx, issuerDID).Return(nil, vdr.ErrNotFound) + keyResolverMock.EXPECT().ResolveAssertionKey(ctx, issuerDID).Return(nil, resolver.ErrNotFound) sut := issuer{keyResolver: keyResolverMock} _, err := sut.buildRevocation(ctx, *testVC.ID) diff --git a/vcr/issuer/keyresolver.go b/vcr/issuer/keyresolver.go index 0815519b8a..6256fb6045 100644 --- a/vcr/issuer/keyresolver.go +++ b/vcr/issuer/keyresolver.go @@ -23,18 +23,18 @@ import ( "fmt" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) // vdrKeyResolver resolves private keys based upon the VDR document resolver type vdrKeyResolver struct { - publicKeyResolver vdr.KeyResolver + publicKeyResolver resolver.KeyResolver privateKeyResolver crypto.KeyResolver } // ResolveAssertionKey is a convenience method which tries to find a assertionKey on in the VDR for a given issuerDID. func (r vdrKeyResolver) ResolveAssertionKey(ctx context.Context, issuerDID did.DID) (crypto.Key, error) { - kid, _, err := r.publicKeyResolver.ResolveKey(issuerDID, nil, vdr.AssertionMethod) + kid, _, err := r.publicKeyResolver.ResolveKey(issuerDID, nil, resolver.AssertionMethod) if err != nil { return nil, fmt.Errorf("invalid issuer: %w", err) } diff --git a/vcr/issuer/keyresolver_test.go b/vcr/issuer/keyresolver_test.go index 8c746dc7a5..2353aff38c 100644 --- a/vcr/issuer/keyresolver_test.go +++ b/vcr/issuer/keyresolver_test.go @@ -24,7 +24,7 @@ import ( ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -44,8 +44,8 @@ func Test_vdrKeyResolver_ResolveAssertionKey(t *testing.T) { t.Run("ok", func(t *testing.T) { ctrl := gomock.NewController(t) - mockPubKeyResolver := types.NewMockKeyResolver(ctrl) - mockPubKeyResolver.EXPECT().ResolveKey(*issuerDID, nil, types.NutsSigningKeyType).Return(methodID.URI(), publicKey, nil) + mockPubKeyResolver := resolver.NewMockKeyResolver(ctrl) + mockPubKeyResolver.EXPECT().ResolveKey(*issuerDID, nil, resolver.NutsSigningKeyType).Return(methodID.URI(), publicKey, nil) mockPrivKeyResolver := crypto.NewMockKeyResolver(ctrl) mockPrivKeyResolver.EXPECT().Resolve(ctx, methodID.String()).Return(crypto.NewTestKey(methodID.String()), nil) @@ -63,8 +63,8 @@ func Test_vdrKeyResolver_ResolveAssertionKey(t *testing.T) { t.Run("document for issuer not found in vdr", func(t *testing.T) { ctrl := gomock.NewController(t) - mockPubKeyResolver := types.NewMockKeyResolver(ctrl) - mockPubKeyResolver.EXPECT().ResolveKey(*issuerDID, nil, types.NutsSigningKeyType).Return(ssi.URI{}, nil, errors.New("not found")) + mockPubKeyResolver := resolver.NewMockKeyResolver(ctrl) + mockPubKeyResolver.EXPECT().ResolveKey(*issuerDID, nil, resolver.NutsSigningKeyType).Return(ssi.URI{}, nil, errors.New("not found")) mockPrivKeyResolver := crypto.NewMockKeyResolver(ctrl) sut := vdrKeyResolver{ @@ -80,8 +80,8 @@ func Test_vdrKeyResolver_ResolveAssertionKey(t *testing.T) { t.Run("key not found in crypto", func(t *testing.T) { ctrl := gomock.NewController(t) - mockPubKeyResolver := types.NewMockKeyResolver(ctrl) - mockPubKeyResolver.EXPECT().ResolveKey(*issuerDID, nil, types.NutsSigningKeyType).Return(methodID.URI(), publicKey, nil) + mockPubKeyResolver := resolver.NewMockKeyResolver(ctrl) + mockPubKeyResolver.EXPECT().ResolveKey(*issuerDID, nil, resolver.NutsSigningKeyType).Return(methodID.URI(), publicKey, nil) mockPrivKeyResolver := crypto.NewMockKeyResolver(ctrl) mockPrivKeyResolver.EXPECT().Resolve(ctx, methodID.String()).Return(nil, errors.New("not found")) diff --git a/vcr/issuer/network_publisher.go b/vcr/issuer/network_publisher.go index ef80243a49..416f77f275 100644 --- a/vcr/issuer/network_publisher.go +++ b/vcr/issuer/network_publisher.go @@ -31,26 +31,25 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/credential" "github.com/nuts-foundation/nuts-node/vcr/log" "github.com/nuts-foundation/nuts-node/vcr/types" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) type networkPublisher struct { networkTx network.Transactions - didResolver vdr.DIDResolver - serviceResolver vdr.ServiceResolver + didResolver resolver.DIDResolver + serviceResolver resolver.ServiceResolver keyResolver keyResolver } // NewNetworkPublisher creates a new networkPublisher which implements the Publisher interface. // It is the default implementation to use for issuers to publish credentials and revocations to the Nuts network. -func NewNetworkPublisher(networkTx network.Transactions, didResolver vdr.DIDResolver, keyResolver crypto.KeyResolver) Publisher { +func NewNetworkPublisher(networkTx network.Transactions, didResolver resolver.DIDResolver, keyResolver crypto.KeyResolver) Publisher { return &networkPublisher{ networkTx: networkTx, didResolver: didResolver, - serviceResolver: didservice.ServiceResolver{Resolver: didResolver}, + serviceResolver: resolver.DIDServiceResolver{Resolver: didResolver}, keyResolver: vdrKeyResolver{ - publicKeyResolver: didservice.KeyResolver{Resolver: didResolver}, + publicKeyResolver: resolver.DIDKeyResolver{Resolver: didResolver}, privateKeyResolver: keyResolver, }, } @@ -131,7 +130,7 @@ func (p networkPublisher) generateParticipants(verifiableCredential vc.Verifiabl } func (p networkPublisher) resolveNutsCommServiceOwner(DID did.DID) (*did.DID, error) { - serviceUser := didservice.MakeServiceReference(DID, transport.NutsCommServiceType) + serviceUser := resolver.MakeServiceReference(DID, transport.NutsCommServiceType) service, err := p.serviceResolver.Resolve(serviceUser, 5) if err != nil { diff --git a/vcr/issuer/network_publisher_test.go b/vcr/issuer/network_publisher_test.go index 90e924ece9..e0199471c6 100644 --- a/vcr/issuer/network_publisher_test.go +++ b/vcr/issuer/network_publisher_test.go @@ -31,7 +31,7 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/credential" types "github.com/nuts-foundation/nuts-node/vcr/types" "github.com/nuts-foundation/nuts-node/vdr" - vdrTypes "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -48,7 +48,7 @@ func Test_networkPublisher_resolveNutsCommServiceOwner(t *testing.T) { ctrl := gomock.NewController(t) sut := networkPublisher{} - mockServiceResolver := vdrTypes.NewMockServiceResolver(ctrl) + mockServiceResolver := resolver.NewMockServiceResolver(ctrl) sut.serviceResolver = mockServiceResolver mockServiceResolver.EXPECT().Resolve(expectedURIA, 5).Return(service, nil) @@ -63,7 +63,7 @@ func Test_networkPublisher_resolveNutsCommServiceOwner(t *testing.T) { ctrl := gomock.NewController(t) sut := networkPublisher{} - mockServiceResolver := vdrTypes.NewMockServiceResolver(ctrl) + mockServiceResolver := resolver.NewMockServiceResolver(ctrl) sut.serviceResolver = mockServiceResolver service := did.Service{ID: serviceID, ServiceEndpoint: "https://foo"} @@ -79,7 +79,7 @@ func Test_networkPublisher_resolveNutsCommServiceOwner(t *testing.T) { ctrl := gomock.NewController(t) sut := networkPublisher{} - mockServiceResolver := vdrTypes.NewMockServiceResolver(ctrl) + mockServiceResolver := resolver.NewMockServiceResolver(ctrl) sut.serviceResolver = mockServiceResolver mockServiceResolver.EXPECT().Resolve(expectedURIA, 5).Return(did.Service{}, errors.New("b00m!")) @@ -100,7 +100,7 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { ctrl := gomock.NewController(t) mockKeyResolver := NewMockkeyResolver(ctrl) - mockDidResolver := vdrTypes.NewMockDIDResolver(ctrl) + mockDidResolver := resolver.NewMockDIDResolver(ctrl) mockNetwork := network.NewMockTransactions(ctrl) sut := networkPublisher{keyResolver: mockKeyResolver, didResolver: mockDidResolver, networkTx: mockNetwork} @@ -114,7 +114,7 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { testKey := crypto.NewTestKey(issuerID.String() + "#abc") mockKeyResolver.EXPECT().ResolveAssertionKey(ctx, *issuerDID).Return(testKey, nil) - mockDidResolver.EXPECT().Resolve(*issuerDID, nil).Return(&did.Document{}, &vdrTypes.DocumentMetadata{}, nil) + mockDidResolver.EXPECT().Resolve(*issuerDID, nil).Return(&did.Document{}, &resolver.DocumentMetadata{}, nil) expectedTemplate := network.Template{ Key: testKey, Payload: payload, @@ -134,9 +134,9 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { ctrl := gomock.NewController(t) mockKeyResolver := NewMockkeyResolver(ctrl) - mockDidResolver := vdrTypes.NewMockDIDResolver(ctrl) + mockDidResolver := resolver.NewMockDIDResolver(ctrl) mockNetwork := network.NewMockTransactions(ctrl) - mockServiceResolver := vdrTypes.NewMockServiceResolver(ctrl) + mockServiceResolver := resolver.NewMockServiceResolver(ctrl) sut := networkPublisher{ keyResolver: mockKeyResolver, @@ -154,7 +154,7 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { testKey := crypto.NewTestKey(issuerID.String() + "#abc") mockKeyResolver.EXPECT().ResolveAssertionKey(ctx, *issuerDID).Return(testKey, nil) - mockDidResolver.EXPECT().Resolve(*issuerDID, nil).Return(&did.Document{}, &vdrTypes.DocumentMetadata{}, nil) + mockDidResolver.EXPECT().Resolve(*issuerDID, nil).Return(&did.Document{}, &resolver.DocumentMetadata{}, nil) expectedIssuerServiceURI := ssi.MustParseURI("did:nuts:123/serviceEndpoint?type=NutsComm") expectedSubjectServiceURI := ssi.MustParseURI("did:nuts:456/serviceEndpoint?type=NutsComm") mockServiceResolver.EXPECT().Resolve(expectedIssuerServiceURI, 5).Return(did.Service{ID: issuerID, ServiceEndpoint: "grpc://foo"}, nil) @@ -215,7 +215,7 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { t.Run("missing NutsCommEndpoint", func(t *testing.T) { ctrl := gomock.NewController(t) - mockServiceResolver := vdrTypes.NewMockServiceResolver(ctrl) + mockServiceResolver := resolver.NewMockServiceResolver(ctrl) sut := networkPublisher{ serviceResolver: mockServiceResolver, @@ -226,7 +226,7 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { CredentialSubject: []interface{}{credential.BaseCredentialSubject{ID: subjectID.String()}}, } expectedIssuerServiceURI := ssi.MustParseURI("did:nuts:123/serviceEndpoint?type=NutsComm") - mockServiceResolver.EXPECT().Resolve(expectedIssuerServiceURI, 5).Return(did.Service{}, vdrTypes.ErrServiceNotFound) + mockServiceResolver.EXPECT().Resolve(expectedIssuerServiceURI, 5).Return(did.Service{}, resolver.ErrServiceNotFound) err := sut.PublishCredential(ctx, credentialToPublish, false) assert.EqualError(t, err, "failed to resolve participating node (did=did:nuts:123): could not resolve NutsComm service owner: service not found in DID Document") @@ -253,7 +253,7 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { ctrl := gomock.NewController(t) mockKeyResolver := NewMockkeyResolver(ctrl) - mockDidResolver := vdrTypes.NewMockDIDResolver(ctrl) + mockDidResolver := resolver.NewMockDIDResolver(ctrl) mockNetwork := network.NewMockTransactions(ctrl) sut := networkPublisher{keyResolver: mockKeyResolver, didResolver: mockDidResolver, networkTx: mockNetwork} @@ -267,7 +267,7 @@ func Test_networkPublisher_PublishCredential(t *testing.T) { testKey := crypto.NewTestKey(issuerID.String() + "#abc") mockKeyResolver.EXPECT().ResolveAssertionKey(ctx, *issuerDID).Return(testKey, nil) - mockDidResolver.EXPECT().Resolve(*issuerDID, nil).Return(&did.Document{}, &vdrTypes.DocumentMetadata{}, nil) + mockDidResolver.EXPECT().Resolve(*issuerDID, nil).Return(&did.Document{}, &resolver.DocumentMetadata{}, nil) expectedTemplate := network.Template{ Key: testKey, Payload: payload, @@ -302,11 +302,11 @@ func Test_networkPublisher_PublishRevocation(t *testing.T) { ctrl := gomock.NewController(t) mockKeyResolver := NewMockkeyResolver(ctrl) - mockDidResolver := vdrTypes.NewMockDIDResolver(ctrl) + mockDidResolver := resolver.NewMockDIDResolver(ctrl) mockNetwork := network.NewMockTransactions(ctrl) mockKeyResolver.EXPECT().ResolveAssertionKey(ctx, *issuerDID).Return(testKey, nil) - mockDidResolver.EXPECT().Resolve(*issuerDID, nil).Return(&did.Document{}, &vdrTypes.DocumentMetadata{}, nil) + mockDidResolver.EXPECT().Resolve(*issuerDID, nil).Return(&did.Document{}, &resolver.DocumentMetadata{}, nil) revocationToPublish := credential.Revocation{ Issuer: issuerID, @@ -344,7 +344,7 @@ func Test_networkPublisher_PublishRevocation(t *testing.T) { ctrl := gomock.NewController(t) mockKeyResolver := NewMockkeyResolver(ctrl) - mockDidResolver := vdrTypes.NewMockDIDResolver(ctrl) + mockDidResolver := resolver.NewMockDIDResolver(ctrl) mockNetwork := network.NewMockTransactions(ctrl) mockKeyResolver.EXPECT().ResolveAssertionKey(ctx, *issuerDID).Return(nil, errors.New("not found")) @@ -364,7 +364,7 @@ func Test_networkPublisher_PublishRevocation(t *testing.T) { ctrl := gomock.NewController(t) mockKeyResolver := NewMockkeyResolver(ctrl) - mockDidResolver := vdrTypes.NewMockDIDResolver(ctrl) + mockDidResolver := resolver.NewMockDIDResolver(ctrl) mockNetwork := network.NewMockTransactions(ctrl) mockKeyResolver.EXPECT().ResolveAssertionKey(ctx, *issuerDID).Return(testKey, nil) @@ -384,11 +384,11 @@ func Test_networkPublisher_PublishRevocation(t *testing.T) { ctrl := gomock.NewController(t) mockKeyResolver := NewMockkeyResolver(ctrl) - mockDidResolver := vdrTypes.NewMockDIDResolver(ctrl) + mockDidResolver := resolver.NewMockDIDResolver(ctrl) mockNetwork := network.NewMockTransactions(ctrl) mockKeyResolver.EXPECT().ResolveAssertionKey(ctx, *issuerDID).Return(testKey, nil) - mockDidResolver.EXPECT().Resolve(*issuerDID, nil).Return(&did.Document{}, &vdrTypes.DocumentMetadata{}, nil) + mockDidResolver.EXPECT().Resolve(*issuerDID, nil).Return(&did.Document{}, &resolver.DocumentMetadata{}, nil) mockNetwork.EXPECT().CreateTransaction(ctx, gomock.Any()).Return(nil, errors.New("foo")) revocationToPublish := credential.Revocation{ diff --git a/vcr/issuer/openid.go b/vcr/issuer/openid.go index 073cb6b170..a8ee1ec298 100644 --- a/vcr/issuer/openid.go +++ b/vcr/issuer/openid.go @@ -37,8 +37,7 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/issuer/assets" "github.com/nuts-foundation/nuts-node/vcr/log" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "io/fs" "net/http" "os" @@ -106,7 +105,7 @@ type OpenIDHandler interface { } // NewOpenIDHandler creates a new OpenIDHandler instance. The identifier is the Credential Issuer Identifier, e.g. https://example.com/issuer/ -func NewOpenIDHandler(issuerDID did.DID, issuerIdentifierURL string, definitionsDIR string, httpClient core.HTTPRequestDoer, keyResolver types.KeyResolver, store OpenIDStore) (OpenIDHandler, error) { +func NewOpenIDHandler(issuerDID did.DID, issuerIdentifierURL string, definitionsDIR string, httpClient core.HTTPRequestDoer, keyResolver resolver.KeyResolver, store OpenIDStore) (OpenIDHandler, error) { i := &openidHandler{ issuerIdentifierURL: issuerIdentifierURL, issuerDID: issuerDID, @@ -126,7 +125,7 @@ type openidHandler struct { issuerDID did.DID definitionsDIR string credentialsSupported []map[string]interface{} - keyResolver types.KeyResolver + keyResolver resolver.KeyResolver store OpenIDStore walletClientCreator func(ctx context.Context, httpClient core.HTTPRequestDoer, walletMetadataURL string) (openid4vci.WalletAPIClient, error) httpClient core.HTTPRequestDoer @@ -321,7 +320,7 @@ func (i *openidHandler) validateProof(ctx context.Context, flow *Flow, request o var signingKeyID string token, err := crypto.ParseJWT(request.Proof.Jwt, func(kid string) (crypt.PublicKey, error) { signingKeyID = kid - return i.keyResolver.ResolveKeyByID(kid, nil, types.NutsSigningKeyType) + return i.keyResolver.ResolveKeyByID(kid, nil, resolver.NutsSigningKeyType) }, jwt.WithAcceptableSkew(5*time.Second)) if err != nil { return generateProofError(openid4vci.Error{ @@ -332,7 +331,7 @@ func (i *openidHandler) validateProof(ctx context.Context, flow *Flow, request o } // Proof must be signed by wallet to which it was offered (proof signer == offer receiver) - if signerDID, err := didservice.GetDIDFromURL(signingKeyID); err != nil || signerDID.String() != wallet.String() { + if signerDID, err := resolver.GetDIDFromURL(signingKeyID); err != nil || signerDID.String() != wallet.String() { return generateProofError(openid4vci.Error{ Err: fmt.Errorf("credential offer was signed by other DID than intended wallet: %s", signingKeyID), Code: openid4vci.InvalidProof, diff --git a/vcr/issuer/openid_test.go b/vcr/issuer/openid_test.go index 9805fc59d7..c19199c7e5 100644 --- a/vcr/issuer/openid_test.go +++ b/vcr/issuer/openid_test.go @@ -29,7 +29,7 @@ import ( "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/crypto" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -122,8 +122,8 @@ func Test_memoryIssuer_HandleCredentialRequest(t *testing.T) { return keyID, nil }) ctrl := gomock.NewController(t) - keyResolver := types.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKeyByID(keyID, nil, types.NutsSigningKeyType).AnyTimes().Return(signerKey.Public(), nil) + keyResolver := resolver.NewMockKeyResolver(ctrl) + keyResolver.EXPECT().ResolveKeyByID(keyID, nil, resolver.NutsSigningKeyType).AnyTimes().Return(signerKey.Public(), nil) createHeaders := func() map[string]interface{} { return map[string]interface{}{ @@ -264,8 +264,8 @@ func Test_memoryIssuer_HandleCredentialRequest(t *testing.T) { assert.Nil(t, response) }) t.Run("signing key is unknown", func(t *testing.T) { - keyResolver := types.NewMockKeyResolver(ctrl) - keyResolver.EXPECT().ResolveKeyByID(keyID, nil, types.NutsSigningKeyType).AnyTimes().Return(nil, types.ErrKeyNotFound) + keyResolver := resolver.NewMockKeyResolver(ctrl) + keyResolver.EXPECT().ResolveKeyByID(keyID, nil, resolver.NutsSigningKeyType).AnyTimes().Return(nil, resolver.ErrKeyNotFound) service := requireNewTestHandler(t, keyResolver) _, err := service.createOffer(ctx, issuedVC, preAuthCode) require.NoError(t, err) @@ -434,7 +434,7 @@ func assertProtocolError(t *testing.T, err error, statusCode int, message string assert.Equal(t, statusCode, protocolError.StatusCode) } -func requireNewTestHandler(t *testing.T, keyResolver types.KeyResolver) *openidHandler { +func requireNewTestHandler(t *testing.T, keyResolver resolver.KeyResolver) *openidHandler { service, err := NewOpenIDHandler(issuerDID, issuerIdentifier, definitionsDIR, &http.Client{}, keyResolver, NewOpenIDMemoryStore()) require.NoError(t, err) return service.(*openidHandler) diff --git a/vcr/openid4vci/identifiers.go b/vcr/openid4vci/identifiers.go index c2457e76ce..ffacf3cb3c 100644 --- a/vcr/openid4vci/identifiers.go +++ b/vcr/openid4vci/identifiers.go @@ -24,8 +24,7 @@ import ( "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/vcr/log" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "net/url" "sync/atomic" @@ -53,15 +52,15 @@ func (n NoopIdentifierResolver) Resolve(id did.DID) (string, error) { // DIDIdentifierResolver is a IdentifierResolver that resolves identifiers from DID documents. type DIDIdentifierResolver struct { - ServiceResolver types.ServiceResolver + ServiceResolver resolver.ServiceResolver } func (i DIDIdentifierResolver) Resolve(id did.DID) (string, error) { - service, err := i.ServiceResolver.Resolve(didservice.MakeServiceReference(id, types.BaseURLServiceType), didservice.DefaultMaxServiceReferenceDepth) - if didservice.IsFunctionalResolveError(err) { + service, err := i.ServiceResolver.Resolve(resolver.MakeServiceReference(id, resolver.BaseURLServiceType), resolver.DefaultMaxServiceReferenceDepth) + if resolver.IsFunctionalResolveError(err) { return "", nil } else if err != nil { - return "", fmt.Errorf("unable to resolve %s service: %w", types.BaseURLServiceType, err) + return "", fmt.Errorf("unable to resolve %s service: %w", resolver.BaseURLServiceType, err) } var result string _ = service.UnmarshalServiceEndpoint(&result) diff --git a/vcr/openid4vci/identifiers_test.go b/vcr/openid4vci/identifiers_test.go index e894fec216..10be177dd3 100644 --- a/vcr/openid4vci/identifiers_test.go +++ b/vcr/openid4vci/identifiers_test.go @@ -25,8 +25,7 @@ import ( "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/jsonld" "github.com/nuts-foundation/nuts-node/test/pki" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -44,9 +43,9 @@ var issuerIdentifier = "http://example.com/n2n/identity/" + issuerDID.String() var issuerService = did.Service{ ServiceEndpoint: "http://example.com/", } -var issuerQuery = ssi.MustParseURI(issuerDID.String() + "/serviceEndpoint?type=" + types.BaseURLServiceType) +var issuerQuery = ssi.MustParseURI(issuerDID.String() + "/serviceEndpoint?type=" + resolver.BaseURLServiceType) var holderDID = did.MustParseDID("did:nuts:GvkzxsezHvEc8nGhgz6Xo3jbqkHwswLmWw3CYtCm7hAW") -var holderQuery = ssi.MustParseURI(holderDID.String() + "/serviceEndpoint?type=" + types.BaseURLServiceType) +var holderQuery = ssi.MustParseURI(holderDID.String() + "/serviceEndpoint?type=" + resolver.BaseURLServiceType) var holderIdentifier = "http://example.com/n2n/identity/" + holderDID.String() var holderService = did.Service{ ServiceEndpoint: "http://example.com/", @@ -55,8 +54,8 @@ var holderService = did.Service{ func TestDIDIdentifierResolver_Resolve(t *testing.T) { t.Run("found", func(t *testing.T) { ctrl := gomock.NewController(t) - serviceResolver := types.NewMockServiceResolver(ctrl) - serviceResolver.EXPECT().Resolve(issuerQuery, didservice.DefaultMaxServiceReferenceDepth).Return(issuerService, nil) + serviceResolver := resolver.NewMockServiceResolver(ctrl) + serviceResolver.EXPECT().Resolve(issuerQuery, resolver.DefaultMaxServiceReferenceDepth).Return(issuerService, nil) resolver := DIDIdentifierResolver{ServiceResolver: serviceResolver} identifier, err := resolver.Resolve(issuerDID) @@ -66,8 +65,8 @@ func TestDIDIdentifierResolver_Resolve(t *testing.T) { }) t.Run("DID not found", func(t *testing.T) { ctrl := gomock.NewController(t) - serviceResolver := types.NewMockServiceResolver(ctrl) - serviceResolver.EXPECT().Resolve(issuerQuery, didservice.DefaultMaxServiceReferenceDepth).Return(did.Service{}, types.ErrNotFound) + serviceResolver := resolver.NewMockServiceResolver(ctrl) + serviceResolver.EXPECT().Resolve(issuerQuery, resolver.DefaultMaxServiceReferenceDepth).Return(did.Service{}, resolver.ErrNotFound) resolver := DIDIdentifierResolver{ServiceResolver: serviceResolver} identifier, err := resolver.Resolve(issuerDID) @@ -77,8 +76,8 @@ func TestDIDIdentifierResolver_Resolve(t *testing.T) { }) t.Run("service not found", func(t *testing.T) { ctrl := gomock.NewController(t) - serviceResolver := types.NewMockServiceResolver(ctrl) - serviceResolver.EXPECT().Resolve(issuerQuery, didservice.DefaultMaxServiceReferenceDepth).Return(did.Service{}, types.ErrServiceNotFound) + serviceResolver := resolver.NewMockServiceResolver(ctrl) + serviceResolver.EXPECT().Resolve(issuerQuery, resolver.DefaultMaxServiceReferenceDepth).Return(did.Service{}, resolver.ErrServiceNotFound) resolver := DIDIdentifierResolver{ServiceResolver: serviceResolver} identifier, err := resolver.Resolve(issuerDID) @@ -88,8 +87,8 @@ func TestDIDIdentifierResolver_Resolve(t *testing.T) { }) t.Run("invalid service", func(t *testing.T) { ctrl := gomock.NewController(t) - serviceResolver := types.NewMockServiceResolver(ctrl) - serviceResolver.EXPECT().Resolve(issuerQuery, didservice.DefaultMaxServiceReferenceDepth). + serviceResolver := resolver.NewMockServiceResolver(ctrl) + serviceResolver.EXPECT().Resolve(issuerQuery, resolver.DefaultMaxServiceReferenceDepth). Return(did.Service{ServiceEndpoint: map[string]string{"foo": "bar"}}, nil) resolver := DIDIdentifierResolver{ServiceResolver: serviceResolver} @@ -100,8 +99,8 @@ func TestDIDIdentifierResolver_Resolve(t *testing.T) { }) t.Run("other error", func(t *testing.T) { ctrl := gomock.NewController(t) - serviceResolver := types.NewMockServiceResolver(ctrl) - serviceResolver.EXPECT().Resolve(issuerQuery, didservice.DefaultMaxServiceReferenceDepth).Return(did.Service{}, errors.New("b00m!")) + serviceResolver := resolver.NewMockServiceResolver(ctrl) + serviceResolver.EXPECT().Resolve(issuerQuery, resolver.DefaultMaxServiceReferenceDepth).Return(did.Service{}, errors.New("b00m!")) resolver := DIDIdentifierResolver{ServiceResolver: serviceResolver} identifier, err := resolver.Resolve(issuerDID) diff --git a/vcr/store_test.go b/vcr/store_test.go index cc4d91f1f8..85585b80fc 100644 --- a/vcr/store_test.go +++ b/vcr/store_test.go @@ -26,7 +26,7 @@ import ( "encoding/json" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto/storage/spi" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "os" "testing" @@ -55,7 +55,7 @@ func TestVcr_StoreCredential(t *testing.T) { ctx := newMockContext(t) ctx.vdr.EXPECT().IsOwner(gomock.Any(), holderDID).Return(false, nil) - ctx.didResolver.EXPECT().Resolve(gomock.Any(), &types.ResolveMetadata{}).Return(documentWithPublicKey(t, pk), nil, nil) + ctx.didResolver.EXPECT().Resolve(gomock.Any(), &resolver.ResolveMetadata{}).Return(documentWithPublicKey(t, pk), nil, nil) err := ctx.vcr.StoreCredential(target, nil) @@ -68,7 +68,7 @@ func TestVcr_StoreCredential(t *testing.T) { ctx := newMockContext(t) ctx.vdr.EXPECT().IsOwner(gomock.Any(), holderDID).Return(true, nil) - ctx.didResolver.EXPECT().Resolve(gomock.Any(), &types.ResolveMetadata{}).Return(documentWithPublicKey(t, pk), nil, nil) + ctx.didResolver.EXPECT().Resolve(gomock.Any(), &resolver.ResolveMetadata{}).Return(documentWithPublicKey(t, pk), nil, nil) err := ctx.vcr.StoreCredential(target, nil) @@ -82,7 +82,7 @@ func TestVcr_StoreCredential(t *testing.T) { ctx := newMockContext(t) now := time.Now() ctx.vdr.EXPECT().IsOwner(gomock.Any(), holderDID).Return(false, nil) - ctx.didResolver.EXPECT().Resolve(gomock.Any(), &types.ResolveMetadata{ResolveTime: &now}).Return(documentWithPublicKey(t, pk), nil, nil) + ctx.didResolver.EXPECT().Resolve(gomock.Any(), &resolver.ResolveMetadata{ResolveTime: &now}).Return(documentWithPublicKey(t, pk), nil, nil) err := ctx.vcr.StoreCredential(target, &now) @@ -93,7 +93,7 @@ func TestVcr_StoreCredential(t *testing.T) { ctx := newMockContext(t) now := time.Now() ctx.vdr.EXPECT().IsOwner(gomock.Any(), holderDID).Return(false, nil) - ctx.didResolver.EXPECT().Resolve(gomock.Any(), &types.ResolveMetadata{ResolveTime: &now}).Return(documentWithPublicKey(t, pk), nil, nil) + ctx.didResolver.EXPECT().Resolve(gomock.Any(), &resolver.ResolveMetadata{ResolveTime: &now}).Return(documentWithPublicKey(t, pk), nil, nil) _ = ctx.vcr.StoreCredential(target, &now) @@ -106,7 +106,7 @@ func TestVcr_StoreCredential(t *testing.T) { ctx := newMockContext(t) now := time.Now() ctx.vdr.EXPECT().IsOwner(gomock.Any(), holderDID).Return(false, nil) - ctx.didResolver.EXPECT().Resolve(gomock.Any(), &types.ResolveMetadata{ResolveTime: &now}).Return(documentWithPublicKey(t, pk), nil, nil) + ctx.didResolver.EXPECT().Resolve(gomock.Any(), &resolver.ResolveMetadata{ResolveTime: &now}).Return(documentWithPublicKey(t, pk), nil, nil) _ = ctx.vcr.StoreCredential(target, &now) diff --git a/vcr/test.go b/vcr/test.go index e46e490186..1e0127a916 100644 --- a/vcr/test.go +++ b/vcr/test.go @@ -32,8 +32,7 @@ import ( "github.com/nuts-foundation/nuts-node/vdr" "github.com/nuts-foundation/nuts-node/vdr/didnuts" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "path" @@ -44,7 +43,7 @@ import ( type TestVCRContext struct { DIDStore didstore.Store KeyStore crypto.KeyStore - KeyResolver types.KeyResolver + KeyResolver resolver.KeyResolver VCR VCR } @@ -54,7 +53,7 @@ func NewTestVCRContext(t *testing.T, keyStore crypto.KeyStore) TestVCRContext { ctx := TestVCRContext{ DIDStore: didStore, KeyStore: keyStore, - KeyResolver: didservice.KeyResolver{Resolver: didResolver}, + KeyResolver: resolver.DIDKeyResolver{Resolver: didResolver}, } testDirectory := io.TestDirectory(t) @@ -153,9 +152,9 @@ func NewTestVCRInstanceInDir(t *testing.T, testDirectory string) *vcr { type mockContext struct { ctrl *gomock.Controller vcr *vcr - didResolver *types.MockDIDResolver + didResolver *resolver.MockDIDResolver crypto *crypto.Crypto - vdr *types.MockVDR + vdr *vdr.MockVDR } func newMockContext(t *testing.T) mockContext { @@ -166,8 +165,8 @@ func newMockContext(t *testing.T) mockContext { tx.EXPECT().Subscribe("vcr_vcs", gomock.Any(), gomock.Any()) tx.EXPECT().Subscribe("vcr_revocations", gomock.Any(), gomock.Any()) tx.EXPECT().CleanupSubscriberEvents("vcr_vcs", gomock.Any()) - didResolver := types.NewMockDIDResolver(ctrl) - vdrInstance := types.NewMockVDR(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().Resolver().Return(didResolver).AnyTimes() jsonldManager := jsonld.NewTestJSONLDManager(t) eventManager := events.NewTestManager(t) diff --git a/vcr/test/openid4vci_integration_test.go b/vcr/test/openid4vci_integration_test.go index 91869e0540..c1e6d76116 100644 --- a/vcr/test/openid4vci_integration_test.go +++ b/vcr/test/openid4vci_integration_test.go @@ -25,7 +25,9 @@ import ( httpModule "github.com/nuts-foundation/nuts-node/http" "github.com/nuts-foundation/nuts-node/network/log" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" + "github.com/nuts-foundation/nuts-node/vdr" "github.com/nuts-foundation/nuts-node/vdr/didnuts" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "io" "net/http" @@ -36,17 +38,15 @@ import ( "testing" "time" + ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/go-did/vc" "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/didman" "github.com/nuts-foundation/nuts-node/test" "github.com/nuts-foundation/nuts-node/test/node" "github.com/nuts-foundation/nuts-node/vcr" credentialTypes "github.com/nuts-foundation/nuts-node/vcr/credential" - vdrTypes "github.com/nuts-foundation/nuts-node/vdr/types" - - ssi "github.com/nuts-foundation/go-did" - "github.com/nuts-foundation/go-did/vc" "github.com/stretchr/testify/require" ) @@ -261,7 +261,7 @@ func testCredential() vc.VerifiableCredential { } func registerDID(t *testing.T, system *core.System) did.DID { - vdrService := system.FindEngineByName("vdr").(vdrTypes.VDR) + vdrService := system.FindEngineByName("vdr").(vdr.VDR) ctx := audit.TestContext() didDocument, _, err := vdrService.Create(ctx, didnuts.DefaultCreationOptions()) require.NoError(t, err) @@ -272,6 +272,6 @@ func registerDID(t *testing.T, system *core.System) did.DID { func registerBaseURL(t *testing.T, httpServerURL string, system *core.System, id did.DID) { didmanService := system.FindEngineByName("didman").(didman.Didman) baseURL, _ := url.Parse(httpServerURL) - _, err := didmanService.AddEndpoint(audit.TestContext(), id, vdrTypes.BaseURLServiceType, *baseURL) + _, err := didmanService.AddEndpoint(audit.TestContext(), id, resolver.BaseURLServiceType, *baseURL) require.NoError(t, err) } diff --git a/vcr/vcr.go b/vcr/vcr.go index dd8a0eef85..81fe82d4b5 100644 --- a/vcr/vcr.go +++ b/vcr/vcr.go @@ -29,7 +29,8 @@ import ( "github.com/nuts-foundation/nuts-node/pki" "github.com/nuts-foundation/nuts-node/vcr/credential" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "io/fs" "net/http" "path" @@ -52,7 +53,6 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/trust" "github.com/nuts-foundation/nuts-node/vcr/types" "github.com/nuts-foundation/nuts-node/vcr/verifier" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" "gopkg.in/yaml.v3" ) @@ -85,8 +85,8 @@ type vcr struct { config Config store storage.KVBackedLeiaStore keyStore crypto.KeyStore - keyResolver vdr.KeyResolver - serviceResolver vdr.ServiceResolver + keyResolver resolver.KeyResolver + serviceResolver resolver.ServiceResolver ambassador Ambassador network network.Transactions trustConfig *trust.Config @@ -237,8 +237,8 @@ func (c *vcr) Configure(config core.ServerConfig) error { var openidHandlerFn func(ctx context.Context, id did.DID) (issuer.OpenIDHandler, error) didResolver := c.vdrInstance.Resolver() - c.keyResolver = didservice.KeyResolver{Resolver: didResolver} - c.serviceResolver = didservice.ServiceResolver{Resolver: didResolver} + c.keyResolver = resolver.DIDKeyResolver{Resolver: didResolver} + c.serviceResolver = resolver.DIDServiceResolver{Resolver: didResolver} networkPublisher := issuer.NewNetworkPublisher(c.network, didResolver, c.keyStore) if c.config.OpenID4VCI.Enabled { @@ -549,7 +549,7 @@ func (c *vcr) Untrusted(credentialType ssi.URI) ([]ssi.URI, error) { } _, _, err = didResolver.Resolve(*issuerDid, nil) if err != nil { - if !(errors.Is(err, did.DeactivatedErr) || errors.Is(err, vdr.ErrNoActiveController)) { + if !(errors.Is(err, did.DeactivatedErr) || errors.Is(err, resolver.ErrNoActiveController)) { return err } } else { diff --git a/vcr/vcr_test.go b/vcr/vcr_test.go index 6f873e73c7..8290c4c4c4 100644 --- a/vcr/vcr_test.go +++ b/vcr/vcr_test.go @@ -32,6 +32,8 @@ import ( "github.com/nuts-foundation/nuts-node/storage" "github.com/nuts-foundation/nuts-node/vcr/holder" "github.com/nuts-foundation/nuts-node/vcr/openid4vci" + "github.com/nuts-foundation/nuts-node/vdr" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "os" "path" @@ -51,7 +53,6 @@ import ( "github.com/nuts-foundation/nuts-node/network" "github.com/nuts-foundation/nuts-node/test/io" vcrTypes "github.com/nuts-foundation/nuts-node/vcr/types" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -66,7 +67,7 @@ func TestVCR_Configure(t *testing.T) { t.Run("openid4vci", func(t *testing.T) { testDirectory := io.TestDirectory(t) ctrl := gomock.NewController(t) - vdrInstance := types.NewMockVDR(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().Resolver().AnyTimes() pkiProvider := pki.NewMockProvider(ctrl) pkiProvider.EXPECT().CreateTLSConfig(gomock.Any()).Return(nil, nil).AnyTimes() @@ -89,7 +90,7 @@ func TestVCR_Configure(t *testing.T) { pkiProvider.EXPECT().CreateTLSConfig(gomock.Any()).Return(nil, nil).AnyTimes() localWalletResolver := openid4vci.NewMockIdentifierResolver(ctrl) localWalletResolver.EXPECT().Resolve(issuerDID).Return("https://example.com", nil).AnyTimes() - vdrInstance := types.NewMockVDR(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().Resolver().AnyTimes() vdrInstance.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil).AnyTimes() instance := NewVCRInstance(nil, vdrInstance, nil, jsonld.NewTestJSONLDManager(t), nil, storage.NewTestStorageEngine(t), pkiProvider).(*vcr) @@ -119,7 +120,7 @@ func TestVCR_Start(t *testing.T) { t.Run("loads default indices", func(t *testing.T) { testDirectory := io.TestDirectory(t) ctrl := gomock.NewController(t) - vdrInstance := types.NewMockVDR(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().Resolver().AnyTimes() instance := NewVCRInstance( nil, @@ -169,7 +170,7 @@ func TestVCR_Start(t *testing.T) { func TestVCR_Diagnostics(t *testing.T) { testDirectory := io.TestDirectory(t) ctrl := gomock.NewController(t) - vdrInstance := types.NewMockVDR(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().Resolver().AnyTimes() instance := NewVCRInstance( nil, @@ -292,7 +293,7 @@ func Test_vcr_GetOIDCIssuer(t *testing.T) { ctx := context.Background() t.Run("found DID, owned", func(t *testing.T) { ctrl := gomock.NewController(t) - vdrInstance := types.NewMockVDR(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().IsOwner(ctx, id).Return(true, nil) identifierResolver := openid4vci.NewMockIdentifierResolver(ctrl) identifierResolver.EXPECT().Resolve(id).Return(identifier, nil) @@ -307,7 +308,7 @@ func Test_vcr_GetOIDCIssuer(t *testing.T) { }) t.Run("found DID, not owned", func(t *testing.T) { ctrl := gomock.NewController(t) - vdrInstance := types.NewMockVDR(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().IsOwner(ctx, id).Return(false, nil) identifierResolver := openid4vci.NewMockIdentifierResolver(ctrl) identifierResolver.EXPECT().Resolve(id).Return(identifier, nil) @@ -340,7 +341,7 @@ func Test_vcr_GetOIDCWallet(t *testing.T) { ctx := context.Background() ctrl := gomock.NewController(t) - vdrInstance := types.NewMockVDR(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().IsOwner(ctx, id).Return(true, nil) identifierResolver := openid4vci.NewMockIdentifierResolver(ctrl) identifierResolver.EXPECT().Resolve(id).Return(identifier, nil) @@ -356,8 +357,8 @@ func Test_vcr_GetOIDCWallet(t *testing.T) { func TestVcr_Untrusted(t *testing.T) { instance := NewTestVCRInstance(t) ctrl := gomock.NewController(t) - mockDidResolver := types.NewMockDIDResolver(ctrl) - vdrInstance := types.NewMockVDR(ctrl) + mockDidResolver := resolver.NewMockDIDResolver(ctrl) + vdrInstance := vdr.NewMockVDR(ctrl) vdrInstance.EXPECT().Resolver().Return(mockDidResolver).AnyTimes() instance.vdrInstance = vdrInstance testCredential := vc.VerifiableCredential{} @@ -393,7 +394,7 @@ func TestVcr_Untrusted(t *testing.T) { }) t.Run("Untrusted - no active controller", func(t *testing.T) { confirmUntrustedStatus(t, func(issuer ssi.URI) ([]ssi.URI, error) { - mockDidResolver.EXPECT().Resolve(did.MustParseDIDURL(testCredential.Issuer.String()), nil).Return(nil, nil, types.ErrNoActiveController) + mockDidResolver.EXPECT().Resolve(did.MustParseDIDURL(testCredential.Issuer.String()), nil).Return(nil, nil, resolver.ErrNoActiveController) return instance.Untrusted(issuer) }, 0) }) @@ -457,7 +458,7 @@ func TestVcr_Migrate(t *testing.T) { ctx := audit.TestContext() ctrl := gomock.NewController(t) instance := NewTestVCRInstance(t) - mockVDR := types.NewMockVDR(ctrl) + mockVDR := vdr.NewMockVDR(ctrl) ownedDID := did.MustParseDID("did:nuts:owned") mockVDR.EXPECT().IsOwner(gomock.Any(), ownedDID).Return(true, nil) mockVDR.EXPECT().IsOwner(gomock.Any(), did.MustParseDID("did:nuts:foo")).Return(false, nil) diff --git a/vcr/verifier/verifier.go b/vcr/verifier/verifier.go index 66ee2b7343..3cb4ec938b 100644 --- a/vcr/verifier/verifier.go +++ b/vcr/verifier/verifier.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "strings" "time" @@ -34,7 +35,6 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/signature/proof" "github.com/nuts-foundation/nuts-node/vcr/trust" "github.com/nuts-foundation/nuts-node/vcr/types" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" ) var timeFunc = time.Now @@ -47,8 +47,8 @@ const ( // It implements the generic methods for verifying verifiable credentials and verifiable presentations. // It does not know anything about the semantics of a credential. It should support a wide range of types. type verifier struct { - didResolver vdr.DIDResolver - keyResolver vdr.KeyResolver + didResolver resolver.DIDResolver + keyResolver resolver.KeyResolver jsonldManager jsonld.JSONLD store Store trustConfig *trust.Config @@ -79,7 +79,7 @@ func (e VerificationError) Error() string { } // NewVerifier creates a new instance of the verifier. It needs a key resolver for validating signatures. -func NewVerifier(store Store, didResolver vdr.DIDResolver, keyResolver vdr.KeyResolver, jsonldManager jsonld.JSONLD, trustConfig *trust.Config) Verifier { +func NewVerifier(store Store, didResolver resolver.DIDResolver, keyResolver resolver.KeyResolver, jsonldManager jsonld.JSONLD, trustConfig *trust.Config) Verifier { return &verifier{store: store, didResolver: didResolver, keyResolver: keyResolver, jsonldManager: jsonldManager, trustConfig: trustConfig} } @@ -128,7 +128,7 @@ func (v *verifier) Validate(credentialToVerify vc.VerifiableCredential, at *time } // find key - pk, err := v.keyResolver.ResolveKeyByID(ldProof.VerificationMethod.String(), at, vdr.NutsSigningKeyType) + pk, err := v.keyResolver.ResolveKeyByID(ldProof.VerificationMethod.String(), at, resolver.NutsSigningKeyType) if err != nil { if at == nil { return fmt.Errorf("unable to resolve signing key: %w", err) @@ -179,7 +179,7 @@ func (v verifier) Verify(credentialToVerify vc.VerifiableCredential, allowUntrus // Check signature if checkSignature { issuerDID, _ := did.ParseDID(credentialToVerify.Issuer.String()) - _, _, err = v.didResolver.Resolve(*issuerDID, &vdr.ResolveMetadata{ResolveTime: validAt, AllowDeactivated: false}) + _, _, err = v.didResolver.Resolve(*issuerDID, &resolver.ResolveMetadata{ResolveTime: validAt, AllowDeactivated: false}) if err != nil { return fmt.Errorf("could not validate issuer: %w", err) } @@ -239,7 +239,7 @@ func (v *verifier) RegisterRevocation(revocation credential.Revocation) error { return errors.New("verificationMethod should owned by the issuer") } - pk, err := v.keyResolver.ResolveKeyByID(revocation.Proof.VerificationMethod.String(), &revocation.Date, vdr.NutsSigningKeyType) + pk, err := v.keyResolver.ResolveKeyByID(revocation.Proof.VerificationMethod.String(), &revocation.Date, resolver.NutsSigningKeyType) if err != nil { return fmt.Errorf("unable to resolve key for revocation: %w", err) } @@ -283,7 +283,7 @@ func (v verifier) doVerifyVP(vcVerifier Verifier, vp vc.VerifiablePresentation, } // Validate signature - signingKey, err := v.keyResolver.ResolveKeyByID(ldProof.VerificationMethod.String(), validAt, vdr.NutsSigningKeyType) + signingKey, err := v.keyResolver.ResolveKeyByID(ldProof.VerificationMethod.String(), validAt, resolver.NutsSigningKeyType) if err != nil { return nil, fmt.Errorf("unable to resolve valid signing key: %w", err) } diff --git a/vcr/verifier/verifier_test.go b/vcr/verifier/verifier_test.go index 0bfa38c591..841d5ee383 100644 --- a/vcr/verifier/verifier_test.go +++ b/vcr/verifier/verifier_test.go @@ -23,6 +23,7 @@ import ( "encoding/json" "errors" "github.com/nuts-foundation/nuts-node/crypto/storage/spi" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "os" "path" @@ -40,7 +41,6 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/trust" "github.com/nuts-foundation/nuts-node/vcr/types" "github.com/nuts-foundation/nuts-node/vdr" - vdrTypes "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -74,7 +74,7 @@ func Test_verifier_Validate(t *testing.T) { ctx := newMockContext(t) instance := ctx.verifier - ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, gomock.Any(), vdrTypes.NutsSigningKeyType).Return(pk, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, gomock.Any(), resolver.NutsSigningKeyType).Return(pk, nil) err := instance.Validate(testCredential(t), nil) @@ -123,7 +123,7 @@ func Test_verifier_Validate(t *testing.T) { vc2 := testCredential(t) vc2.IssuanceDate = time.Now() - ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, vdrTypes.NutsSigningKeyType).Return(pk, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, resolver.NutsSigningKeyType).Return(pk, nil) err := instance.Validate(vc2, nil) @@ -139,7 +139,7 @@ func Test_verifier_Validate(t *testing.T) { pr[0].Created = time.Now() vc2.Proof = []interface{}{pr[0]} - ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, vdrTypes.NutsSigningKeyType).Return(pk, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, resolver.NutsSigningKeyType).Return(pk, nil) err := instance.Validate(vc2, nil) @@ -159,7 +159,7 @@ func Test_verifier_Validate(t *testing.T) { t.Run("error - wrong jws in proof", func(t *testing.T) { ctx := newMockContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, vdrTypes.NutsSigningKeyType).Return(pk, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, resolver.NutsSigningKeyType).Return(pk, nil) instance := ctx.verifier vc2 := testCredential(t) pr := make([]vc.JSONWebSignature2020Proof, 0) @@ -174,7 +174,7 @@ func Test_verifier_Validate(t *testing.T) { t.Run("error - wrong base64 encoding in jws", func(t *testing.T) { ctx := newMockContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, vdrTypes.NutsSigningKeyType).Return(pk, nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, resolver.NutsSigningKeyType).Return(pk, nil) instance := ctx.verifier vc2 := testCredential(t) pr := make([]vc.JSONWebSignature2020Proof, 0) @@ -191,7 +191,7 @@ func Test_verifier_Validate(t *testing.T) { ctx := newMockContext(t) instance := ctx.verifier - ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, vdrTypes.NutsSigningKeyType).Return(nil, errors.New("b00m!")) + ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, nil, resolver.NutsSigningKeyType).Return(nil, errors.New("b00m!")) err := instance.Validate(testCredential(t), nil) @@ -231,7 +231,7 @@ func TestVerifier_Verify(t *testing.T) { subject := testCredential(t) subject.IssuanceDate.Add(-1 * time.Minute) - ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, gomock.Any(), vdrTypes.NutsSigningKeyType).Return(nil, errors.New("not found")) + ctx.keyResolver.EXPECT().ResolveKeyByID(testKID, gomock.Any(), resolver.NutsSigningKeyType).Return(nil, errors.New("not found")) at := time.Now() err := instance.Validate(subject, &at) @@ -250,7 +250,7 @@ func TestVerifier_Verify(t *testing.T) { ctx.store.EXPECT().GetRevocations(*vc.ID).Return(nil, ErrNotFound) proofs, _ := vc.Proofs() ctx.didResolver.EXPECT().Resolve(did.MustParseDID(vc.Issuer.String()), gomock.Any()).Return(nil, nil, nil) - ctx.keyResolver.EXPECT().ResolveKeyByID(proofs[0].VerificationMethod.String(), nil, vdrTypes.NutsSigningKeyType).Return(nil, vdrTypes.ErrKeyNotFound) + ctx.keyResolver.EXPECT().ResolveKeyByID(proofs[0].VerificationMethod.String(), nil, resolver.NutsSigningKeyType).Return(nil, resolver.ErrKeyNotFound) validationErr := ctx.verifier.Verify(vc, true, true, nil) @@ -260,7 +260,7 @@ func TestVerifier_Verify(t *testing.T) { t.Run("fails when controller or issuer is deactivated", func(t *testing.T) { ctx := newMockContext(t) ctx.store.EXPECT().GetRevocations(*vc.ID).Return(nil, ErrNotFound) - ctx.didResolver.EXPECT().Resolve(did.MustParseDID(vc.Issuer.String()), gomock.Any()).Return(nil, nil, vdrTypes.ErrDeactivated) + ctx.didResolver.EXPECT().Resolve(did.MustParseDID(vc.Issuer.String()), gomock.Any()).Return(nil, nil, resolver.ErrDeactivated) validationErr := ctx.verifier.Verify(vc, true, true, nil) @@ -417,7 +417,7 @@ func Test_verifier_CheckAndStoreRevocation(t *testing.T) { t.Run("it checks and stores a valid revocation", func(t *testing.T) { sut := newMockContext(t) - sut.keyResolver.EXPECT().ResolveKeyByID(revocation.Proof.VerificationMethod.String(), &revocation.Date, vdrTypes.NutsSigningKeyType).Return(key, nil) + sut.keyResolver.EXPECT().ResolveKeyByID(revocation.Proof.VerificationMethod.String(), &revocation.Date, resolver.NutsSigningKeyType).Return(key, nil) sut.store.EXPECT().StoreRevocation(revocation) err := sut.verifier.RegisterRevocation(revocation) assert.NoError(t, err) @@ -461,14 +461,14 @@ func Test_verifier_CheckAndStoreRevocation(t *testing.T) { t.Run("it handles an unknown key error", func(t *testing.T) { sut := newMockContext(t) - sut.keyResolver.EXPECT().ResolveKeyByID(revocation.Proof.VerificationMethod.String(), &revocation.Date, vdrTypes.NutsSigningKeyType).Return(nil, errors.New("unknown key")) + sut.keyResolver.EXPECT().ResolveKeyByID(revocation.Proof.VerificationMethod.String(), &revocation.Date, resolver.NutsSigningKeyType).Return(nil, errors.New("unknown key")) err := sut.verifier.RegisterRevocation(revocation) assert.EqualError(t, err, "unable to resolve key for revocation: unknown key") }) t.Run("it handles an error from store operation", func(t *testing.T) { sut := newMockContext(t) - sut.keyResolver.EXPECT().ResolveKeyByID(revocation.Proof.VerificationMethod.String(), &revocation.Date, vdrTypes.NutsSigningKeyType).Return(key, nil) + sut.keyResolver.EXPECT().ResolveKeyByID(revocation.Proof.VerificationMethod.String(), &revocation.Date, resolver.NutsSigningKeyType).Return(key, nil) sut.store.EXPECT().StoreRevocation(revocation).Return(errors.New("storage error")) err := sut.verifier.RegisterRevocation(revocation) assert.EqualError(t, err, "unable to store revocation: storage error") @@ -477,7 +477,7 @@ func Test_verifier_CheckAndStoreRevocation(t *testing.T) { t.Run("it handles an invalid signature error", func(t *testing.T) { sut := newMockContext(t) otherKey := crypto.NewTestKey("did:nuts:123#abc").Public() - sut.keyResolver.EXPECT().ResolveKeyByID(revocation.Proof.VerificationMethod.String(), &revocation.Date, vdrTypes.NutsSigningKeyType).Return(otherKey, nil) + sut.keyResolver.EXPECT().ResolveKeyByID(revocation.Proof.VerificationMethod.String(), &revocation.Date, resolver.NutsSigningKeyType).Return(otherKey, nil) err := sut.verifier.RegisterRevocation(revocation) assert.EqualError(t, err, "unable to verify revocation signature: invalid proof signature: failed to verify signature using ecdsa") }) @@ -535,7 +535,7 @@ func TestVerifier_VerifyVP(t *testing.T) { var validAt *time.Time ctx := newMockContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, vdrTypes.NutsSigningKeyType).Return(vdr.TestMethodDIDAPrivateKey().Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, resolver.NutsSigningKeyType).Return(vdr.TestMethodDIDAPrivateKey().Public(), nil) vcs, err := ctx.verifier.VerifyVP(vp, false, false, validAt) @@ -548,7 +548,7 @@ func TestVerifier_VerifyVP(t *testing.T) { var validAt *time.Time ctx := newMockContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, vdrTypes.NutsSigningKeyType).Return(vdr.TestMethodDIDAPrivateKey().Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, resolver.NutsSigningKeyType).Return(vdr.TestMethodDIDAPrivateKey().Public(), nil) mockVerifier := NewMockVerifier(ctx.ctrl) mockVerifier.EXPECT().Verify(vp.VerifiableCredential[0], false, true, validAt) @@ -564,7 +564,7 @@ func TestVerifier_VerifyVP(t *testing.T) { var validAt *time.Time ctx := newMockContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, vdrTypes.NutsSigningKeyType).Return(vdr.TestMethodDIDAPrivateKey().Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, resolver.NutsSigningKeyType).Return(vdr.TestMethodDIDAPrivateKey().Public(), nil) mockVerifier := NewMockVerifier(ctx.ctrl) mockVerifier.EXPECT().Verify(vp.VerifiableCredential[0], true, true, validAt) @@ -594,7 +594,7 @@ func TestVerifier_VerifyVP(t *testing.T) { var validAt *time.Time ctx := newMockContext(t) - ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, vdrTypes.NutsSigningKeyType).Return(vdr.TestMethodDIDAPrivateKey().Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, resolver.NutsSigningKeyType).Return(vdr.TestMethodDIDAPrivateKey().Public(), nil) mockVerifier := NewMockVerifier(ctx.ctrl) mockVerifier.EXPECT().Verify(vp.VerifiableCredential[0], false, true, validAt).Return(errors.New("invalid")) @@ -611,7 +611,7 @@ func TestVerifier_VerifyVP(t *testing.T) { ctx := newMockContext(t) // Return incorrect key, causing signature verification failure - ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, vdrTypes.NutsSigningKeyType).Return(vdr.TestMethodDIDBPrivateKey().Public(), nil) + ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, resolver.NutsSigningKeyType).Return(vdr.TestMethodDIDBPrivateKey().Public(), nil) vcs, err := ctx.verifier.VerifyVP(vp, false, false, validAt) @@ -625,11 +625,11 @@ func TestVerifier_VerifyVP(t *testing.T) { ctx := newMockContext(t) // Return incorrect key, causing signature verification failure - ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, vdrTypes.NutsSigningKeyType).Return(nil, vdrTypes.ErrKeyNotFound) + ctx.keyResolver.EXPECT().ResolveKeyByID(vpSignerKeyID.String(), validAt, resolver.NutsSigningKeyType).Return(nil, resolver.ErrKeyNotFound) vcs, err := ctx.verifier.VerifyVP(vp, false, false, validAt) - assert.ErrorIs(t, err, vdrTypes.ErrKeyNotFound) + assert.ErrorIs(t, err, resolver.ErrKeyNotFound) assert.Empty(t, vcs) }) t.Run("error - invalid proof", func(t *testing.T) { @@ -725,8 +725,8 @@ func TestVerificationError_Is(t *testing.T) { type mockContext struct { ctrl *gomock.Controller - didResolver *vdrTypes.MockDIDResolver - keyResolver *vdrTypes.MockKeyResolver + didResolver *resolver.MockDIDResolver + keyResolver *resolver.MockKeyResolver store *MockStore trustConfig *trust.Config verifier *verifier @@ -735,8 +735,8 @@ type mockContext struct { func newMockContext(t *testing.T) mockContext { t.Helper() ctrl := gomock.NewController(t) - didResolver := vdrTypes.NewMockDIDResolver(ctrl) - keyResolver := vdrTypes.NewMockKeyResolver(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) + keyResolver := resolver.NewMockKeyResolver(ctrl) jsonldManager := jsonld.NewTestJSONLDManager(t) verifierStore := NewMockStore(ctrl) trustConfig := trust.NewConfig(path.Join(io.TestDirectory(t), "trust.yaml")) diff --git a/vdr/api/v1/api.go b/vdr/api/v1/api.go index d745bbae61..a44a297bca 100644 --- a/vdr/api/v1/api.go +++ b/vdr/api/v1/api.go @@ -25,6 +25,8 @@ import ( "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/vdr" "github.com/nuts-foundation/nuts-node/vdr/didnuts" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "time" @@ -32,7 +34,6 @@ import ( "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/nuts-foundation/nuts-node/vdr/types" ) var _ StrictServerInterface = (*Wrapper)(nil) @@ -40,20 +41,20 @@ var _ core.ErrorStatusCodeResolver = (*Wrapper)(nil) // Wrapper is needed to connect the implementation to the echo ServiceWrapper type Wrapper struct { - VDR types.VDR - DocManipulator types.DocManipulator + VDR vdr.VDR + DocManipulator management.DocManipulator } // ResolveStatusCode maps errors returned by this API to specific HTTP status codes. func (a *Wrapper) ResolveStatusCode(err error) int { return core.ResolveStatusCode(err, map[error]int{ - types.ErrNotFound: http.StatusNotFound, - types.ErrDIDNotManagedByThisNode: http.StatusForbidden, - types.ErrDeactivated: http.StatusConflict, - types.ErrNoActiveController: http.StatusConflict, - types.ErrDuplicateService: http.StatusBadRequest, - didnuts.ErrInvalidOptions: http.StatusBadRequest, - did.ErrInvalidDID: http.StatusBadRequest, + resolver.ErrNotFound: http.StatusNotFound, + resolver.ErrDIDNotManagedByThisNode: http.StatusForbidden, + resolver.ErrDeactivated: http.StatusConflict, + resolver.ErrNoActiveController: http.StatusConflict, + resolver.ErrDuplicateService: http.StatusBadRequest, + didnuts.ErrInvalidOptions: http.StatusBadRequest, + did.ErrInvalidDID: http.StatusBadRequest, }) } @@ -145,7 +146,7 @@ func (a *Wrapper) GetDID(ctx context.Context, request GetDIDRequestObject) (GetD if err != nil { return nil, core.InvalidInputError("given did is not valid: %w", err) } - resolverMetadata := &types.ResolveMetadata{AllowDeactivated: true} + resolverMetadata := &resolver.ResolveMetadata{AllowDeactivated: true} params := request.Params if params.VersionId != nil { diff --git a/vdr/api/v1/api_test.go b/vdr/api/v1/api_test.go index 58659a7bb2..867af71f8d 100644 --- a/vdr/api/v1/api_test.go +++ b/vdr/api/v1/api_test.go @@ -19,7 +19,10 @@ import ( "context" "errors" "github.com/nuts-foundation/nuts-node/audit" + "github.com/nuts-foundation/nuts-node/vdr" "github.com/nuts-foundation/nuts-node/vdr/didnuts" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/http" "testing" "time" @@ -27,7 +30,6 @@ import ( "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -104,13 +106,13 @@ func TestWrapper_GetDID(t *testing.T) { didDoc := &did.Document{ ID: *id, } - meta := &types.DocumentMetadata{} + meta := &resolver.DocumentMetadata{} versionId := "e6efa34322812bd5ddec7f1aa3389957a2c35d19949913287407cb1068e16eb9" versionTime := "2021-11-03T08:25:13Z" t.Run("ok", func(t *testing.T) { ctx := newMockContext(t) - ctx.didResolver.EXPECT().Resolve(*id, &types.ResolveMetadata{AllowDeactivated: true}).Return(didDoc, meta, nil) + ctx.didResolver.EXPECT().Resolve(*id, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(didDoc, meta, nil) response, err := ctx.client.GetDID(nil, GetDIDRequestObject{Did: id.String()}) @@ -122,7 +124,7 @@ func TestWrapper_GetDID(t *testing.T) { ctx := newMockContext(t) expectedVersionHash, err := hash.ParseHex(versionId) require.NoError(t, err) - ctx.didResolver.EXPECT().Resolve(*id, &types.ResolveMetadata{AllowDeactivated: true, Hash: &expectedVersionHash}).Return(didDoc, meta, nil) + ctx.didResolver.EXPECT().Resolve(*id, &resolver.ResolveMetadata{AllowDeactivated: true, Hash: &expectedVersionHash}).Return(didDoc, meta, nil) response, err := ctx.client.GetDID(nil, GetDIDRequestObject{Did: id.String(), Params: GetDIDParams{VersionId: &versionId}}) @@ -134,7 +136,7 @@ func TestWrapper_GetDID(t *testing.T) { ctx := newMockContext(t) expectedTime, err := time.Parse(time.RFC3339, versionTime) require.NoError(t, err) - ctx.didResolver.EXPECT().Resolve(*id, &types.ResolveMetadata{AllowDeactivated: true, ResolveTime: &expectedTime}).Return(didDoc, meta, nil) + ctx.didResolver.EXPECT().Resolve(*id, &resolver.ResolveMetadata{AllowDeactivated: true, ResolveTime: &expectedTime}).Return(didDoc, meta, nil) response, err := ctx.client.GetDID(nil, GetDIDRequestObject{Did: id.String(), Params: GetDIDParams{VersionTime: &versionTime}}) @@ -189,18 +191,18 @@ func TestWrapper_GetDID(t *testing.T) { t.Run("error - not found", func(t *testing.T) { ctx := newMockContext(t) - ctx.didResolver.EXPECT().Resolve(*id, &types.ResolveMetadata{AllowDeactivated: true}).Return(nil, nil, types.ErrNotFound) + ctx.didResolver.EXPECT().Resolve(*id, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(nil, nil, resolver.ErrNotFound) response, err := ctx.client.GetDID(nil, GetDIDRequestObject{Did: id.String()}) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Equal(t, http.StatusNotFound, ctx.client.ResolveStatusCode(err)) assert.Nil(t, response) }) t.Run("error - other", func(t *testing.T) { ctx := newMockContext(t) - ctx.didResolver.EXPECT().Resolve(*id, &types.ResolveMetadata{AllowDeactivated: true}).Return(nil, nil, errors.New("b00m!")) + ctx.didResolver.EXPECT().Resolve(*id, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(nil, nil, errors.New("b00m!")) response, err := ctx.client.GetDID(nil, GetDIDRequestObject{Did: id.String()}) @@ -214,11 +216,11 @@ func TestWrapper_ConflictedDIDs(t *testing.T) { didDoc := &did.Document{ ID: *id, } - meta := &types.DocumentMetadata{} + meta := &resolver.DocumentMetadata{} t.Run("ok", func(t *testing.T) { ctx := newMockContext(t) - ctx.vdr.EXPECT().ConflictedDocuments().Return([]did.Document{*didDoc}, []types.DocumentMetadata{*meta}, nil) + ctx.vdr.EXPECT().ConflictedDocuments().Return([]did.Document{*didDoc}, []resolver.DocumentMetadata{*meta}, nil) response, err := ctx.client.ConflictedDIDs(nil, ConflictedDIDsRequestObject{}) @@ -268,11 +270,11 @@ func TestWrapper_UpdateDID(t *testing.T) { t.Run("error - not found", func(t *testing.T) { ctx := newMockContext(t) - ctx.vdr.EXPECT().Update(gomock.Any(), *id, gomock.Any()).Return(types.ErrNotFound) + ctx.vdr.EXPECT().Update(gomock.Any(), *id, gomock.Any()).Return(resolver.ErrNotFound) response, err := ctx.client.UpdateDID(nil, UpdateDIDRequestObject{Did: id.String(), Body: &request}) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Equal(t, http.StatusNotFound, ctx.client.ResolveStatusCode(err)) assert.Nil(t, response) }) @@ -289,22 +291,22 @@ func TestWrapper_UpdateDID(t *testing.T) { t.Run("error - document deactivated", func(t *testing.T) { ctx := newMockContext(t) - ctx.vdr.EXPECT().Update(gomock.Any(), *id, gomock.Any()).Return(types.ErrDeactivated) + ctx.vdr.EXPECT().Update(gomock.Any(), *id, gomock.Any()).Return(resolver.ErrDeactivated) response, err := ctx.client.UpdateDID(nil, UpdateDIDRequestObject{Did: id.String(), Body: &request}) - assert.ErrorIs(t, err, types.ErrDeactivated) + assert.ErrorIs(t, err, resolver.ErrDeactivated) assert.Equal(t, http.StatusConflict, ctx.client.ResolveStatusCode(err)) assert.Nil(t, response) }) t.Run("error - did not managed by this node", func(t *testing.T) { ctx := newMockContext(t) - ctx.vdr.EXPECT().Update(gomock.Any(), *id, gomock.Any()).Return(types.ErrDIDNotManagedByThisNode) + ctx.vdr.EXPECT().Update(gomock.Any(), *id, gomock.Any()).Return(resolver.ErrDIDNotManagedByThisNode) response, err := ctx.client.UpdateDID(nil, UpdateDIDRequestObject{Did: id.String(), Body: &request}) - assert.ErrorIs(t, err, types.ErrDIDNotManagedByThisNode) + assert.ErrorIs(t, err, resolver.ErrDIDNotManagedByThisNode) assert.Equal(t, http.StatusForbidden, ctx.client.ResolveStatusCode(err)) assert.Nil(t, response) }) @@ -333,31 +335,31 @@ func TestWrapper_DeactivateDID(t *testing.T) { t.Run("error - not found", func(t *testing.T) { ctx := newMockContext(t) - ctx.docUpdater.EXPECT().Deactivate(ctx.requestCtx, *did123).Return(types.ErrNotFound) + ctx.docUpdater.EXPECT().Deactivate(ctx.requestCtx, *did123).Return(resolver.ErrNotFound) _, err := ctx.client.DeactivateDID(ctx.requestCtx, DeactivateDIDRequestObject{Did: did123.String()}) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Equal(t, http.StatusNotFound, ctx.client.ResolveStatusCode(err)) }) t.Run("error - document already deactivated", func(t *testing.T) { ctx := newMockContext(t) - ctx.docUpdater.EXPECT().Deactivate(ctx.requestCtx, *did123).Return(types.ErrDeactivated) + ctx.docUpdater.EXPECT().Deactivate(ctx.requestCtx, *did123).Return(resolver.ErrDeactivated) _, err := ctx.client.DeactivateDID(ctx.requestCtx, DeactivateDIDRequestObject{Did: did123.String()}) - assert.ErrorIs(t, err, types.ErrDeactivated) + assert.ErrorIs(t, err, resolver.ErrDeactivated) assert.Equal(t, http.StatusConflict, ctx.client.ResolveStatusCode(err)) }) t.Run("error - did not managed by this node", func(t *testing.T) { ctx := newMockContext(t) - ctx.docUpdater.EXPECT().Deactivate(ctx.requestCtx, *did123).Return(types.ErrDIDNotManagedByThisNode) + ctx.docUpdater.EXPECT().Deactivate(ctx.requestCtx, *did123).Return(resolver.ErrDIDNotManagedByThisNode) _, err := ctx.client.DeactivateDID(ctx.requestCtx, DeactivateDIDRequestObject{Did: did123.String()}) - assert.ErrorIs(t, err, types.ErrDIDNotManagedByThisNode) + assert.ErrorIs(t, err, resolver.ErrDIDNotManagedByThisNode) assert.Equal(t, http.StatusForbidden, ctx.client.ResolveStatusCode(err)) }) } @@ -380,7 +382,7 @@ func TestWrapper_AddNewVerificationMethod(t *testing.T) { t.Run("ok - with key usage", func(t *testing.T) { ctx := newMockContext(t) - expectedKeyUsage := didnuts.DefaultCreationOptions().KeyFlags | types.AuthenticationUsage | types.CapabilityDelegationUsage + expectedKeyUsage := didnuts.DefaultCreationOptions().KeyFlags | management.AuthenticationUsage | management.CapabilityDelegationUsage ctx.docUpdater.EXPECT().AddVerificationMethod(ctx.requestCtx, *did123, expectedKeyUsage).Return(newMethod, nil) trueBool := true request := AddNewVerificationMethodJSONRequestBody{ @@ -464,9 +466,9 @@ func Test_ErrorStatusCodes(t *testing.T) { type mockContext struct { ctrl *gomock.Controller - vdr *types.MockVDR - didResolver *types.MockDIDResolver - docUpdater *types.MockDocManipulator + vdr *vdr.MockVDR + didResolver *resolver.MockDIDResolver + docUpdater *management.MockDocManipulator client *Wrapper requestCtx context.Context } @@ -474,10 +476,10 @@ type mockContext struct { func newMockContext(t *testing.T) mockContext { t.Helper() ctrl := gomock.NewController(t) - didResolver := types.NewMockDIDResolver(ctrl) - vdr := types.NewMockVDR(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) + vdr := vdr.NewMockVDR(ctrl) vdr.EXPECT().Resolver().Return(didResolver).AnyTimes() - docManipulator := types.NewMockDocManipulator(ctrl) + docManipulator := management.NewMockDocManipulator(ctrl) client := &Wrapper{VDR: vdr, DocManipulator: docManipulator} requestCtx := audit.TestContext() diff --git a/vdr/api/v1/client_test.go b/vdr/api/v1/client_test.go index b5f52cce42..5d9cc4bf9a 100644 --- a/vdr/api/v1/client_test.go +++ b/vdr/api/v1/client_test.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "github.com/nuts-foundation/nuts-node/core" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "net/http" "net/http/httptest" @@ -32,7 +33,6 @@ import ( "github.com/nuts-foundation/go-did/did" http2 "github.com/nuts-foundation/nuts-node/test/http" "github.com/nuts-foundation/nuts-node/vdr" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "schneider.vip/problem" ) @@ -68,7 +68,7 @@ func TestHttpClient_Get(t *testing.T) { didDoc := did.Document{ ID: vdr.TestDIDA, } - meta := types.DocumentMetadata{} + meta := resolver.DocumentMetadata{} t.Run("ok", func(t *testing.T) { resolutionResult := DIDResolutionResult{ @@ -112,7 +112,7 @@ func TestHTTPClient_ConflictedDIDs(t *testing.T) { didDoc := did.Document{ ID: vdr.TestDIDA, } - meta := types.DocumentMetadata{} + meta := resolver.DocumentMetadata{} t.Run("ok", func(t *testing.T) { resolutionResults := []DIDResolutionResult{{ diff --git a/vdr/api/v1/types.go b/vdr/api/v1/types.go index 5dcb823d28..495533ecb2 100644 --- a/vdr/api/v1/types.go +++ b/vdr/api/v1/types.go @@ -17,14 +17,15 @@ package v1 import ( "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) // DIDDocument is an alias type DIDDocument = did.Document // DIDDocumentMetadata is an alias -type DIDDocumentMetadata = types.DocumentMetadata +type DIDDocumentMetadata = resolver.DocumentMetadata // VerificationMethod is an alias type VerificationMethod = did.VerificationMethod @@ -64,13 +65,13 @@ type VerificationMethodRelationship struct { // ToFlags takes default key flags, and enabled/disables the flags which are set on the VerificationMethodRelationship, // and the result is returned. -func (r VerificationMethodRelationship) ToFlags(defaults types.DIDKeyFlags) types.DIDKeyFlags { +func (r VerificationMethodRelationship) ToFlags(defaults management.DIDKeyFlags) management.DIDKeyFlags { result := defaults - result = withKeyFlag(result, types.AuthenticationUsage, r.Authentication) - result = withKeyFlag(result, types.AssertionMethodUsage, r.AssertionMethod) - result = withKeyFlag(result, types.CapabilityDelegationUsage, r.CapabilityDelegation) - result = withKeyFlag(result, types.CapabilityInvocationUsage, r.CapabilityInvocation) - result = withKeyFlag(result, types.KeyAgreementUsage, r.KeyAgreement) + result = withKeyFlag(result, management.AuthenticationUsage, r.Authentication) + result = withKeyFlag(result, management.AssertionMethodUsage, r.AssertionMethod) + result = withKeyFlag(result, management.CapabilityDelegationUsage, r.CapabilityDelegation) + result = withKeyFlag(result, management.CapabilityInvocationUsage, r.CapabilityInvocation) + result = withKeyFlag(result, management.KeyAgreementUsage, r.KeyAgreement) return result } @@ -78,7 +79,7 @@ func (r VerificationMethodRelationship) ToFlags(defaults types.DIDKeyFlags) type // - bool == nil: do nothing // - bool == true: enable flag // - bool == false: disable flag -func withKeyFlag(current, flag types.DIDKeyFlags, value *bool) types.DIDKeyFlags { +func withKeyFlag(current, flag management.DIDKeyFlags, value *bool) management.DIDKeyFlags { switch { case value == nil: // no setting return current diff --git a/vdr/cmd/cmd.go b/vdr/cmd/cmd.go index a3d1888e37..0b3a3134c7 100644 --- a/vdr/cmd/cmd.go +++ b/vdr/cmd/cmd.go @@ -24,7 +24,8 @@ import ( "errors" "fmt" "github.com/nuts-foundation/nuts-node/vdr/didnuts" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "io" "os" "strings" @@ -32,7 +33,6 @@ import ( "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/core" api "github.com/nuts-foundation/nuts-node/vdr/api/v1" - vdrTypes "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -100,11 +100,11 @@ func createCmd() *cobra.Command { } return fmt.Sprintf(usage, !def, opposite) } - result.Flags().BoolVar(createRequest.AssertionMethod, "assertionMethod", defs.KeyFlags.Is(vdrTypes.AssertionMethodUsage), setUsage(defs.KeyFlags.Is(vdrTypes.AssertionMethodUsage), "Pass '%t' to %s assertionMethod capabilities.")) - result.Flags().BoolVar(createRequest.Authentication, "authentication", defs.KeyFlags.Is(vdrTypes.AuthenticationUsage), setUsage(defs.KeyFlags.Is(vdrTypes.AuthenticationUsage), "Pass '%t' to %s authentication capabilities.")) - result.Flags().BoolVar(createRequest.CapabilityDelegation, "capabilityDelegation", defs.KeyFlags.Is(vdrTypes.CapabilityDelegationUsage), setUsage(defs.KeyFlags.Is(vdrTypes.CapabilityDelegationUsage), "Pass '%t' to %s capabilityDelegation capabilities.")) - result.Flags().BoolVar(createRequest.CapabilityInvocation, "capabilityInvocation", defs.KeyFlags.Is(vdrTypes.CapabilityInvocationUsage), setUsage(defs.KeyFlags.Is(vdrTypes.CapabilityInvocationUsage), "Pass '%t' to %s capabilityInvocation capabilities.")) - result.Flags().BoolVar(createRequest.KeyAgreement, "keyAgreement", defs.KeyFlags.Is(vdrTypes.KeyAgreementUsage), setUsage(defs.KeyFlags.Is(vdrTypes.KeyAgreementUsage), "Pass '%t' to %s keyAgreement capabilities.")) + result.Flags().BoolVar(createRequest.AssertionMethod, "assertionMethod", defs.KeyFlags.Is(management.AssertionMethodUsage), setUsage(defs.KeyFlags.Is(management.AssertionMethodUsage), "Pass '%t' to %s assertionMethod capabilities.")) + result.Flags().BoolVar(createRequest.Authentication, "authentication", defs.KeyFlags.Is(management.AuthenticationUsage), setUsage(defs.KeyFlags.Is(management.AuthenticationUsage), "Pass '%t' to %s authentication capabilities.")) + result.Flags().BoolVar(createRequest.CapabilityDelegation, "capabilityDelegation", defs.KeyFlags.Is(management.CapabilityDelegationUsage), setUsage(defs.KeyFlags.Is(management.CapabilityDelegationUsage), "Pass '%t' to %s capabilityDelegation capabilities.")) + result.Flags().BoolVar(createRequest.CapabilityInvocation, "capabilityInvocation", defs.KeyFlags.Is(management.CapabilityInvocationUsage), setUsage(defs.KeyFlags.Is(management.CapabilityInvocationUsage), "Pass '%t' to %s capabilityInvocation capabilities.")) + result.Flags().BoolVar(createRequest.KeyAgreement, "keyAgreement", defs.KeyFlags.Is(management.KeyAgreementUsage), setUsage(defs.KeyFlags.Is(management.KeyAgreementUsage), "Pass '%t' to %s keyAgreement capabilities.")) result.Flags().BoolVar(createRequest.SelfControl, "selfControl", defs.SelfControl, setUsage(defs.SelfControl, "Pass '%t' to %s DID Document control.")) result.Flags().StringSliceVar(createRequest.Controllers, "controllers", []string{}, "Comma-separated list of DIDs that can control the generated DID Document.") @@ -286,7 +286,7 @@ func addKeyAgreementKeyCmd() *cobra.Command { if err != nil { return fmt.Errorf("invalid key ID '%s': %w", args[0], err) } - targetDID, _ := didservice.GetDIDFromURL(args[0]) // can't fail because we already parsed the key ID + targetDID, _ := resolver.GetDIDFromURL(args[0]) // can't fail because we already parsed the key ID clientConfig := core.NewClientConfigForCommand(cmd) client := httpClient(clientConfig) diff --git a/vdr/did_owner.go b/vdr/did_owner.go index cdee39abd3..469e795c23 100644 --- a/vdr/did_owner.go +++ b/vdr/did_owner.go @@ -23,14 +23,14 @@ import ( "fmt" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "strings" "sync" ) -var _ types.DocumentOwner = (*cachingDocumentOwner)(nil) -var _ types.DocumentOwner = (*privateKeyDocumentOwner)(nil) +var _ management.DocumentOwner = (*cachingDocumentOwner)(nil) +var _ management.DocumentOwner = (*privateKeyDocumentOwner)(nil) // cachingDocumentOwner is a types.DocumentOwner that caches the result, minimizing expensive lookups. // It assumes: @@ -40,17 +40,17 @@ var _ types.DocumentOwner = (*privateKeyDocumentOwner)(nil) // Before calling the more expensive, underlying types.DocumentOwner, it checks whether the DID actually exists. // The ListOwned call is not cached. type cachingDocumentOwner struct { - underlying types.DocumentOwner + underlying management.DocumentOwner ownedDIDs *sync.Map notOwnedDIDs *sync.Map - didResolver types.DIDResolver + didResolver resolver.DIDResolver } func (t *cachingDocumentOwner) ListOwned(ctx context.Context) ([]did.DID, error) { return t.underlying.ListOwned(ctx) } -func newCachingDocumentOwner(underlying types.DocumentOwner, didResolver types.DIDResolver) *cachingDocumentOwner { +func newCachingDocumentOwner(underlying management.DocumentOwner, didResolver resolver.DIDResolver) *cachingDocumentOwner { return &cachingDocumentOwner{ didResolver: didResolver, underlying: underlying, @@ -76,7 +76,7 @@ func (t *cachingDocumentOwner) IsOwner(ctx context.Context, id did.DID) (bool, e // First perform a cheap DID existence check (subsequent checks are more expensive), // without caching it as negative match (would allow unbound number of negative matches). _, _, err := t.didResolver.Resolve(id, nil) - if didservice.IsFunctionalResolveError(err) { + if resolver.IsFunctionalResolveError(err) { return false, nil } else if err != nil { return false, fmt.Errorf("unable to check ownership of DID: %w", err) diff --git a/vdr/did_owner_test.go b/vdr/did_owner_test.go index 7d2b8ef5fc..eedb0da975 100644 --- a/vdr/did_owner_test.go +++ b/vdr/did_owner_test.go @@ -23,7 +23,8 @@ import ( "errors" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -34,9 +35,9 @@ func Test_cachingDocumentOwner_IsOwner(t *testing.T) { id := did.MustParseDID("did:nuts:example.com") t.Run("owned, cached", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + resolver := resolver.NewMockDIDResolver(ctrl) resolver.EXPECT().Resolve(id, gomock.Any()).Return(nil, nil, nil) - underlying := types.NewMockDocumentOwner(ctrl) + underlying := management.NewMockDocumentOwner(ctrl) underlying.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(true, nil) documentOwner := newCachingDocumentOwner(underlying, resolver) @@ -50,9 +51,9 @@ func Test_cachingDocumentOwner_IsOwner(t *testing.T) { }) t.Run("not owned, cached", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + resolver := resolver.NewMockDIDResolver(ctrl) resolver.EXPECT().Resolve(id, gomock.Any()).Return(nil, nil, nil) - underlying := types.NewMockDocumentOwner(ctrl) + underlying := management.NewMockDocumentOwner(ctrl) underlying.EXPECT().IsOwner(gomock.Any(), gomock.Any()).Return(false, nil) documentOwner := newCachingDocumentOwner(underlying, resolver) @@ -66,11 +67,11 @@ func Test_cachingDocumentOwner_IsOwner(t *testing.T) { }) t.Run("DID does not exist", func(t *testing.T) { ctrl := gomock.NewController(t) - underlying := types.NewMockDocumentOwner(ctrl) - resolver := types.NewMockDIDResolver(ctrl) - resolver.EXPECT().Resolve(id, gomock.Any()).Return(nil, nil, types.ErrNotFound) + underlying := management.NewMockDocumentOwner(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) + didResolver.EXPECT().Resolve(id, gomock.Any()).Return(nil, nil, resolver.ErrNotFound) - documentOwner := newCachingDocumentOwner(underlying, resolver) + documentOwner := newCachingDocumentOwner(underlying, didResolver) result, err := documentOwner.IsOwner(context.Background(), id) @@ -80,11 +81,11 @@ func Test_cachingDocumentOwner_IsOwner(t *testing.T) { t.Run("DID is deactivated", func(t *testing.T) { ctrl := gomock.NewController(t) - underlying := types.NewMockDocumentOwner(ctrl) - resolver := types.NewMockDIDResolver(ctrl) - resolver.EXPECT().Resolve(id, gomock.Any()).Return(nil, nil, types.ErrDeactivated) + underlying := management.NewMockDocumentOwner(ctrl) + didResolver := resolver.NewMockDIDResolver(ctrl) + didResolver.EXPECT().Resolve(id, gomock.Any()).Return(nil, nil, resolver.ErrDeactivated) - documentOwner := newCachingDocumentOwner(underlying, resolver) + documentOwner := newCachingDocumentOwner(underlying, didResolver) result, err := documentOwner.IsOwner(context.Background(), id) @@ -93,8 +94,8 @@ func Test_cachingDocumentOwner_IsOwner(t *testing.T) { }) t.Run("error - DID resolve fails", func(t *testing.T) { ctrl := gomock.NewController(t) - underlying := types.NewMockDocumentOwner(ctrl) - resolver := types.NewMockDIDResolver(ctrl) + underlying := management.NewMockDocumentOwner(ctrl) + resolver := resolver.NewMockDIDResolver(ctrl) resolver.EXPECT().Resolve(id, gomock.Any()).Return(nil, nil, errors.New("b00m")) documentOwner := newCachingDocumentOwner(underlying, resolver) diff --git a/vdr/didjwk/resolver.go b/vdr/didjwk/resolver.go index d4de1e8c8f..489580415f 100644 --- a/vdr/didjwk/resolver.go +++ b/vdr/didjwk/resolver.go @@ -21,10 +21,9 @@ package didjwk import ( "encoding/base64" "fmt" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "reflect" - "github.com/nuts-foundation/nuts-node/vdr/types" - godid "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" @@ -34,7 +33,7 @@ import ( // MethodName is the name of this DID method. const MethodName = "jwk" -var _ types.DIDResolver = (*Resolver)(nil) +var _ resolver.DIDResolver = (*Resolver)(nil) // Resolver is a DID resolver for the did:jwk method. type Resolver struct{} @@ -45,7 +44,7 @@ func NewResolver() *Resolver { } // Resolve implements the DIDResolver interface. -func (w Resolver) Resolve(id did.DID, _ *types.ResolveMetadata) (*did.Document, *types.DocumentMetadata, error) { +func (w Resolver) Resolve(id did.DID, _ *resolver.ResolveMetadata) (*did.Document, *resolver.DocumentMetadata, error) { // Ensure this is a did:jwk if id.Method != "jwk" { return nil, nil, fmt.Errorf("unsupported DID method: %s", id.Method) @@ -100,7 +99,7 @@ func (w Resolver) Resolve(id did.DID, _ *types.ResolveMetadata) (*did.Document, document.AddAssertionMethod(verificationMethod) // Return the newly created document - return &document, &types.DocumentMetadata{}, nil + return &document, &resolver.DocumentMetadata{}, nil } // rawPrivateKeyOf returns the private key component of a jwk.Key, or nil if one is not available (e.g. public key only JWK's). An error is returned if a public key is not contained in the JWK. This is more tricky than it might seem at first diff --git a/vdr/didnuts/ambassador.go b/vdr/didnuts/ambassador.go index 980bd1abb2..eeef823d99 100644 --- a/vdr/didnuts/ambassador.go +++ b/vdr/didnuts/ambassador.go @@ -30,6 +30,7 @@ import ( "github.com/nuts-foundation/go-stoabs" "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "sort" "github.com/lestrrat-go/jwx/jwk" @@ -40,7 +41,6 @@ import ( "github.com/nuts-foundation/nuts-node/network" "github.com/nuts-foundation/nuts-node/network/dag" "github.com/nuts-foundation/nuts-node/vdr/log" - "github.com/nuts-foundation/nuts-node/vdr/types" ) // DIDDocumentType contains network transaction mime-type to identify a DID Document in the network. @@ -61,8 +61,8 @@ type Ambassador interface { type ambassador struct { networkClient network.Transactions didStore didstore.Store - keyResolver types.NutsKeyResolver - didResolver types.DIDResolver + keyResolver resolver.NutsKeyResolver + didResolver resolver.DIDResolver eventManager events.Event } @@ -248,8 +248,8 @@ func (n *ambassador) handleUpdateDIDDocument(transaction dag.Transaction, propos var currentDIDDocument *did.Document var err error for _, ref := range transaction.Previous() { - currentDIDDocument, _, err = n.didStore.Resolve(proposedDIDDocument.ID, &types.ResolveMetadata{AllowDeactivated: true, SourceTransaction: &ref}) - if err != nil && !errors.Is(err, types.ErrNotFound) { + currentDIDDocument, _, err = n.didStore.Resolve(proposedDIDDocument.ID, &resolver.ResolveMetadata{AllowDeactivated: true, SourceTransaction: &ref}) + if err != nil && !errors.Is(err, resolver.ErrNotFound) { return fmt.Errorf("unable to update DID document: %w", err) } if currentDIDDocument != nil { @@ -258,7 +258,7 @@ func (n *ambassador) handleUpdateDIDDocument(transaction dag.Transaction, propos } // fallback if currentDIDDocument == nil { - currentDIDDocument, _, err = n.didStore.Resolve(proposedDIDDocument.ID, &types.ResolveMetadata{AllowDeactivated: true}) + currentDIDDocument, _, err = n.didStore.Resolve(proposedDIDDocument.ID, &resolver.ResolveMetadata{AllowDeactivated: true}) if err != nil { return fmt.Errorf("unable to update DID document: %w", err) } @@ -327,9 +327,9 @@ func (n *ambassador) resolveControllers(document did.Document, transaction dag.T signingTime := transaction.SigningTime() for _, prev := range transaction.Previous() { - didControllers, err := ResolveControllers(n.didResolver, document, &types.ResolveMetadata{SourceTransaction: &prev}) + didControllers, err := ResolveControllers(n.didResolver, document, &resolver.ResolveMetadata{SourceTransaction: &prev}) if err != nil { - if errors.Is(err, types.ErrNotFound) || errors.Is(err, types.ErrNoActiveController) { + if errors.Is(err, resolver.ErrNotFound) || errors.Is(err, resolver.ErrNoActiveController) { continue } return nil, err @@ -339,7 +339,7 @@ func (n *ambassador) resolveControllers(document did.Document, transaction dag.T // legacy resolve if len(controllers) == 0 { - didControllers, err := ResolveControllers(n.didResolver, document, &types.ResolveMetadata{ResolveTime: &signingTime}) + didControllers, err := ResolveControllers(n.didResolver, document, &resolver.ResolveMetadata{ResolveTime: &signingTime}) if err != nil { return nil, err } diff --git a/vdr/didnuts/ambassador_test.go b/vdr/didnuts/ambassador_test.go index 281a034ee7..02aff6b86e 100644 --- a/vdr/didnuts/ambassador_test.go +++ b/vdr/didnuts/ambassador_test.go @@ -29,6 +29,8 @@ import ( "fmt" "github.com/nuts-foundation/nuts-node/audit" "github.com/nuts-foundation/nuts-node/network" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "testing" "time" @@ -41,7 +43,6 @@ import ( "github.com/nuts-foundation/nuts-node/events" "github.com/nuts-foundation/nuts-node/network/dag" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -366,7 +367,7 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { currentPayloadHash := hash.SHA256Sum([]byte("currentPayloadHash")) // This is the metadata of the current version of the document which will be returned by the resolver - currentMetadata := &types.DocumentMetadata{ + currentMetadata := &resolver.DocumentMetadata{ Created: createdAt, Updated: nil, Hash: currentPayloadHash, @@ -375,7 +376,7 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { var pKey crypto2.PublicKey _ = signingKey.Raw(&pKey) - ctx.didStore.EXPECT().Resolve(didDocument.ID, &types.ResolveMetadata{AllowDeactivated: true}).Return(&storedDocument, currentMetadata, nil) + ctx.didStore.EXPECT().Resolve(didDocument.ID, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(&storedDocument, currentMetadata, nil) ctx.keyResolver.EXPECT().ResolvePublicKey(storedDocument.CapabilityInvocation[0].ID.String(), gomock.Any()).Return(pKey, nil) ctx.didStore.EXPECT().Add(deactivatedDocument, toStoreTX(tx)) @@ -389,7 +390,7 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { ctx := newMockContext(t) controllerDoc, signingKey, _ := newDidDoc() - didDocument, _, _ := newDidDocWithOptions(types.DIDCreationOptions{Controllers: []did.DID{controllerDoc.ID}}) + didDocument, _, _ := newDidDocWithOptions(management.DIDCreationOptions{Controllers: []did.DID{controllerDoc.ID}}) didDocPayload, _ := json.Marshal(didDocument) payloadHash := hash.SHA256Sum(didDocPayload) @@ -402,7 +403,7 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { _ = json.Unmarshal(didDocPayload, &expectedDocument) // This is the metadata of the current version of the document which will be returned by the resolver - currentMetadata := &types.DocumentMetadata{ + currentMetadata := &resolver.DocumentMetadata{ Created: createdAt, Updated: nil, Hash: hash.SHA256Sum([]byte("currentPayloadHash")), @@ -411,7 +412,7 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { var pKey crypto2.PublicKey _ = signingKey.Raw(&pKey) - ctx.didStore.EXPECT().Resolve(didDocument.ID, &types.ResolveMetadata{AllowDeactivated: true}).Return(&expectedDocument, currentMetadata, nil) + ctx.didStore.EXPECT().Resolve(didDocument.ID, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(&expectedDocument, currentMetadata, nil) ctx.didResolver.EXPECT().Resolve(controllerDoc.ID, gomock.Any()).Return(&controllerDoc, currentMetadata, nil) ctx.keyResolver.EXPECT().ResolvePublicKey(controllerDoc.CapabilityInvocation[0].ID.String(), gomock.Any()).Return(pKey, nil) ctx.didStore.EXPECT().Add(expectedDocument, toStoreTX(tx)) @@ -438,7 +439,7 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { tx.prevs = []hash.SHA256Hash{prev} // This is the metadata of the current version of the document which will be returned by the resolver - currentMetadata := &types.DocumentMetadata{ + currentMetadata := &resolver.DocumentMetadata{ Created: createdAt, Updated: nil, Hash: hash.SHA256Sum([]byte("currentPayloadHash")), @@ -447,7 +448,7 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { var pKey crypto2.PublicKey _ = signingKey.Raw(&pKey) - ctx.didStore.EXPECT().Resolve(currentDoc.ID, &types.ResolveMetadata{AllowDeactivated: true, SourceTransaction: &prev}).Return(¤tDoc, currentMetadata, nil) + ctx.didStore.EXPECT().Resolve(currentDoc.ID, &resolver.ResolveMetadata{AllowDeactivated: true, SourceTransaction: &prev}).Return(¤tDoc, currentMetadata, nil) ctx.keyResolver.EXPECT().ResolvePublicKey(currentDoc.CapabilityInvocation[0].ID.String(), gomock.Any()).Return(pKey, nil) ctx.didStore.EXPECT().Add(newDoc, toStoreTX(tx)) @@ -473,7 +474,7 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { tx.prevs = []hash.SHA256Hash{hash.RandomHash(), prev} // This is the metadata of the current version of the document which will be returned by the resolver - currentMetadata := &types.DocumentMetadata{ + currentMetadata := &resolver.DocumentMetadata{ Created: createdAt, Updated: nil, Hash: hash.SHA256Sum([]byte("currentPayloadHash")), @@ -483,8 +484,8 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { _ = signingKey.Raw(&pKey) gomock.InOrder( - ctx.didStore.EXPECT().Resolve(currentDoc.ID, gomock.Any()).Return(nil, nil, types.ErrNotFound), - ctx.didStore.EXPECT().Resolve(currentDoc.ID, &types.ResolveMetadata{AllowDeactivated: true, SourceTransaction: &prev}).Return(¤tDoc, currentMetadata, nil), + ctx.didStore.EXPECT().Resolve(currentDoc.ID, gomock.Any()).Return(nil, nil, resolver.ErrNotFound), + ctx.didStore.EXPECT().Resolve(currentDoc.ID, &resolver.ResolveMetadata{AllowDeactivated: true, SourceTransaction: &prev}).Return(¤tDoc, currentMetadata, nil), ) ctx.keyResolver.EXPECT().ResolvePublicKey(currentDoc.CapabilityInvocation[0].ID.String(), gomock.Any()).Return(pKey, nil) ctx.didStore.EXPECT().Add(newDoc, toStoreTX(tx)) @@ -533,13 +534,13 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { currentPayloadHash := hash.SHA256Sum([]byte("currentPayloadHash")) // This is the metadata of the current version of the document which will be returned by the resolver - currentMetadata := &types.DocumentMetadata{ + currentMetadata := &resolver.DocumentMetadata{ Created: createdAt, Updated: nil, Hash: currentPayloadHash, } - ctx.didStore.EXPECT().Resolve(didDocument.ID, &types.ResolveMetadata{AllowDeactivated: true}).Return(&expectedDocument, currentMetadata, nil) + ctx.didStore.EXPECT().Resolve(didDocument.ID, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(&expectedDocument, currentMetadata, nil) ctx.didResolver.EXPECT().Resolve(didDocumentController.ID, gomock.Any()).Return(&didDocumentController, currentMetadata, nil) ctx.keyResolver.EXPECT().ResolvePublicKey(didDocumentController.CapabilityInvocation[0].ID.String(), gomock.Any()).Return(pKey, nil) ctx.didStore.EXPECT().Add(expectedDocument, toStoreTX(tx)) @@ -594,13 +595,13 @@ func TestAmbassador_handleUpdateDIDDocument(t *testing.T) { currentPayloadHash := hash.SHA256Sum([]byte("currentPayloadHash")) // This is the metadata of the current version of the document which will be returned by the resolver - currentMetadata := &types.DocumentMetadata{ + currentMetadata := &resolver.DocumentMetadata{ Created: createdAt, Updated: nil, Hash: currentPayloadHash, } - ctx.didStore.EXPECT().Resolve(didDocument.ID, &types.ResolveMetadata{AllowDeactivated: true}).Return(&expectedDocument, currentMetadata, nil) + ctx.didStore.EXPECT().Resolve(didDocument.ID, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(&expectedDocument, currentMetadata, nil) ctx.didResolver.EXPECT().Resolve(didDocumentController.ID, gomock.Any()).Return(&didDocumentController, currentMetadata, nil) ctx.keyResolver.EXPECT().ResolvePublicKey(keyID, gomock.Any()).Return(pKey, nil) @@ -623,7 +624,7 @@ func Test_handleUpdateDIDDocument(t *testing.T) { ctrl := gomock.NewController(t) didStoreMock := didstore.NewMockStore(ctrl) - keyStoreMock := types.NewMockNutsKeyResolver(ctrl) + keyStoreMock := resolver.NewMockNutsKeyResolver(ctrl) didResolver := &Resolver{Store: didStoreMock} am := ambassador{ @@ -637,7 +638,7 @@ func Test_handleUpdateDIDDocument(t *testing.T) { didDocument.Controller = []did.DID{didDocumentController.ID} tx := testTransaction{signingTime: time.Now()} - didStoreMock.EXPECT().Resolve(didDocument.ID, &types.ResolveMetadata{AllowDeactivated: true}).Return(&didDocument, &types.DocumentMetadata{}, nil) + didStoreMock.EXPECT().Resolve(didDocument.ID, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(&didDocument, &resolver.DocumentMetadata{}, nil) didStoreMock.EXPECT().Resolve(didDocumentController.ID, gomock.Any()).Return(nil, nil, errors.New("failed")) err := am.handleUpdateDIDDocument(&tx, didDocument) @@ -795,7 +796,7 @@ func Test_uniqueTransactions(t *testing.T) { }) } -func newDidDocWithOptions(opts types.DIDCreationOptions) (did.Document, jwk.Key, error) { +func newDidDocWithOptions(opts management.DIDCreationOptions) (did.Document, jwk.Key, error) { kc := &mockKeyCreator{} docCreator := Creator{KeyStore: kc} didDocument, key, err := docCreator.Create(audit.TestContext(), opts) @@ -826,8 +827,8 @@ func newDidDoc() (did.Document, jwk.Key, error) { type mockContext struct { ctrl *gomock.Controller didStore *didstore.MockStore - keyResolver *types.MockNutsKeyResolver - didResolver *types.MockDIDResolver + keyResolver *resolver.MockNutsKeyResolver + didResolver *resolver.MockDIDResolver eventManager *events.MockEvent network *network.MockTransactions ambassador ambassador @@ -836,8 +837,8 @@ type mockContext struct { func newMockContext(t *testing.T) mockContext { ctrl := gomock.NewController(t) storeMock := didstore.NewMockStore(ctrl) - keyResolverMock := types.NewMockNutsKeyResolver(ctrl) - resolverMock := types.NewMockDIDResolver(ctrl) + keyResolverMock := resolver.NewMockNutsKeyResolver(ctrl) + resolverMock := resolver.NewMockDIDResolver(ctrl) eventManager := events.NewMockEvent(ctrl) networkMock := network.NewMockTransactions(ctrl) am := ambassador{ diff --git a/vdr/didnuts/creator.go b/vdr/didnuts/creator.go index 03ae71f4ab..e51027b49b 100644 --- a/vdr/didnuts/creator.go +++ b/vdr/didnuts/creator.go @@ -22,12 +22,11 @@ import ( "crypto" "errors" "fmt" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ssi "github.com/nuts-foundation/go-did" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" - "github.com/lestrrat-go/jwx/jwk" "github.com/nuts-foundation/go-did/did" @@ -67,10 +66,10 @@ type Creator struct { } // DefaultCreationOptions returns the default DIDCreationOptions when creating DID Documents. -func DefaultCreationOptions() vdr.DIDCreationOptions { - return vdr.DIDCreationOptions{ +func DefaultCreationOptions() management.DIDCreationOptions { + return management.DIDCreationOptions{ Controllers: []did.DID{}, - KeyFlags: vdr.AssertionMethodUsage | vdr.CapabilityInvocationUsage | vdr.KeyAgreementUsage, + KeyFlags: management.AssertionMethodUsage | management.CapabilityInvocationUsage | management.KeyAgreementUsage, SelfControl: true, } } @@ -127,11 +126,11 @@ var ErrInvalidOptions = errors.New("create request has invalid combination of op // Create creates a Nuts DID Document with a valid DID id based on a freshly generated keypair. // The key is added to the verificationMethod list and referred to from the Authentication list -func (n Creator) Create(ctx context.Context, options vdr.DIDCreationOptions) (*did.Document, nutsCrypto.Key, error) { +func (n Creator) Create(ctx context.Context, options management.DIDCreationOptions) (*did.Document, nutsCrypto.Key, error) { var key nutsCrypto.Key var err error - if options.SelfControl && !options.KeyFlags.Is(vdr.CapabilityInvocationUsage) { + if options.SelfControl && !options.KeyFlags.Is(management.CapabilityInvocationUsage) { return nil, nil, ErrInvalidOptions } @@ -153,7 +152,7 @@ func (n Creator) Create(ctx context.Context, options vdr.DIDCreationOptions) (*d } // Create the bare document. The Document DID will be the keyIDStr without the fragment. - didID, _ := didservice.GetDIDFromURL(key.KID()) + didID, _ := resolver.GetDIDFromURL(key.KID()) doc := CreateDocument() doc.ID = didID doc.Controller = options.Controllers @@ -192,20 +191,20 @@ func (n Creator) Create(ctx context.Context, options vdr.DIDCreationOptions) (*d } // applyKeyUsage checks intendedKeyUsage and adds the given verificationMethod to every relationship specified as key usage. -func applyKeyUsage(document *did.Document, keyToAdd *did.VerificationMethod, intendedKeyUsage vdr.DIDKeyFlags) { - if intendedKeyUsage.Is(vdr.CapabilityDelegationUsage) { +func applyKeyUsage(document *did.Document, keyToAdd *did.VerificationMethod, intendedKeyUsage management.DIDKeyFlags) { + if intendedKeyUsage.Is(management.CapabilityDelegationUsage) { document.AddCapabilityDelegation(keyToAdd) } - if intendedKeyUsage.Is(vdr.CapabilityInvocationUsage) { + if intendedKeyUsage.Is(management.CapabilityInvocationUsage) { document.AddCapabilityInvocation(keyToAdd) } - if intendedKeyUsage.Is(vdr.AuthenticationUsage) { + if intendedKeyUsage.Is(management.AuthenticationUsage) { document.AddAuthenticationMethod(keyToAdd) } - if intendedKeyUsage.Is(vdr.AssertionMethodUsage) { + if intendedKeyUsage.Is(management.AssertionMethodUsage) { document.AddAssertionMethod(keyToAdd) } - if intendedKeyUsage.Is(vdr.KeyAgreementUsage) { + if intendedKeyUsage.Is(management.KeyAgreementUsage) { document.AddKeyAgreement(keyToAdd) } } diff --git a/vdr/didnuts/creator_test.go b/vdr/didnuts/creator_test.go index d91187653a..2d06676ec3 100644 --- a/vdr/didnuts/creator_test.go +++ b/vdr/didnuts/creator_test.go @@ -24,6 +24,7 @@ import ( "crypto/elliptic" "crypto/rand" "errors" + "github.com/nuts-foundation/nuts-node/vdr/management" "testing" "github.com/lestrrat-go/jwx/jwk" @@ -33,7 +34,6 @@ import ( "go.uber.org/mock/gomock" nutsCrypto "github.com/nuts-foundation/nuts-node/crypto" - "github.com/nuts-foundation/nuts-node/vdr/types" ) var jwkString = `{"crv":"P-256","kid":"did:nuts:3gU9z3j7j4VCboc3qq3Vc5mVVGDNGjfg32xokeX8c8Zn#J9O6wvqtYOVwjc8JtZ4aodRdbPv_IKAjLkEq9uHlDdE","kty":"EC","x":"Qn6xbZtOYFoLO2qMEAczcau9uGGWwa1bT+7JmAVLtg4=","y":"d20dD0qlT+d1djVpAfrfsAfKOUxKwKkn1zqFSIuJ398="},"type":"JsonWebKey2020"}` @@ -42,11 +42,11 @@ func TestDefaultCreationOptions(t *testing.T) { ops := DefaultCreationOptions() usage := ops.KeyFlags - assert.True(t, usage.Is(types.AssertionMethodUsage)) - assert.False(t, usage.Is(types.AuthenticationUsage)) - assert.False(t, usage.Is(types.CapabilityDelegationUsage)) - assert.True(t, usage.Is(types.CapabilityInvocationUsage)) - assert.True(t, usage.Is(types.KeyAgreementUsage)) + assert.True(t, usage.Is(management.AssertionMethodUsage)) + assert.False(t, usage.Is(management.AuthenticationUsage)) + assert.False(t, usage.Is(management.CapabilityDelegationUsage)) + assert.True(t, usage.Is(management.CapabilityInvocationUsage)) + assert.True(t, usage.Is(management.KeyAgreementUsage)) assert.True(t, ops.SelfControl) assert.Empty(t, ops.Controllers) } @@ -72,12 +72,12 @@ func TestCreator_Create(t *testing.T) { }) t.Run("all keys", func(t *testing.T) { - ops := types.DIDCreationOptions{ - KeyFlags: types.AssertionMethodUsage | - types.AuthenticationUsage | - types.CapabilityDelegationUsage | - types.CapabilityInvocationUsage | - types.KeyAgreementUsage, + ops := management.DIDCreationOptions{ + KeyFlags: management.AssertionMethodUsage | + management.AuthenticationUsage | + management.CapabilityDelegationUsage | + management.CapabilityInvocationUsage | + management.KeyAgreementUsage, SelfControl: true, } doc, _, err := creator.Create(nil, ops) @@ -93,8 +93,8 @@ func TestCreator_Create(t *testing.T) { t.Run("extra controller", func(t *testing.T) { c, _ := did.ParseDID("did:nuts:controller") - ops := types.DIDCreationOptions{ - KeyFlags: types.AssertionMethodUsage | types.CapabilityInvocationUsage, + ops := management.DIDCreationOptions{ + KeyFlags: management.AssertionMethodUsage | management.CapabilityInvocationUsage, SelfControl: true, Controllers: []did.DID{*c}, } @@ -121,8 +121,8 @@ func TestCreator_Create(t *testing.T) { }, nil }) - ops := types.DIDCreationOptions{ - KeyFlags: types.AssertionMethodUsage, + ops := management.DIDCreationOptions{ + KeyFlags: management.AssertionMethodUsage, SelfControl: false, } doc, docCreationKey, err := creator.Create(nil, ops) @@ -146,7 +146,7 @@ func TestCreator_Create(t *testing.T) { }) t.Run("error - invalid combination", func(t *testing.T) { - ops := types.DIDCreationOptions{ + ops := management.DIDCreationOptions{ // CapabilityInvocation is not enabled, required when SelfControl = true SelfControl: true, } @@ -172,8 +172,8 @@ func TestCreator_Create(t *testing.T) { ctrl := gomock.NewController(t) mockKeyStore := nutsCrypto.NewMockKeyStore(ctrl) creator := Creator{KeyStore: mockKeyStore} - ops := types.DIDCreationOptions{ - KeyFlags: types.AssertionMethodUsage, + ops := management.DIDCreationOptions{ + KeyFlags: management.AssertionMethodUsage, SelfControl: false, } mockKeyStore.EXPECT().New(gomock.Any(), gomock.Any()).Return(nil, errors.New("b00m!")) diff --git a/vdr/didnuts/didstore/finder.go b/vdr/didnuts/didstore/finder.go index 09edb0c2d1..e6bc3c9c6d 100644 --- a/vdr/didnuts/didstore/finder.go +++ b/vdr/didnuts/didstore/finder.go @@ -19,7 +19,8 @@ package didstore import ( "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) // Finder is a helper that implements the DocFinder interface @@ -27,10 +28,10 @@ type Finder struct { Store Store } -func (f Finder) Find(predicate ...types.Predicate) ([]did.Document, error) { +func (f Finder) Find(predicate ...management.Predicate) ([]did.Document, error) { matches := make([]did.Document, 0) - err := f.Store.Iterate(func(doc did.Document, metadata types.DocumentMetadata) error { + err := f.Store.Iterate(func(doc did.Document, metadata resolver.DocumentMetadata) error { for _, p := range predicate { if !p.Match(doc, metadata) { return nil diff --git a/vdr/didnuts/didstore/finder_test.go b/vdr/didnuts/didstore/finder_test.go index 75b7ad8204..ef97bbd188 100644 --- a/vdr/didnuts/didstore/finder_test.go +++ b/vdr/didnuts/didstore/finder_test.go @@ -19,12 +19,12 @@ package didstore import ( "errors" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/require" "testing" "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -35,11 +35,11 @@ func TestFinder_Find(t *testing.T) { didStore := NewMockStore(ctrl) finder := Finder{Store: didStore} didStore.EXPECT().Iterate(gomock.Any()).Do(func(arg interface{}) { - f := arg.(types.DocIterator) - f(did.Document{}, types.DocumentMetadata{}) + f := arg.(management.DocIterator) + f(did.Document{}, resolver.DocumentMetadata{}) }) - docs, err := finder.Find(didservice.IsActive()) + docs, err := finder.Find(management.IsActive()) require.NoError(t, err) assert.Len(t, docs, 1) @@ -51,7 +51,7 @@ func TestFinder_Find(t *testing.T) { finder := Finder{Store: didStore} didStore.EXPECT().Iterate(gomock.Any()).Return(errors.New("b00m!")) - _, err := finder.Find(didservice.IsActive()) + _, err := finder.Find(management.IsActive()) assert.Error(t, err) }) diff --git a/vdr/didnuts/didstore/integration_test.go b/vdr/didnuts/didstore/integration_test.go index b295379a79..488c5e4e45 100644 --- a/vdr/didnuts/didstore/integration_test.go +++ b/vdr/didnuts/didstore/integration_test.go @@ -21,12 +21,12 @@ package didstore import ( "encoding/json" ssi "github.com/nuts-foundation/go-did" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "testing" "time" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -145,7 +145,7 @@ func TestStore_deactivated(t *testing.T) { add(t, store, create, tx) t.Run("meta shows deactivated", func(t *testing.T) { - doc, meta, err := store.Resolve(testDID, &types.ResolveMetadata{AllowDeactivated: true}) + doc, meta, err := store.Resolve(testDID, &resolver.ResolveMetadata{AllowDeactivated: true}) require.NoError(t, err) require.NotNil(t, doc) @@ -167,7 +167,7 @@ func TestStore_conflicted(t *testing.T) { require.NoError(t, err) assert.Equal(t, uint(0), count) - err = store.Conflicted(func(doc did.Document, metadata types.DocumentMetadata) error { + err = store.Conflicted(func(doc did.Document, metadata resolver.DocumentMetadata) error { t.Fail() return nil }) @@ -185,7 +185,7 @@ func TestStore_conflicted(t *testing.T) { assert.Equal(t, uint(1), count) assert.Len(t, store.conflictedDocuments, 1) - err = store.Conflicted(func(doc did.Document, metadata types.DocumentMetadata) error { + err = store.Conflicted(func(doc did.Document, metadata resolver.DocumentMetadata) error { assert.NotEqual(t, doc1, doc) assert.NotEqual(t, doc2, doc) assert.True(t, metadata.IsConflicted()) @@ -207,7 +207,7 @@ func TestStore_conflicted(t *testing.T) { assert.Equal(t, uint(0), count) assert.Len(t, store.conflictedDocuments, 0) - err = store.Conflicted(func(doc did.Document, metadata types.DocumentMetadata) error { + err = store.Conflicted(func(doc did.Document, metadata resolver.DocumentMetadata) error { t.Fail() return nil }) @@ -222,7 +222,7 @@ func TestStore_duplicate(t *testing.T) { add(t, store, create, tx) add(t, store, create, tx) - doc, meta, err := store.Resolve(testDID, &types.ResolveMetadata{AllowDeactivated: true}) + doc, meta, err := store.Resolve(testDID, &resolver.ResolveMetadata{AllowDeactivated: true}) require.NoError(t, err) require.NotNil(t, doc) diff --git a/vdr/didnuts/didstore/interface.go b/vdr/didnuts/didstore/interface.go index 962c1f7565..6b46c6a277 100644 --- a/vdr/didnuts/didstore/interface.go +++ b/vdr/didnuts/didstore/interface.go @@ -20,7 +20,8 @@ package didstore import ( "github.com/nuts-foundation/go-did/did" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) // Store is the interface that groups all low level VDR DID storage operations. @@ -28,20 +29,20 @@ type Store interface { // Add a DID Document to the store. The store will place it on the timeline and reprocess other versions if needed Add(didDocument did.Document, transaction Transaction) error // Conflicted iterates over all conflicted documents - Conflicted(fn vdr.DocIterator) error + Conflicted(fn management.DocIterator) error // ConflictedCount returns the number of conflicted DID Documents ConflictedCount() (uint, error) // DocumentCount returns the number of DID Documents DocumentCount() (uint, error) // Iterate loops over all the latest versions of the stored DID Documents and applies fn. // Calling any of the Store's functions from the given fn might cause a deadlock. - Iterate(fn vdr.DocIterator) error + Iterate(fn management.DocIterator) error // Resolve returns the DID Document for the provided DID. // If metadata is not provided the latest version is returned. // If metadata is provided then the result is filtered or scoped on that metadata. // It returns vdr.ErrNotFound if there are no corresponding DID documents or when the DID Documents are disjoint with the provided ResolveMetadata. // It returns vdr.ErrDeactivated if no metadata is given and the latest version of the DID Document is deactivated. - Resolve(id did.DID, metadata *vdr.ResolveMetadata) (*did.Document, *vdr.DocumentMetadata, error) + Resolve(id did.DID, metadata *resolver.ResolveMetadata) (*did.Document, *resolver.DocumentMetadata, error) } // Transaction is an alias to the didstore.event. Internally to the didstore it's an event based on a transaction. diff --git a/vdr/didnuts/didstore/metadata.go b/vdr/didnuts/didstore/metadata.go index 6c44b0e5f7..c22a0414ae 100644 --- a/vdr/didnuts/didstore/metadata.go +++ b/vdr/didnuts/didstore/metadata.go @@ -19,10 +19,10 @@ package didstore import ( + "github.com/nuts-foundation/nuts-node/vdr/resolver" "time" "github.com/nuts-foundation/nuts-node/crypto/hash" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" ) // documentMetadata is like VDR documentMetadata but usable for storage @@ -43,8 +43,8 @@ type documentMetadata struct { Deactivated bool `json:"deactivated"` } -func (md documentMetadata) asVDRMetadata() vdr.DocumentMetadata { - result := vdr.DocumentMetadata{ +func (md documentMetadata) asVDRMetadata() resolver.DocumentMetadata { + result := resolver.DocumentMetadata{ Created: md.Created, Hash: md.Hash, PreviousHash: md.PreviousHash, diff --git a/vdr/didnuts/didstore/mock.go b/vdr/didnuts/didstore/mock.go index 7b40c49973..fda940544a 100644 --- a/vdr/didnuts/didstore/mock.go +++ b/vdr/didnuts/didstore/mock.go @@ -12,7 +12,8 @@ import ( reflect "reflect" did "github.com/nuts-foundation/go-did/did" - types "github.com/nuts-foundation/nuts-node/vdr/types" + management "github.com/nuts-foundation/nuts-node/vdr/management" + resolver "github.com/nuts-foundation/nuts-node/vdr/resolver" gomock "go.uber.org/mock/gomock" ) @@ -54,7 +55,7 @@ func (mr *MockStoreMockRecorder) Add(didDocument, transaction any) *gomock.Call } // Conflicted mocks base method. -func (m *MockStore) Conflicted(fn types.DocIterator) error { +func (m *MockStore) Conflicted(fn management.DocIterator) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Conflicted", fn) ret0, _ := ret[0].(error) @@ -98,7 +99,7 @@ func (mr *MockStoreMockRecorder) DocumentCount() *gomock.Call { } // Iterate mocks base method. -func (m *MockStore) Iterate(fn types.DocIterator) error { +func (m *MockStore) Iterate(fn management.DocIterator) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Iterate", fn) ret0, _ := ret[0].(error) @@ -112,11 +113,11 @@ func (mr *MockStoreMockRecorder) Iterate(fn any) *gomock.Call { } // Resolve mocks base method. -func (m *MockStore) Resolve(id did.DID, metadata *types.ResolveMetadata) (*did.Document, *types.DocumentMetadata, error) { +func (m *MockStore) Resolve(id did.DID, metadata *resolver.ResolveMetadata) (*did.Document, *resolver.DocumentMetadata, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Resolve", id, metadata) ret0, _ := ret[0].(*did.Document) - ret1, _ := ret[1].(*types.DocumentMetadata) + ret1, _ := ret[1].(*resolver.DocumentMetadata) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } diff --git a/vdr/didnuts/didstore/reader.go b/vdr/didnuts/didstore/reader.go index 4ce84d4dc4..7d7c7d470f 100644 --- a/vdr/didnuts/didstore/reader.go +++ b/vdr/didnuts/didstore/reader.go @@ -22,11 +22,10 @@ import ( "encoding/json" "errors" "fmt" - "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/nuts-foundation/nuts-node/vdr/types" - "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-stoabs" + "github.com/nuts-foundation/nuts-node/crypto/hash" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) func readDocument(tx stoabs.ReadTx, documentHash hash.SHA256Hash) (did.Document, error) { @@ -37,7 +36,7 @@ func readDocument(tx stoabs.ReadTx, documentHash hash.SHA256Hash) (did.Document, return document, err } if len(documentBytes) == 0 { - return document, types.ErrNotFound + return document, resolver.ErrNotFound } if err := json.Unmarshal(documentBytes, &document); err != nil { return document, fmt.Errorf("unmarshal error on document: %w", err) diff --git a/vdr/didnuts/didstore/reader_test.go b/vdr/didnuts/didstore/reader_test.go index 42fc00e1eb..b0f12d7302 100644 --- a/vdr/didnuts/didstore/reader_test.go +++ b/vdr/didnuts/didstore/reader_test.go @@ -24,7 +24,7 @@ import ( "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-stoabs" "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "testing" @@ -37,7 +37,7 @@ func Test_readDocument(t *testing.T) { err := store.db.Read(context.Background(), func(tx stoabs.ReadTx) error { _, err := readDocument(tx, hash.SHA256Hash{}) - assert.Equal(t, types.ErrNotFound, err) + assert.Equal(t, resolver.ErrNotFound, err) return nil }) diff --git a/vdr/didnuts/didstore/store.go b/vdr/didnuts/didstore/store.go index e1ea473b95..ddd4083f5c 100644 --- a/vdr/didnuts/didstore/store.go +++ b/vdr/didnuts/didstore/store.go @@ -27,7 +27,8 @@ import ( "github.com/nuts-foundation/go-stoabs" "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/storage" - vdr "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) var _ core.Configurable = (*store)(nil) @@ -134,7 +135,7 @@ func (tl *store) Add(didDocument did.Document, transaction Transaction) error { return nil } -func (tl *store) Resolve(id did.DID, resolveMetadata *vdr.ResolveMetadata) (returnDocument *did.Document, returnMetadata *vdr.DocumentMetadata, txErr error) { +func (tl *store) Resolve(id did.DID, resolveMetadata *resolver.ResolveMetadata) (returnDocument *did.Document, returnMetadata *resolver.DocumentMetadata, txErr error) { txErr = tl.db.Read(context.Background(), func(tx stoabs.ReadTx) error { latestReader := tx.GetShelfReader(latestShelf) latestMetaRef, err := latestReader.Get(stoabs.BytesKey(id.String())) @@ -143,7 +144,7 @@ func (tl *store) Resolve(id did.DID, resolveMetadata *vdr.ResolveMetadata) (retu } if latestMetaRef == nil { - return vdr.ErrNotFound + return resolver.ErrNotFound } // loop over all versions @@ -155,7 +156,7 @@ func (tl *store) Resolve(id did.DID, resolveMetadata *vdr.ResolveMetadata) (retu if metadata.Deactivated && latestNonDeactivatedRequested(resolveMetadata) { // We're trying to resolve the latest, it should not return an older (active) version when deactivated - return vdr.ErrDeactivated + return resolver.ErrDeactivated } if matches(metadata, resolveMetadata) { mdTmp := metadata.asVDRMetadata() @@ -172,12 +173,12 @@ func (tl *store) Resolve(id did.DID, resolveMetadata *vdr.ResolveMetadata) (retu } latestMetaRef = []byte(fmt.Sprintf("%s%d", id.String(), metadata.Version-1)) } - return vdr.ErrNotFound + return resolver.ErrNotFound }) return } -func (tl *store) Iterate(fn vdr.DocIterator) error { +func (tl *store) Iterate(fn management.DocIterator) error { return tl.db.Read(context.Background(), func(tx stoabs.ReadTx) error { latestReader := tx.GetShelfReader(latestShelf) @@ -246,7 +247,7 @@ func (tl *store) removeCachedConflict(document did.Document) { delete(tl.conflictedDocuments, document.ID.String()) } -func (tl *store) Conflicted(fn vdr.DocIterator) error { +func (tl *store) Conflicted(fn management.DocIterator) error { for _, conflicted := range tl.conflictedDocuments { if err := fn(conflicted.didDocument, conflicted.metadata.asVDRMetadata()); err != nil { return err @@ -289,7 +290,7 @@ func (tl *store) DocumentCount() (uint, error) { return uint(count), err } -func matches(metadata documentMetadata, resolveMetadata *vdr.ResolveMetadata) bool { +func matches(metadata documentMetadata, resolveMetadata *resolver.ResolveMetadata) bool { if metadata.Deactivated && (resolveMetadata == nil || !resolveMetadata.AllowDeactivated) { return false } @@ -334,7 +335,7 @@ func matches(metadata documentMetadata, resolveMetadata *vdr.ResolveMetadata) bo // if resolveTime, hash or sourceTransaction is given, most likely the latest version is not requested // the deactivated check is then done in matches() // finally, if the latest is requested and it is deactivated, the allowDeactivated flag is checked -func latestNonDeactivatedRequested(resolveMetadata *vdr.ResolveMetadata) bool { +func latestNonDeactivatedRequested(resolveMetadata *resolver.ResolveMetadata) bool { if resolveMetadata == nil { return true } diff --git a/vdr/didnuts/didstore/store_test.go b/vdr/didnuts/didstore/store_test.go index b1fd349f66..c4eb5b004b 100644 --- a/vdr/didnuts/didstore/store_test.go +++ b/vdr/didnuts/didstore/store_test.go @@ -29,7 +29,7 @@ import ( "github.com/nuts-foundation/go-stoabs" "github.com/nuts-foundation/go-stoabs/redis7" "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/redis/go-redis/v9" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -221,7 +221,7 @@ func TestStore_Resolve(t *testing.T) { t.Run("not found", func(t *testing.T) { _, _, err := store.Resolve(did.MustParseDID("did:nuts:unknown"), nil) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) }) t.Run("latest", func(t *testing.T) { @@ -234,7 +234,7 @@ func TestStore_Resolve(t *testing.T) { t.Run("previous", func(t *testing.T) { before := txUpdate.SigningTime.Add(-1 * time.Second) - doc, meta, err := store.Resolve(testDID, &types.ResolveMetadata{ResolveTime: &before}) + doc, meta, err := store.Resolve(testDID, &resolver.ResolveMetadata{ResolveTime: &before}) require.NoError(t, err) assert.Len(t, doc.Service, 0) @@ -243,9 +243,9 @@ func TestStore_Resolve(t *testing.T) { t.Run("to far back", func(t *testing.T) { before := txUpdate.SigningTime.Add(-3 * time.Second) - _, _, err := store.Resolve(testDID, &types.ResolveMetadata{ResolveTime: &before}) + _, _, err := store.Resolve(testDID, &resolver.ResolveMetadata{ResolveTime: &before}) - assert.Equal(t, types.ErrNotFound, err) + assert.Equal(t, resolver.ErrNotFound, err) }) t.Run("deactivated", func(t *testing.T) { @@ -257,7 +257,7 @@ func TestStore_Resolve(t *testing.T) { _, _, err := store.Resolve(testDID, nil) - assert.Equal(t, types.ErrDeactivated, err) + assert.Equal(t, resolver.ErrDeactivated, err) }) t.Run("deactivated, but specifically asking for !allowDeactivated", func(t *testing.T) { @@ -267,9 +267,9 @@ func TestStore_Resolve(t *testing.T) { add(t, store, create, txCreate) add(t, store, update, txUpdate) - _, _, err := store.Resolve(testDID, &types.ResolveMetadata{}) + _, _, err := store.Resolve(testDID, &resolver.ResolveMetadata{}) - assert.Equal(t, types.ErrDeactivated, err) + assert.Equal(t, resolver.ErrDeactivated, err) }) } @@ -280,7 +280,7 @@ func TestStore_Iterate(t *testing.T) { transaction := newTestTransaction(document) add(t, store, document, transaction) - err := store.Iterate(func(doc did.Document, metadata types.DocumentMetadata) error { + err := store.Iterate(func(doc did.Document, metadata resolver.DocumentMetadata) error { assert.Equal(t, document, doc) assert.Equal(t, []hash.SHA256Hash{transaction.Ref}, metadata.SourceTransactions) return nil @@ -384,38 +384,38 @@ func Test_matches(t *testing.T) { t.Run("time", func(t *testing.T) { resolveTime := now.Add(-1 * time.Second) - assert.True(t, matches(metadata, &types.ResolveMetadata{ResolveTime: &resolveTime})) + assert.True(t, matches(metadata, &resolver.ResolveMetadata{ResolveTime: &resolveTime})) }) t.Run("no resolveMetadata", func(t *testing.T) { assert.True(t, matches(metadata, nil)) }) t.Run("empty resolveMetadata", func(t *testing.T) { - assert.True(t, matches(metadata, &types.ResolveMetadata{})) + assert.True(t, matches(metadata, &resolver.ResolveMetadata{})) }) t.Run("deactivated", func(t *testing.T) { - assert.True(t, matches(deactivated, &types.ResolveMetadata{AllowDeactivated: true})) + assert.True(t, matches(deactivated, &resolver.ResolveMetadata{AllowDeactivated: true})) }) t.Run("source transaction", func(t *testing.T) { - assert.True(t, matches(metadata, &types.ResolveMetadata{SourceTransaction: &h})) + assert.True(t, matches(metadata, &resolver.ResolveMetadata{SourceTransaction: &h})) }) t.Run("hash", func(t *testing.T) { - assert.True(t, matches(metadata, &types.ResolveMetadata{Hash: &h})) + assert.True(t, matches(metadata, &resolver.ResolveMetadata{Hash: &h})) }) }) t.Run("false", func(t *testing.T) { t.Run("time", func(t *testing.T) { resolveTime := now.Add(-3 * time.Second) - assert.False(t, matches(metadata, &types.ResolveMetadata{ResolveTime: &resolveTime})) + assert.False(t, matches(metadata, &resolver.ResolveMetadata{ResolveTime: &resolveTime})) }) t.Run("no meta and deactivated", func(t *testing.T) { assert.False(t, matches(deactivated, nil)) }) t.Run("hash", func(t *testing.T) { - assert.False(t, matches(metadata, &types.ResolveMetadata{Hash: &h2})) + assert.False(t, matches(metadata, &resolver.ResolveMetadata{Hash: &h2})) }) t.Run("source transaction", func(t *testing.T) { - assert.False(t, matches(metadata, &types.ResolveMetadata{SourceTransaction: &h2})) + assert.False(t, matches(metadata, &resolver.ResolveMetadata{SourceTransaction: &h2})) }) }) } diff --git a/vdr/didnuts/manipulator.go b/vdr/didnuts/manipulator.go index 4ab2ecd520..9e2f2ad0a4 100644 --- a/vdr/didnuts/manipulator.go +++ b/vdr/didnuts/manipulator.go @@ -22,7 +22,8 @@ import ( ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" nutsCrypto "github.com/nuts-foundation/nuts-node/crypto" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) // Manipulator contains helper methods to update a Nuts DID document. @@ -30,15 +31,15 @@ type Manipulator struct { // KeyCreator is used for getting a fresh key and use it to generate the Nuts DID KeyCreator nutsCrypto.KeyCreator // Updater is used for updating DID documents after the operation has been performed - Updater types.DocUpdater + Updater management.DocUpdater // Resolver is used for resolving DID Documents - Resolver types.DIDResolver + Resolver resolver.DIDResolver } // Deactivate updates the DID Document so it can no longer be updated // It removes key material, services and controllers. func (u Manipulator) Deactivate(ctx context.Context, id did.DID) error { - _, _, err := u.Resolver.Resolve(id, &types.ResolveMetadata{AllowDeactivated: true}) + _, _, err := u.Resolver.Resolve(id, &resolver.ResolveMetadata{AllowDeactivated: true}) if err != nil { return err } @@ -50,13 +51,13 @@ func (u Manipulator) Deactivate(ctx context.Context, id did.DID) error { // AddVerificationMethod adds a new key as a VerificationMethod to the document. // The key is added to the VerficationMethod relationships specified by keyUsage. -func (u Manipulator) AddVerificationMethod(ctx context.Context, id did.DID, keyUsage types.DIDKeyFlags) (*did.VerificationMethod, error) { - doc, meta, err := u.Resolver.Resolve(id, &types.ResolveMetadata{AllowDeactivated: true}) +func (u Manipulator) AddVerificationMethod(ctx context.Context, id did.DID, keyUsage management.DIDKeyFlags) (*did.VerificationMethod, error) { + doc, meta, err := u.Resolver.Resolve(id, &resolver.ResolveMetadata{AllowDeactivated: true}) if err != nil { return nil, err } if meta.Deactivated { - return nil, types.ErrDeactivated + return nil, resolver.ErrDeactivated } method, err := CreateNewVerificationMethodForDID(ctx, doc.ID, u.KeyCreator) if err != nil { @@ -73,12 +74,12 @@ func (u Manipulator) AddVerificationMethod(ctx context.Context, id did.DID, keyU // RemoveVerificationMethod is a helper function to remove a verificationMethod from a DID Document func (u Manipulator) RemoveVerificationMethod(ctx context.Context, id, keyID did.DID) error { - doc, meta, err := u.Resolver.Resolve(id, &types.ResolveMetadata{AllowDeactivated: true}) + doc, meta, err := u.Resolver.Resolve(id, &resolver.ResolveMetadata{AllowDeactivated: true}) if err != nil { return err } if meta.Deactivated { - return types.ErrDeactivated + return resolver.ErrDeactivated } lenBefore := len(doc.VerificationMethod) doc.RemoveVerificationMethod(keyID) diff --git a/vdr/didnuts/manipulator_test.go b/vdr/didnuts/manipulator_test.go index b1d0bdc763..362561894d 100644 --- a/vdr/didnuts/manipulator_test.go +++ b/vdr/didnuts/manipulator_test.go @@ -21,12 +21,13 @@ import ( "context" "errors" "github.com/nuts-foundation/nuts-node/audit" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "testing" ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -35,8 +36,8 @@ import ( // manipulatorTestContext contains the controller and mocks needed for testing the Manipulator type manipulatorTestContext struct { ctrl *gomock.Controller - mockUpdater *types.MockDocUpdater - mockResolver *types.MockDIDResolver + mockUpdater *management.MockDocUpdater + mockResolver *resolver.MockDIDResolver mockKeyCreator *mockKeyCreator manipulator *Manipulator audit context.Context @@ -45,8 +46,8 @@ type manipulatorTestContext struct { func newManipulatorTestContext(t *testing.T) manipulatorTestContext { t.Helper() ctrl := gomock.NewController(t) - updater := types.NewMockDocUpdater(ctrl) - resolver := types.NewMockDIDResolver(ctrl) + updater := management.NewMockDocUpdater(ctrl) + resolver := resolver.NewMockDIDResolver(ctrl) keyCreator := &mockKeyCreator{} return manipulatorTestContext{ ctrl: ctrl, @@ -74,7 +75,7 @@ func TestManipulator_RemoveVerificationMethod(t *testing.T) { t.Run("ok", func(t *testing.T) { ctx := newManipulatorTestContext(t) - ctx.mockResolver.EXPECT().Resolve(*id123, &types.ResolveMetadata{AllowDeactivated: true}).Return(doc, &types.DocumentMetadata{}, nil) + ctx.mockResolver.EXPECT().Resolve(*id123, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(doc, &resolver.DocumentMetadata{}, nil) ctx.mockUpdater.EXPECT().Update(ctx.audit, *id123, did.Document{ID: *id123}) err := ctx.manipulator.RemoveVerificationMethod(ctx.audit, *id123, *id123Method) @@ -89,7 +90,7 @@ func TestManipulator_RemoveVerificationMethod(t *testing.T) { t.Run("ok - verificationMethod is not part of the document", func(t *testing.T) { ctx := newManipulatorTestContext(t) - ctx.mockResolver.EXPECT().Resolve(*id123, &types.ResolveMetadata{AllowDeactivated: true}).Return(&did.Document{ID: *id123}, &types.DocumentMetadata{}, nil) + ctx.mockResolver.EXPECT().Resolve(*id123, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(&did.Document{ID: *id123}, &resolver.DocumentMetadata{}, nil) err := ctx.manipulator.RemoveVerificationMethod(ctx.audit, *id123, *id123Method) @@ -98,11 +99,11 @@ func TestManipulator_RemoveVerificationMethod(t *testing.T) { t.Run("error - document is deactivated", func(t *testing.T) { ctx := newManipulatorTestContext(t) - ctx.mockResolver.EXPECT().Resolve(*id123, &types.ResolveMetadata{AllowDeactivated: true}).Return(&did.Document{ID: *id123}, &types.DocumentMetadata{Deactivated: true}, nil) + ctx.mockResolver.EXPECT().Resolve(*id123, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(&did.Document{ID: *id123}, &resolver.DocumentMetadata{Deactivated: true}, nil) err := ctx.manipulator.RemoveVerificationMethod(ctx.audit, *id123, *id123Method) assert.EqualError(t, err, "the DID document has been deactivated") - assert.True(t, errors.Is(err, types.ErrDeactivated)) + assert.True(t, errors.Is(err, resolver.ErrDeactivated)) }) } @@ -134,13 +135,13 @@ func TestManipulator_AddKey(t *testing.T) { currentDIDDocument := did.Document{ID: *id, Controller: []did.DID{*id}} currentDIDDocument.AddCapabilityInvocation(&did.VerificationMethod{ID: *keyID}) - ctx.mockResolver.EXPECT().Resolve(*id, &types.ResolveMetadata{AllowDeactivated: true}).Return(¤tDIDDocument, &types.DocumentMetadata{}, nil) + ctx.mockResolver.EXPECT().Resolve(*id, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(¤tDIDDocument, &resolver.DocumentMetadata{}, nil) var updatedDocument did.Document ctx.mockUpdater.EXPECT().Update(ctx.audit, *id, gomock.Any()).Do(func(_ context.Context, _ did.DID, doc did.Document) { updatedDocument = doc }) - key, err := ctx.manipulator.AddVerificationMethod(ctx.audit, *id, types.AuthenticationUsage) + key, err := ctx.manipulator.AddVerificationMethod(ctx.audit, *id, management.AuthenticationUsage) require.NoError(t, err) assert.NotNil(t, key) assert.Equal(t, key.Controller, *id, @@ -156,11 +157,11 @@ func TestManipulator_AddKey(t *testing.T) { currentDIDDocument := did.Document{ID: *id, Controller: []did.DID{*id}} currentDIDDocument.AddCapabilityInvocation(&did.VerificationMethod{ID: *keyID}) - ctx.mockResolver.EXPECT().Resolve(*id, &types.ResolveMetadata{AllowDeactivated: true}).Return(¤tDIDDocument, &types.DocumentMetadata{}, nil) - ctx.mockUpdater.EXPECT().Update(ctx.audit, *id, gomock.Any()).Return(types.ErrNotFound) + ctx.mockResolver.EXPECT().Resolve(*id, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(¤tDIDDocument, &resolver.DocumentMetadata{}, nil) + ctx.mockUpdater.EXPECT().Update(ctx.audit, *id, gomock.Any()).Return(resolver.ErrNotFound) key, err := ctx.manipulator.AddVerificationMethod(ctx.audit, *id, 0) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Nil(t, key) }) @@ -168,11 +169,11 @@ func TestManipulator_AddKey(t *testing.T) { ctx := newManipulatorTestContext(t) currentDIDDocument := did.Document{ID: *id, Controller: []did.DID{*id}} - ctx.mockResolver.EXPECT().Resolve(*id, &types.ResolveMetadata{AllowDeactivated: true}).Return(¤tDIDDocument, &types.DocumentMetadata{Deactivated: true}, nil) + ctx.mockResolver.EXPECT().Resolve(*id, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(¤tDIDDocument, &resolver.DocumentMetadata{Deactivated: true}, nil) key, err := ctx.manipulator.AddVerificationMethod(nil, *id, 0) - assert.ErrorIs(t, err, types.ErrDeactivated) + assert.ErrorIs(t, err, resolver.ErrDeactivated) assert.Nil(t, key) }) } @@ -186,7 +187,7 @@ func TestManipulator_Deactivate(t *testing.T) { currentDIDDocument := did.Document{ID: *id, Controller: []did.DID{*id}} currentDIDDocument.AddCapabilityInvocation(&did.VerificationMethod{ID: *keyID}) - ctx.mockResolver.EXPECT().Resolve(*id, &types.ResolveMetadata{AllowDeactivated: true}).Return(¤tDIDDocument, &types.DocumentMetadata{}, nil) + ctx.mockResolver.EXPECT().Resolve(*id, &resolver.ResolveMetadata{AllowDeactivated: true}).Return(¤tDIDDocument, &resolver.DocumentMetadata{}, nil) expectedDocument := CreateDocument() expectedDocument.ID = *id ctx.mockUpdater.EXPECT().Update(ctx.audit, *id, expectedDocument) diff --git a/vdr/didnuts/resolver.go b/vdr/didnuts/resolver.go index aa079b6198..5be2bc14b8 100644 --- a/vdr/didnuts/resolver.go +++ b/vdr/didnuts/resolver.go @@ -23,8 +23,7 @@ import ( "fmt" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) const maxControllerDepth = 5 @@ -37,7 +36,7 @@ type Resolver struct { Store didstore.Store } -func (d Resolver) Resolve(id did.DID, metadata *types.ResolveMetadata) (*did.Document, *types.DocumentMetadata, error) { +func (d Resolver) Resolve(id did.DID, metadata *resolver.ResolveMetadata) (*did.Document, *resolver.DocumentMetadata, error) { if metadata != nil && metadata.AllowDeactivated { // No need to check whether controllers are active if we allow deactivated documents return d.Store.Resolve(id, metadata) @@ -45,12 +44,12 @@ func (d Resolver) Resolve(id did.DID, metadata *types.ResolveMetadata) (*did.Doc return resolve(d.Store, id, metadata, 0) } -func resolve(resolver types.DIDResolver, id did.DID, metadata *types.ResolveMetadata, depth int) (*did.Document, *types.DocumentMetadata, error) { +func resolve(didResolver resolver.DIDResolver, id did.DID, metadata *resolver.ResolveMetadata, depth int) (*did.Document, *resolver.DocumentMetadata, error) { if depth >= maxControllerDepth { return nil, nil, ErrNestedDocumentsTooDeep } - doc, meta, err := resolver.Resolve(id, metadata) + doc, meta, err := didResolver.Resolve(id, metadata) if err != nil { return nil, nil, err } @@ -59,13 +58,13 @@ func resolve(resolver types.DIDResolver, id did.DID, metadata *types.ResolveMeta if len(doc.Controller) > 0 && (metadata == nil || !metadata.AllowDeactivated) { // also check if the controller is not deactivated // since ResolveControllers calls Resolve and propagates the metadata - controllers, err := resolveControllers(resolver, *doc, metadata, depth+1) + controllers, err := resolveControllers(didResolver, *doc, metadata, depth+1) if err != nil { return nil, nil, err } // doc should have controllers, but no results, so they are not active, return error: if len(controllers) == 0 { - return nil, nil, types.ErrNoActiveController + return nil, nil, resolver.ErrNoActiveController } } @@ -73,11 +72,11 @@ func resolve(resolver types.DIDResolver, id did.DID, metadata *types.ResolveMeta } // ResolveControllers finds the DID Document controllers -func ResolveControllers(resolver types.DIDResolver, doc did.Document, metadata *types.ResolveMetadata) ([]did.Document, error) { +func ResolveControllers(resolver resolver.DIDResolver, doc did.Document, metadata *resolver.ResolveMetadata) ([]did.Document, error) { return resolveControllers(resolver, doc, metadata, 0) } -func resolveControllers(resolver types.DIDResolver, doc did.Document, metadata *types.ResolveMetadata, depth int) ([]did.Document, error) { +func resolveControllers(didResolver resolver.DIDResolver, doc did.Document, metadata *resolver.ResolveMetadata, depth int) ([]did.Document, error) { var leaves []did.Document var refsToResolve []did.DID @@ -100,8 +99,8 @@ func resolveControllers(resolver types.DIDResolver, doc did.Document, metadata * // resolve all unresolved doc for _, ref := range refsToResolve { - node, _, err := resolve(resolver, ref, metadata, depth) - if errors.Is(err, types.ErrDeactivated) || errors.Is(err, types.ErrNoActiveController) || errors.Is(err, types.ErrNotFound) || errors.Is(err, types.ErrDIDMethodNotSupported) { + node, _, err := resolve(didResolver, ref, metadata, depth) + if errors.Is(err, resolver.ErrDeactivated) || errors.Is(err, resolver.ErrNoActiveController) || errors.Is(err, resolver.ErrNotFound) || errors.Is(err, resolver.ErrDIDMethodNotSupported) { continue } if errors.Is(err, ErrNestedDocumentsTooDeep) { @@ -116,7 +115,7 @@ func resolveControllers(resolver types.DIDResolver, doc did.Document, metadata * // filter deactivated j := 0 for _, leaf := range leaves { - if !didservice.IsDeactivated(leaf) { + if !resolver.IsDeactivated(leaf) { leaves[j] = leaf j++ } diff --git a/vdr/didnuts/resolver_test.go b/vdr/didnuts/resolver_test.go index dfe13f835b..92bc9206c3 100644 --- a/vdr/didnuts/resolver_test.go +++ b/vdr/didnuts/resolver_test.go @@ -22,7 +22,7 @@ import ( "fmt" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -36,18 +36,18 @@ func TestNutsDIDResolver_Resolve(t *testing.T) { docA := did.Document{ID: *id123} docB := did.Document{ID: *id456, Controller: []did.DID{*id123}} resolveTime := time.Date(2010, 1, 1, 1, 1, 1, 0, time.UTC) - resolveMD := &types.ResolveMetadata{ResolveTime: &resolveTime} + resolveMD := &resolver.ResolveMetadata{ResolveTime: &resolveTime} t.Run("ok", func(t *testing.T) { ctrl := gomock.NewController(t) didStore := didstore.NewMockStore(ctrl) - resolver := Resolver{Store: didStore} + nutsResolver := Resolver{Store: didStore} doc := did.Document{ID: *id123} id123Method1, _ := did.ParseDIDURL("did:nuts:123#method-1") doc.AddCapabilityInvocation(&did.VerificationMethod{ID: *id123Method1}) - didStore.EXPECT().Resolve(*id123, resolveMD).Return(&doc, &types.DocumentMetadata{}, nil) + didStore.EXPECT().Resolve(*id123, resolveMD).Return(&doc, &resolver.DocumentMetadata{}, nil) - resultDoc, _, err := resolver.Resolve(*id123, resolveMD) + resultDoc, _, err := nutsResolver.Resolve(*id123, resolveMD) require.NoError(t, err) @@ -58,40 +58,40 @@ func TestNutsDIDResolver_Resolve(t *testing.T) { t.Run("err - with resolver metadata", func(t *testing.T) { ctrl := gomock.NewController(t) didStore := didstore.NewMockStore(ctrl) - resolver := Resolver{Store: didStore} - didStore.EXPECT().Resolve(*id456, resolveMD).Return(&docB, &types.DocumentMetadata{}, nil) - didStore.EXPECT().Resolve(*id123, resolveMD).Return(&docA, &types.DocumentMetadata{}, nil) + nutsResolver := Resolver{Store: didStore} + didStore.EXPECT().Resolve(*id456, resolveMD).Return(&docB, &resolver.DocumentMetadata{}, nil) + didStore.EXPECT().Resolve(*id123, resolveMD).Return(&docA, &resolver.DocumentMetadata{}, nil) - doc, _, err := resolver.Resolve(*id456, resolveMD) + doc, _, err := nutsResolver.Resolve(*id456, resolveMD) assert.Error(t, err) - assert.Equal(t, types.ErrNoActiveController, err) + assert.Equal(t, resolver.ErrNoActiveController, err) assert.Nil(t, doc) }) t.Run("err - without resolve metadata", func(t *testing.T) { ctrl := gomock.NewController(t) didStore := didstore.NewMockStore(ctrl) - resolver := Resolver{Store: didStore} - didStore.EXPECT().Resolve(*id456, nil).Return(&docB, &types.DocumentMetadata{}, nil) - didStore.EXPECT().Resolve(*id123, nil).Return(&docA, &types.DocumentMetadata{}, nil) + nutsResolver := Resolver{Store: didStore} + didStore.EXPECT().Resolve(*id456, nil).Return(&docB, &resolver.DocumentMetadata{}, nil) + didStore.EXPECT().Resolve(*id123, nil).Return(&docA, &resolver.DocumentMetadata{}, nil) - doc, _, err := resolver.Resolve(*id456, nil) + doc, _, err := nutsResolver.Resolve(*id456, nil) assert.Error(t, err) - assert.Equal(t, types.ErrNoActiveController, err) + assert.Equal(t, resolver.ErrNoActiveController, err) assert.Nil(t, doc) }) t.Run("ok - allowed deactivated", func(t *testing.T) { ctrl := gomock.NewController(t) didStore := didstore.NewMockStore(ctrl) - resolver := Resolver{Store: didStore} - resolveMD := &types.ResolveMetadata{ResolveTime: &resolveTime, AllowDeactivated: true} + nutsResolver := Resolver{Store: didStore} + resolveMD := &resolver.ResolveMetadata{ResolveTime: &resolveTime, AllowDeactivated: true} - didStore.EXPECT().Resolve(*id456, resolveMD).Return(&docB, &types.DocumentMetadata{}, nil) + didStore.EXPECT().Resolve(*id456, resolveMD).Return(&docB, &resolver.DocumentMetadata{}, nil) - doc, _, err := resolver.Resolve(*id456, resolveMD) + doc, _, err := nutsResolver.Resolve(*id456, resolveMD) assert.NoError(t, err) assert.Equal(t, docB, *doc) }) @@ -107,19 +107,19 @@ func TestNutsDIDResolver_Resolve(t *testing.T) { prevID := rootID prevDoc := rootDoc didStore := didstore.NewMockStore(ctrl) - resolver := Resolver{Store: didStore} + nutsResolver := Resolver{Store: didStore} for i := 0; i < depth; i++ { id, _ := did.ParseDID(fmt.Sprintf("did:nuts:%d", i)) d := did.Document{ID: *id, Controller: []did.DID{*prevID}} - didStore.EXPECT().Resolve(*prevID, resolveMD).Return(&prevDoc, &types.DocumentMetadata{}, nil).AnyTimes() + didStore.EXPECT().Resolve(*prevID, resolveMD).Return(&prevDoc, &resolver.DocumentMetadata{}, nil).AnyTimes() dids[i] = id docs[i] = d prevID = id prevDoc = d } - didStore.EXPECT().Resolve(*dids[depth-1], resolveMD).Return(&docs[depth-1], &types.DocumentMetadata{}, nil) + didStore.EXPECT().Resolve(*dids[depth-1], resolveMD).Return(&docs[depth-1], &resolver.DocumentMetadata{}, nil) - _, _, err := resolver.Resolve(*dids[depth-1], resolveMD) + _, _, err := nutsResolver.Resolve(*dids[depth-1], resolveMD) assert.Error(t, err) assert.Equal(t, ErrNestedDocumentsTooDeep, err) @@ -132,7 +132,7 @@ func TestResolveControllers(t *testing.T) { id456Method1, _ := did.ParseDIDURL("did:nuts:456#method-1") t.Run("emtpy input", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + resolver := resolver.NewMockDIDResolver(ctrl) docs, err := ResolveControllers(resolver, did.Document{}, nil) assert.NoError(t, err) assert.Len(t, docs, 0, @@ -141,7 +141,7 @@ func TestResolveControllers(t *testing.T) { t.Run("doc is its own controller", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + resolver := resolver.NewMockDIDResolver(ctrl) doc := did.Document{ID: *id123} doc.AddCapabilityInvocation(&did.VerificationMethod{ID: *id123Method1}) docs, err := ResolveControllers(resolver, doc, nil) @@ -153,7 +153,7 @@ func TestResolveControllers(t *testing.T) { t.Run("doc is deactivated", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + resolver := resolver.NewMockDIDResolver(ctrl) doc := did.Document{ID: *id123} docs, err := ResolveControllers(resolver, doc, nil) assert.NoError(t, err) @@ -163,18 +163,18 @@ func TestResolveControllers(t *testing.T) { t.Run("docA is controller of docB", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + nutsResolver := resolver.NewMockDIDResolver(ctrl) docA := did.Document{ID: *id123} docA.AddCapabilityInvocation(&did.VerificationMethod{ID: *id123Method1}) resolveTime := time.Date(2010, 1, 1, 1, 1, 1, 0, time.UTC) - resolveMD := &types.ResolveMetadata{ResolveTime: &resolveTime} - resolver.EXPECT().Resolve(*id123, resolveMD).Return(&docA, &types.DocumentMetadata{}, nil) + resolveMD := &resolver.ResolveMetadata{ResolveTime: &resolveTime} + nutsResolver.EXPECT().Resolve(*id123, resolveMD).Return(&docA, &resolver.DocumentMetadata{}, nil) docB := did.Document{ID: *id456, Controller: []did.DID{*id123}} - docs, err := ResolveControllers(resolver, docB, resolveMD) + docs, err := ResolveControllers(nutsResolver, docB, resolveMD) assert.NoError(t, err) assert.Len(t, docs, 1) assert.Equal(t, docA, docs[0], @@ -183,32 +183,32 @@ func TestResolveControllers(t *testing.T) { t.Run("docA is controller of docB and docA is deactivated", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + nutsResolver := resolver.NewMockDIDResolver(ctrl) docA := did.Document{ID: *id123} resolveTime := time.Date(2010, 1, 1, 1, 1, 1, 0, time.UTC) - resolveMD := &types.ResolveMetadata{ResolveTime: &resolveTime} - resolver.EXPECT().Resolve(*id123, resolveMD).Return(&docA, &types.DocumentMetadata{}, nil) + resolveMD := &resolver.ResolveMetadata{ResolveTime: &resolveTime} + nutsResolver.EXPECT().Resolve(*id123, resolveMD).Return(&docA, &resolver.DocumentMetadata{}, nil) docB := did.Document{ID: *id456, Controller: []did.DID{*id123}} - docs, err := ResolveControllers(resolver, docB, resolveMD) + docs, err := ResolveControllers(nutsResolver, docB, resolveMD) assert.NoError(t, err) assert.Len(t, docs, 0) }) t.Run("docA and docB are both the controllers of docB", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + nutsResolver := resolver.NewMockDIDResolver(ctrl) docA := did.Document{ID: *id123} docA.AddCapabilityInvocation(&did.VerificationMethod{ID: *id123Method1}) - resolver.EXPECT().Resolve(*id123, gomock.Any()).Return(&docA, &types.DocumentMetadata{}, nil) + nutsResolver.EXPECT().Resolve(*id123, gomock.Any()).Return(&docA, &resolver.DocumentMetadata{}, nil) docB := did.Document{ID: *id456, Controller: []did.DID{*id123, *id456}} docB.AddCapabilityInvocation(&did.VerificationMethod{ID: *id456Method1}) - docs, err := ResolveControllers(resolver, docB, nil) + docs, err := ResolveControllers(nutsResolver, docB, nil) assert.NoError(t, err) assert.Len(t, docs, 2) assert.Equal(t, []did.Document{docB, docA}, docs, @@ -217,17 +217,17 @@ func TestResolveControllers(t *testing.T) { t.Run("docA and docB are both the controllers of docB, resolve by source transaction", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + nutsResolver := resolver.NewMockDIDResolver(ctrl) docA := did.Document{ID: *id123} docA.AddCapabilityInvocation(&did.VerificationMethod{ID: *id123Method1}) // when we resolve by source TX, we will not find the other controller - resolver.EXPECT().Resolve(*id123, gomock.Any()).Return(nil, nil, types.ErrNotFound) + nutsResolver.EXPECT().Resolve(*id123, gomock.Any()).Return(nil, nil, resolver.ErrNotFound) docB := did.Document{ID: *id456, Controller: []did.DID{*id123, *id456}} docB.AddCapabilityInvocation(&did.VerificationMethod{ID: *id456Method1}) - docs, err := ResolveControllers(resolver, docB, nil) + docs, err := ResolveControllers(nutsResolver, docB, nil) assert.NoError(t, err) assert.Len(t, docs, 1) assert.Equal(t, []did.Document{docB}, docs, @@ -236,11 +236,11 @@ func TestResolveControllers(t *testing.T) { t.Run("docA, docB and docC are controllers of docA, docB is deactivated", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + nutsResolver := resolver.NewMockDIDResolver(ctrl) // Doc B is deactivated docBID, _ := did.ParseDID("did:nuts:B") docB := did.Document{ID: *docBID} - resolver.EXPECT().Resolve(docB.ID, gomock.Any()).Return(&docB, &types.DocumentMetadata{}, nil) + nutsResolver.EXPECT().Resolve(docB.ID, gomock.Any()).Return(&docB, &resolver.DocumentMetadata{}, nil) // Doc C is active docCID, _ := did.ParseDID("did:nuts:C") @@ -248,7 +248,7 @@ func TestResolveControllers(t *testing.T) { docCIDCapInv.Fragment = "cap-inv" docC := did.Document{ID: *docCID} docC.AddCapabilityInvocation(&did.VerificationMethod{ID: docCIDCapInv}) - resolver.EXPECT().Resolve(docC.ID, gomock.Any()).Return(&docC, &types.DocumentMetadata{}, nil) + nutsResolver.EXPECT().Resolve(docC.ID, gomock.Any()).Return(&docC, &resolver.DocumentMetadata{}, nil) // Doc A is active docAID, _ := did.ParseDID("did:nuts:A") @@ -258,7 +258,7 @@ func TestResolveControllers(t *testing.T) { docA.Controller = []did.DID{docA.ID, docB.ID, docC.ID} docA.AddCapabilityInvocation(&did.VerificationMethod{ID: docAIDCapInv}) - docs, err := ResolveControllers(resolver, docA, nil) + docs, err := ResolveControllers(nutsResolver, docA, nil) assert.NoError(t, err) assert.Len(t, docs, 2) assert.Contains(t, docs, docA, "expected docA to be resolved as controller of docA") @@ -267,15 +267,15 @@ func TestResolveControllers(t *testing.T) { t.Run("docA is controller of docB, docA has explicit self link in Controllers", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) + nutsResolver := resolver.NewMockDIDResolver(ctrl) docA := did.Document{ID: *id123, Controller: []did.DID{*id123}} docA.AddCapabilityInvocation(&did.VerificationMethod{ID: *id123Method1}) - resolver.EXPECT().Resolve(*id123, gomock.Any()).Return(&docA, &types.DocumentMetadata{}, nil) + nutsResolver.EXPECT().Resolve(*id123, gomock.Any()).Return(&docA, &resolver.DocumentMetadata{}, nil) docB := did.Document{ID: *id456, Controller: []did.DID{*id123}} - docs, err := ResolveControllers(resolver, docB, nil) + docs, err := ResolveControllers(nutsResolver, docB, nil) assert.NoError(t, err) assert.Len(t, docs, 1) assert.Equal(t, docA, docs[0], @@ -284,12 +284,12 @@ func TestResolveControllers(t *testing.T) { t.Run("ok - Resolve can not find the document", func(t *testing.T) { ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) - resolver.EXPECT().Resolve(*id123, gomock.Any()).Return(nil, nil, types.ErrNotFound) + nutsResolver := resolver.NewMockDIDResolver(ctrl) + nutsResolver.EXPECT().Resolve(*id123, gomock.Any()).Return(nil, nil, resolver.ErrNotFound) docB := did.Document{ID: *id456, Controller: []did.DID{*id123}} - docs, err := ResolveControllers(resolver, docB, nil) + docs, err := ResolveControllers(nutsResolver, docB, nil) require.NoError(t, err) assert.Len(t, docs, 0) }) diff --git a/vdr/didnuts/validators.go b/vdr/didnuts/validators.go index 346451e630..95d4acb079 100644 --- a/vdr/didnuts/validators.go +++ b/vdr/didnuts/validators.go @@ -25,8 +25,7 @@ import ( ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/network/transport" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" ) // NetworkDocumentValidator creates a DID Document validator that checks for inconsistencies in the DID Document: @@ -50,7 +49,7 @@ func NetworkDocumentValidator() did.Validator { } // ManagedDocumentValidator extends NetworkDocumentValidator with extra safety checks to be performed on DID documents managed by this node before they are published on the network. -func ManagedDocumentValidator(serviceResolver types.ServiceResolver) did.Validator { +func ManagedDocumentValidator(serviceResolver resolver.ServiceResolver) did.Validator { return &did.MultiValidator{Validators: []did.Validator{ NetworkDocumentValidator(), managedServiceValidator{serviceResolver}, @@ -113,7 +112,7 @@ func (b basicServiceValidator) Validate(document did.Document) error { // service.type if knownServiceTypes[service.Type] { // RFC006 §4: A DID Document MAY NOT contain more than one service with the same type. - return InvalidServiceError{types.ErrDuplicateService} + return InvalidServiceError{resolver.ErrDuplicateService} } knownServiceTypes[service.Type] = true } @@ -125,7 +124,7 @@ func (b basicServiceValidator) Validate(document did.Document) error { // This validator is exists to guarantee that the service endpoints are at least valid at time of publication. // Should be used together with basicServiceValidator for full service validation. type managedServiceValidator struct { - serviceResolver types.ServiceResolver + serviceResolver resolver.ServiceResolver } func (m managedServiceValidator) Validate(document did.Document) error { @@ -192,15 +191,15 @@ func (m managedServiceValidator) resolveOrReturnEndpoint(service did.Service, ca return nil, errors.New("invalid service format") } // make sure that it resolves if it is a reference - if didservice.IsServiceReference(serviceEndpoint) { + if resolver.IsServiceReference(serviceEndpoint) { serviceURI, err := ssi.ParseURI(serviceEndpoint) if err != nil { return nil, err } - if err = didservice.ValidateServiceReference(*serviceURI); err != nil { + if err = resolver.ValidateServiceReference(*serviceURI); err != nil { return nil, err } - resolvedService, err := m.serviceResolver.ResolveEx(*serviceURI, 0, didservice.DefaultMaxServiceReferenceDepth, cache) + resolvedService, err := m.serviceResolver.ResolveEx(*serviceURI, 0, resolver.DefaultMaxServiceReferenceDepth, cache) if err != nil { return nil, err } diff --git a/vdr/didnuts/validators_test.go b/vdr/didnuts/validators_test.go index 40d7602d41..867527d34a 100644 --- a/vdr/didnuts/validators_test.go +++ b/vdr/didnuts/validators_test.go @@ -23,8 +23,7 @@ import ( "crypto/elliptic" "crypto/rand" "errors" - "github.com/nuts-foundation/nuts-node/vdr/didservice" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "testing" ssi "github.com/nuts-foundation/go-did" @@ -113,12 +112,12 @@ func Test_managedServiceValidator(t *testing.T) { // comment out validatorTests in the table to find the culprit. referencedDocument, _, _ := newDidDoc() service := did.Service{Type: "referenced_service", ServiceEndpoint: "https://nuts.nl"} - serviceRef := didservice.MakeServiceReference(referencedDocument.ID, service.Type) + serviceRef := resolver.MakeServiceReference(referencedDocument.ID, service.Type) referencedDocument.Service = append(referencedDocument.Service, service) ctrl := gomock.NewController(t) - didResolver := types.NewMockDIDResolver(ctrl) - serviceResolver := didservice.ServiceResolver{Resolver: didResolver} + didResolver := resolver.NewMockDIDResolver(ctrl) + serviceResolver := resolver.DIDServiceResolver{Resolver: didResolver} t.Run("basic", func(t *testing.T) { table := []validatorTest{ @@ -157,7 +156,7 @@ func Test_managedServiceValidator(t *testing.T) { didDoc.Service = append(didDoc.Service, did.Service{ ID: ssi.URI{}, Type: "self_reference", - ServiceEndpoint: didservice.MakeServiceReference(didDoc.ID, didDoc.Service[0].Type), + ServiceEndpoint: resolver.MakeServiceReference(didDoc.ID, didDoc.Service[0].Type), }) didResolver.EXPECT().Resolve(referencedDocument.ID, nil).Return(&referencedDocument, nil, nil) @@ -166,7 +165,7 @@ func Test_managedServiceValidator(t *testing.T) { }, nil}, {"nok - resolve fails", func() did.Document { didDoc, _, _ := newDidDoc() - didDoc.Service[0].ServiceEndpoint = didservice.MakeServiceReference(referencedDocument.ID, "does_not_exist") + didDoc.Service[0].ServiceEndpoint = resolver.MakeServiceReference(referencedDocument.ID, "does_not_exist") didResolver.EXPECT().Resolve(referencedDocument.ID, nil).Return(&referencedDocument, nil, nil) @@ -203,7 +202,7 @@ func Test_managedServiceValidator(t *testing.T) { didDoc, _, _ := newDidDoc() didDoc.Service = append(didDoc.Service, did.Service{ Type: "NutsComm", - ServiceEndpoint: didservice.MakeServiceReference(didDoc.ID, didDoc.Service[0].Type), + ServiceEndpoint: resolver.MakeServiceReference(didDoc.ID, didDoc.Service[0].Type), }) return didDoc }, errors.New("invalid service: NutsComm: scheme must be grpc")}, @@ -242,7 +241,7 @@ func Test_managedServiceValidator(t *testing.T) { didDoc, _, _ := newDidDoc() didDoc.Service[0] = did.Service{ Type: "node-contact-info", - ServiceEndpoint: didservice.MakeServiceReference(didDoc.ID, "otherService"), + ServiceEndpoint: resolver.MakeServiceReference(didDoc.ID, "otherService"), } didDoc.Service = append(didDoc.Service, did.Service{ Type: "otherService", diff --git a/vdr/didservice/resolvers.go b/vdr/didservice/resolvers.go deleted file mode 100644 index 19481c4232..0000000000 --- a/vdr/didservice/resolvers.go +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2022 Nuts community - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -// Package service contains DID Document related functionality that only matters to the current node. -// All functionality here has zero relations to the network. -package didservice - -import ( - "crypto" - "errors" - "fmt" - "sync" - "time" - - ssi "github.com/nuts-foundation/go-did" - "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/vdr/types" -) - -// DefaultMaxServiceReferenceDepth holds the default max. allowed depth for DID service references. -const DefaultMaxServiceReferenceDepth = 5 - -var _ types.DIDResolver = &DIDResolverRouter{} - -// DIDResolverRouter is a DID resolver that can route to different DID resolvers based on the DID method -type DIDResolverRouter struct { - resolvers sync.Map -} - -// Resolve looks up the right resolver for the given DID and delegates the resolution to it. -// If no resolver is registered for the given DID method, ErrDIDMethodNotSupported is returned. -func (r *DIDResolverRouter) Resolve(id did.DID, metadata *types.ResolveMetadata) (*did.Document, *types.DocumentMetadata, error) { - method := id.Method - resolver, registered := r.resolvers.Load(method) - if !registered { - return nil, nil, types.ErrDIDMethodNotSupported - } - return resolver.(types.DIDResolver).Resolve(id, metadata) -} - -// Register registers a DID resolver for the given DID method. -func (r *DIDResolverRouter) Register(method string, resolver types.DIDResolver) { - r.resolvers.Store(method, resolver) -} - -var _ types.KeyResolver = KeyResolver{} - -// KeyResolver implements the KeyResolver interface that uses keys from resolved DIDs. -type KeyResolver struct { - Resolver types.DIDResolver -} - -func (r KeyResolver) ResolveKeyByID(keyID string, validAt *time.Time, relationType types.RelationType) (crypto.PublicKey, error) { - holder, err := GetDIDFromURL(keyID) - if err != nil { - return nil, fmt.Errorf("invalid key ID (id=%s): %w", keyID, err) - } - doc, _, err := r.Resolver.Resolve(holder, &types.ResolveMetadata{ - ResolveTime: validAt, - }) - if err != nil { - return nil, err - } - relationships, err := resolveRelationships(doc, relationType) - if err != nil { - return nil, err - } - for _, rel := range relationships { - if rel.ID.String() == keyID { - return rel.PublicKey() - } - } - return nil, types.ErrKeyNotFound -} - -func (r KeyResolver) ResolveKey(id did.DID, validAt *time.Time, relationType types.RelationType) (ssi.URI, crypto.PublicKey, error) { - doc, _, err := r.Resolver.Resolve(id, &types.ResolveMetadata{ - ResolveTime: validAt, - }) - if err != nil { - return ssi.URI{}, nil, err - } - keys, err := resolveRelationships(doc, relationType) - if err != nil { - return ssi.URI{}, nil, err - } - if len(keys) == 0 { - return ssi.URI{}, nil, types.ErrKeyNotFound - } - publicKey, err := keys[0].PublicKey() - if err != nil { - return ssi.URI{}, nil, err - } - return keys[0].ID.URI(), publicKey, nil -} - -func resolveRelationships(doc *did.Document, relationType types.RelationType) (relationships did.VerificationRelationships, err error) { - switch relationType { - case types.Authentication: - return doc.Authentication, nil - case types.AssertionMethod: - return doc.AssertionMethod, nil - case types.KeyAgreement: - return doc.KeyAgreement, nil - case types.CapabilityInvocation: - return doc.CapabilityInvocation, nil - case types.CapabilityDelegation: - return doc.CapabilityDelegation, nil - default: - return nil, fmt.Errorf("unable to locate RelationType %v", relationType) - } -} - -// ServiceResolver is a wrapper around a DID store that allows resolving services, following references. -type ServiceResolver struct { - Resolver types.DIDResolver -} - -func (s ServiceResolver) Resolve(query ssi.URI, maxDepth int) (did.Service, error) { - return s.ResolveEx(query, 0, maxDepth, map[string]*did.Document{}) -} - -func (s ServiceResolver) ResolveEx(endpoint ssi.URI, depth int, maxDepth int, documentCache map[string]*did.Document) (did.Service, error) { - if depth >= maxDepth { - return did.Service{}, types.ErrServiceReferenceToDeep - } - - referencedDID, err := GetDIDFromURL(endpoint.String()) - if err != nil { - // Shouldn't happen, because only DID URLs are passed? - return did.Service{}, err - } - var document *did.Document - if document = documentCache[referencedDID.String()]; document == nil { - document, _, err = s.Resolver.Resolve(referencedDID, nil) - if err != nil { - return did.Service{}, err - } - documentCache[referencedDID.String()] = document - } - - var service *did.Service - for _, curr := range document.Service { - if curr.Type == endpoint.Query().Get(serviceTypeQueryParameter) { - // If there are multiple services with the same type the document is conflicted. - // This can happen temporarily during a service update (delete old, add new). - // Both endpoints are likely to be active in the timeframe that the conflict exists, so picking the first entry is preferred for availability over an error. - service = &curr - break - } - } - if service == nil { - return did.Service{}, types.ErrServiceNotFound - } - - var endpointURL string - if service.UnmarshalServiceEndpoint(&endpointURL) == nil { - // Service endpoint is a string, if it's a reference we need to resolve it - if IsServiceReference(endpointURL) { - // Looks like a reference, recurse - resolvedEndpointURI, err := ssi.ParseURI(endpointURL) - if err != nil { - return did.Service{}, err - } - err = ValidateServiceReference(*resolvedEndpointURI) - if err != nil { - return did.Service{}, err - } - return s.ResolveEx(*resolvedEndpointURI, depth+1, maxDepth, documentCache) - } - } - return *service, nil -} - -// IsFunctionalResolveError returns true if the given error indicates the DID or service not being found or invalid, -// e.g. because it is deactivated, referenced too deeply, etc. -func IsFunctionalResolveError(target error) bool { - return errors.Is(target, types.ErrNotFound) || - errors.Is(target, types.ErrDeactivated) || - errors.Is(target, types.ErrServiceNotFound) || - errors.Is(target, types.ErrNoActiveController) || - errors.Is(target, types.ErrServiceReferenceToDeep) || - errors.Is(target, did.InvalidDIDErr) || - errors.As(target, new(ServiceQueryError)) -} diff --git a/vdr/didservice/resolvers_test.go b/vdr/didservice/resolvers_test.go deleted file mode 100644 index 352c1acf4a..0000000000 --- a/vdr/didservice/resolvers_test.go +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) 2022 Nuts community - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package didservice - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "errors" - "fmt" - ssi "github.com/nuts-foundation/go-did" - "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/nuts-foundation/nuts-node/vdr/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - "testing" -) - -func TestServiceResolver_Resolve(t *testing.T) { - meta := &types.DocumentMetadata{Hash: hash.EmptyHash()} - - didA, _ := did.ParseDID("did:nuts:A") - didB, _ := did.ParseDID("did:nuts:B") - - serviceID := ssi.MustParseURI(fmt.Sprintf("%s#1", didA.String())) - docA := did.Document{ - Context: []ssi.URI{did.DIDContextV1URI()}, - ID: *didA, - Service: []did.Service{{ - ID: serviceID, - Type: "hello", - ServiceEndpoint: "http://hello", - }}, - } - docB := did.Document{ - Context: []ssi.URI{did.DIDContextV1URI()}, - ID: *didA, - Service: []did.Service{ - { - Type: "simple", - ServiceEndpoint: "http://world", - }, - { - Type: "cyclic-ref", - ServiceEndpoint: didB.String() + "/serviceEndpoint?type=cyclic-ref", - }, - { - Type: "invalid-ref", - ServiceEndpoint: didB.String() + "?type=invalid-ref", - }, - { - Type: "external", - ServiceEndpoint: MakeServiceReference(docA.ID, "hello").String(), - }, - }, - } - - t.Run("ok", func(t *testing.T) { - ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) - - resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(&docB, meta, nil) - - actual, err := ServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "simple"), DefaultMaxServiceReferenceDepth) - - assert.NoError(t, err) - assert.Equal(t, docB.Service[0], actual) - }) - t.Run("ok - external", func(t *testing.T) { - ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) - - resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(&docB, meta, nil) - resolver.EXPECT().Resolve(*didA, nil).MinTimes(1).Return(&docA, meta, nil) - - actual, err := ServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "external"), DefaultMaxServiceReferenceDepth) - - assert.NoError(t, err) - assert.Equal(t, docA.Service[0], actual) - }) - t.Run("error - cyclic reference (yields refs too deep)", func(t *testing.T) { - ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) - - resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(&docB, meta, nil) - - actual, err := ServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "cyclic-ref"), DefaultMaxServiceReferenceDepth) - - assert.EqualError(t, err, "service references are nested to deeply before resolving to a non-reference") - assert.Empty(t, actual) - }) - t.Run("error - invalid ref", func(t *testing.T) { - ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) - - resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(&docB, meta, nil) - - actual, err := ServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "invalid-ref"), DefaultMaxServiceReferenceDepth) - - assert.EqualError(t, err, "DID service query invalid: endpoint URI path must be /serviceEndpoint") - assert.Empty(t, actual) - }) - t.Run("error - service not found", func(t *testing.T) { - ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) - - resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(&docB, meta, nil) - - actual, err := ServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "non-existent"), DefaultMaxServiceReferenceDepth) - - assert.EqualError(t, err, "service not found in DID Document") - assert.Empty(t, actual) - }) - t.Run("error - DID not found", func(t *testing.T) { - ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) - - resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(nil, nil, types.ErrNotFound) - - actual, err := ServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "non-existent"), DefaultMaxServiceReferenceDepth) - - assert.EqualError(t, err, "unable to find the DID document") - assert.Empty(t, actual) - }) -} - -func TestKeyResolver_ResolveKey(t *testing.T) { - ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) - keyResolver := KeyResolver{Resolver: resolver} - - doc := newDidDoc() - resolver.EXPECT().Resolve(doc.ID, gomock.Any()).AnyTimes().Return(&doc, nil, nil) - - t.Run("ok - it finds the key", func(t *testing.T) { - keyId, key, err := keyResolver.ResolveKey(doc.ID, nil, types.AssertionMethod) - require.NoError(t, err) - assert.Equal(t, doc.VerificationMethod[0].ID.URI(), keyId) - assert.NotNil(t, key) - }) - - t.Run("error - document not found", func(t *testing.T) { - unknownDID := did.MustParseDID("did:example:123") - resolver.EXPECT().Resolve(unknownDID, gomock.Any()).Return(nil, nil, types.ErrNotFound) - keyId, key, err := keyResolver.ResolveKey(unknownDID, nil, types.AssertionMethod) - assert.EqualError(t, err, "unable to find the DID document") - assert.Empty(t, keyId) - assert.Nil(t, key) - }) - - t.Run("error - key not found", func(t *testing.T) { - keyId, key, err := keyResolver.ResolveKey(did.MustParseDIDURL(doc.ID.String()), nil, types.CapabilityDelegation) - assert.EqualError(t, err, "key not found in DID document") - assert.Empty(t, keyId) - assert.Nil(t, key) - }) - - t.Run("error - unknown relationship type", func(t *testing.T) { - keyId, key, err := keyResolver.ResolveKey(doc.ID, nil, 1000) - assert.EqualError(t, err, "unable to locate RelationType 1000") - assert.Empty(t, keyId) - assert.Nil(t, key) - }) -} - -func newDidDoc() did.Document { - privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - id := did.MustParseDID("did:example:sakjsakldjsakld") - keyID := id - keyID.Fragment = "key-1" - vm, _ := did.NewVerificationMethod(keyID, ssi.JsonWebKey2020, id, privateKey.Public()) - doc := did.Document{ - ID: id, - } - doc.AddAssertionMethod(vm) - return doc -} - -func TestKeyResolver_ResolveKeyByID(t *testing.T) { - ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) - keyResolver := KeyResolver{Resolver: resolver} - doc := newDidDoc() - resolver.EXPECT().Resolve(doc.ID, gomock.Any()).AnyTimes().Return(&doc, nil, nil) - keyID := doc.VerificationMethod[0].ID - - t.Run("ok - it finds the key", func(t *testing.T) { - key, err := keyResolver.ResolveKeyByID(keyID.String(), nil, types.AssertionMethod) - assert.NoError(t, err) - assert.NotNil(t, key) - }) - - t.Run("error - invalid key ID", func(t *testing.T) { - key, err := keyResolver.ResolveKeyByID("abcdef", nil, types.AssertionMethod) - assert.EqualError(t, err, "invalid key ID (id=abcdef): invalid DID") - assert.Nil(t, key) - }) - - t.Run("error - document not found", func(t *testing.T) { - unknownDID := did.MustParseDIDURL("did:example:123") - resolver.EXPECT().Resolve(unknownDID, gomock.Any()).Return(nil, nil, types.ErrNotFound) - key, err := keyResolver.ResolveKeyByID(unknownDID.String()+"#456", nil, types.AssertionMethod) - assert.EqualError(t, err, "unable to find the DID document") - assert.Nil(t, key) - }) - - t.Run("error - key not found", func(t *testing.T) { - key, err := keyResolver.ResolveKeyByID(did.MustParseDIDURL(doc.ID.String()+"#123").String(), nil, types.AssertionMethod) - assert.EqualError(t, err, "key not found in DID document") - assert.Nil(t, key) - }) - - t.Run("error - unknown relationship type", func(t *testing.T) { - key, err := keyResolver.ResolveKeyByID(keyID.String(), nil, 1000) - assert.EqualError(t, err, "unable to locate RelationType 1000") - assert.Nil(t, key) - }) -} - -func TestIsFunctionalResolveError(t *testing.T) { - assert.True(t, IsFunctionalResolveError(types.ErrNotFound)) - assert.True(t, IsFunctionalResolveError(types.ErrDeactivated)) - assert.True(t, IsFunctionalResolveError(types.ErrServiceNotFound)) - assert.True(t, IsFunctionalResolveError(types.ErrServiceReferenceToDeep)) - assert.True(t, IsFunctionalResolveError(types.ErrNoActiveController)) - assert.True(t, IsFunctionalResolveError(did.InvalidDIDErr)) - assert.True(t, IsFunctionalResolveError(ServiceQueryError{Err: errors.New("oops")})) - - assert.False(t, IsFunctionalResolveError(errors.New("some error"))) - assert.False(t, IsFunctionalResolveError(types.ErrDuplicateService)) -} - -func TestDIDResolverRouter_Resolve(t *testing.T) { - doc := newDidDoc() - t.Run("ok, 1 resolver", func(t *testing.T) { - ctrl := gomock.NewController(t) - resolver := types.NewMockDIDResolver(ctrl) - resolver.EXPECT().Resolve(doc.ID, gomock.Any()).Return(&doc, nil, nil) - router := &DIDResolverRouter{} - router.Register(doc.ID.Method, resolver) - - actual, _, err := router.Resolve(doc.ID, nil) - assert.NoError(t, err) - assert.Equal(t, &doc, actual) - }) - t.Run("ok, 2 resolvers", func(t *testing.T) { - ctrl := gomock.NewController(t) - otherResolver := types.NewMockDIDResolver(ctrl) - resolver := types.NewMockDIDResolver(ctrl) - resolver.EXPECT().Resolve(doc.ID, gomock.Any()).Return(&doc, nil, nil) - router := &DIDResolverRouter{} - router.Register(doc.ID.Method, resolver) - router.Register("test2", otherResolver) - - actual, _, err := router.Resolve(doc.ID, nil) - assert.NoError(t, err) - assert.Equal(t, &doc, actual) - }) - t.Run("error - resolver not found", func(t *testing.T) { - ctrl := gomock.NewController(t) - otherResolver := types.NewMockDIDResolver(ctrl) - router := &DIDResolverRouter{} - router.Register("other", otherResolver) - - actual, _, err := router.Resolve(doc.ID, nil) - assert.EqualError(t, err, "DID method not supported") - assert.Nil(t, actual) - }) -} diff --git a/vdr/didservice/test.go b/vdr/didservice/test.go deleted file mode 100644 index 121faeab3b..0000000000 --- a/vdr/didservice/test.go +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Nuts node - * Copyright (C) 2021 Nuts community - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package didservice - -import ( - ssi "github.com/nuts-foundation/go-did" - "github.com/nuts-foundation/go-did/did" -) - -// marshal/unmarshal safe notation -var testServiceA = did.Service{ID: ssi.MustParseURI("did:nuts:service:a"), ServiceEndpoint: []interface{}{"http://a"}} -var testServiceB = did.Service{ID: ssi.MustParseURI("did:nuts:service:b"), ServiceEndpoint: []interface{}{"http://b"}} diff --git a/vdr/didservice/util.go b/vdr/didservice/util.go deleted file mode 100644 index 67adff1ab4..0000000000 --- a/vdr/didservice/util.go +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2022 Nuts community - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package didservice - -import ( - "errors" - "fmt" - "strings" - - "github.com/nuts-foundation/go-did" - "github.com/nuts-foundation/go-did/did" -) - -const serviceTypeQueryParameter = "type" -const serviceEndpointPath = "/serviceEndpoint" - -// MakeServiceReference creates a service reference, which can be used as query when looking up services. -func MakeServiceReference(subjectDID did.DID, serviceType string) ssi.URI { - ref := subjectDID.URI() - ref.Opaque += serviceEndpointPath - ref.Fragment = "" - ref.RawQuery = fmt.Sprintf("%s=%s", serviceTypeQueryParameter, serviceType) - return ref -} - -// IsServiceReference checks whether the given endpoint string looks like a service reference (e.g. did:nuts:1234/serviceType?type=HelloWorld). -func IsServiceReference(endpoint string) bool { - return strings.HasPrefix(endpoint, "did:") -} - -// GetDIDFromURL returns the DID from the given URL, stripping any query parameters, path segments and fragments. -func GetDIDFromURL(didURL string) (did.DID, error) { - parsed, err := did.ParseDIDURL(didURL) - if err != nil { - return did.DID{}, err - } - return parsed.WithoutURL(), nil -} - -// ServiceQueryError denies the query based on validation constraints. -type ServiceQueryError struct { - Err error // cause -} - -// Error implements the error interface. -func (e ServiceQueryError) Error() string { - return "DID service query invalid: " + e.Err.Error() -} - -// Unwrap implements the errors.Unwrap convention. -func (e ServiceQueryError) Unwrap() error { return e.Err } - -// ValidateServiceReference checks whether the given URI matches the format for a service reference. -func ValidateServiceReference(endpointURI ssi.URI) error { - // Parse it as DID URL since DID URLs are rootless and thus opaque (RFC 3986), meaning the path will be part of the URI body, rather than the URI path. - // For DID URLs the path is parsed properly. - didEndpointURL, err := did.ParseDIDURL(endpointURI.String()) - if err != nil { - return ServiceQueryError{err} - } - - if "/"+didEndpointURL.Path != serviceEndpointPath { - return ServiceQueryError{errors.New("endpoint URI path must be " + serviceEndpointPath)} - } - - q := endpointURI.Query() - switch len(q[serviceTypeQueryParameter]) { - case 1: - break // good - case 0: - return ServiceQueryError{errors.New("endpoint URI without " + serviceTypeQueryParameter + " query parameter")} - default: - return ServiceQueryError{errors.New("endpoint URI with multiple " + serviceTypeQueryParameter + " query parameters")} - } - - // “Other query parameters, paths or fragments SHALL NOT be used.” - // — RFC006, subsection 4.2 - if len(q) > 1 { - return ServiceQueryError{errors.New("endpoint URI with query parameter other than " + serviceTypeQueryParameter)} - } - - return nil -} - -// IsDeactivated returns true if the DID.Document has already been deactivated -func IsDeactivated(document did.Document) bool { - return len(document.Controller) == 0 && len(document.CapabilityInvocation) == 0 -} diff --git a/vdr/didservice/util_test.go b/vdr/didservice/util_test.go deleted file mode 100644 index 4ab59e8a8d..0000000000 --- a/vdr/didservice/util_test.go +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2022 Nuts community - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package didservice - -import ( - "testing" - - ssi "github.com/nuts-foundation/go-did" - "github.com/nuts-foundation/go-did/did" - "github.com/stretchr/testify/assert" -) - -func Test_MakeServiceReference(t *testing.T) { - d, _ := did.ParseDID("did:nuts:abc") - assert.Equal(t, "did:nuts:abc/serviceEndpoint?type=hello", MakeServiceReference(*d, "hello").String()) -} - -func Test_IsServiceReference(t *testing.T) { - assert.True(t, IsServiceReference("did:nuts:bla")) - assert.False(t, IsServiceReference("nuts:did:not-a-did")) -} - -func Test_ValidateServiceReference(t *testing.T) { - t.Run("ok", func(t *testing.T) { - ref := ssi.MustParseURI("did:nuts:abc/serviceEndpoint?type=t") - err := ValidateServiceReference(ref) - assert.NoError(t, err) - }) - t.Run("error - invalid path", func(t *testing.T) { - ref := ssi.MustParseURI("did:nuts:abc/serviceEndpointWithInvalidPostfix?type=sajdklsad") - err := ValidateServiceReference(ref) - assert.ErrorAs(t, err, new(ServiceQueryError)) - assert.ErrorContains(t, err, "DID service query invalid") - assert.ErrorContains(t, err, "endpoint URI path must be /serviceEndpoint") - }) - t.Run("error - too many type params", func(t *testing.T) { - ref := ssi.MustParseURI("did:nuts:abc/serviceEndpoint?type=t1&type=t2") - err := ValidateServiceReference(ref) - assert.ErrorAs(t, err, new(ServiceQueryError)) - assert.ErrorContains(t, err, "DID service query invalid") - assert.ErrorContains(t, err, "endpoint URI with multiple type query parameters") - }) - t.Run("error - no type params", func(t *testing.T) { - ref := ssi.MustParseURI("did:nuts:abc/serviceEndpoint") - err := ValidateServiceReference(ref) - assert.ErrorAs(t, err, new(ServiceQueryError)) - assert.ErrorContains(t, err, "DID service query invalid") - assert.ErrorContains(t, err, "endpoint URI without type query parameter") - }) - t.Run("error - invalid params", func(t *testing.T) { - ref := ssi.MustParseURI("did:nuts:abc/serviceEndpoint?type=t1&someOther=not-allowed") - err := ValidateServiceReference(ref) - assert.ErrorAs(t, err, new(ServiceQueryError)) - assert.ErrorContains(t, err, "DID service query invalid") - assert.ErrorContains(t, err, "endpoint URI with query parameter other than type") - }) -} - -func TestGetDIDFromURL(t *testing.T) { - t.Run("just it", func(t *testing.T) { - actual, err := GetDIDFromURL("did:nuts:abc") - assert.NoError(t, err) - assert.Equal(t, "did:nuts:abc", actual.String()) - }) - t.Run("with path", func(t *testing.T) { - actual, err := GetDIDFromURL("did:nuts:abc/serviceEndpoint") - assert.NoError(t, err) - assert.Equal(t, "did:nuts:abc", actual.String()) - }) - t.Run("with fragment", func(t *testing.T) { - actual, err := GetDIDFromURL("did:nuts:abc#key-1") - assert.NoError(t, err) - assert.Equal(t, "did:nuts:abc", actual.String()) - }) - t.Run("with params", func(t *testing.T) { - actual, err := GetDIDFromURL("did:nuts:abc?foo=bar") - assert.NoError(t, err) - assert.Equal(t, "did:nuts:abc", actual.String()) - }) - t.Run("invalid DID", func(t *testing.T) { - _, err := GetDIDFromURL("https://example.com") - assert.Error(t, err) - }) -} diff --git a/vdr/didweb/web.go b/vdr/didweb/web.go index f975754f44..288aaebc94 100644 --- a/vdr/didweb/web.go +++ b/vdr/didweb/web.go @@ -23,7 +23,7 @@ import ( "errors" "fmt" "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "io" "mime" "net/http" @@ -33,7 +33,7 @@ import ( // MethodName is the DID method name used by did:web const MethodName = "web" -var _ types.DIDResolver = (*Resolver)(nil) +var _ resolver.DIDResolver = (*Resolver)(nil) // Resolver is a DID resolver for the did:web method. type Resolver struct { @@ -60,7 +60,7 @@ func NewResolver() *Resolver { } // Resolve implements the DIDResolver interface. -func (w Resolver) Resolve(id did.DID, _ *types.ResolveMetadata) (*did.Document, *types.DocumentMetadata, error) { +func (w Resolver) Resolve(id did.DID, _ *resolver.ResolveMetadata) (*did.Document, *resolver.DocumentMetadata, error) { if id.Method != "web" { return nil, nil, errors.New("DID is not did:web") } @@ -121,5 +121,5 @@ func (w Resolver) Resolve(id did.DID, _ *types.ResolveMetadata) (*did.Document, return nil, nil, fmt.Errorf("did:web document ID mismatch: %s != %s", document.ID, id.WithoutURL()) } - return &document, &types.DocumentMetadata{}, nil + return &document, &resolver.DocumentMetadata{}, nil } diff --git a/vdr/integration_test.go b/vdr/integration_test.go index 7cc5c093b0..f1f5f661b9 100644 --- a/vdr/integration_test.go +++ b/vdr/integration_test.go @@ -26,6 +26,8 @@ import ( "github.com/nuts-foundation/nuts-node/pki" "github.com/nuts-foundation/nuts-node/vdr/didnuts" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/url" "sync" "testing" @@ -42,7 +44,6 @@ import ( "github.com/nuts-foundation/nuts-node/storage" "github.com/nuts-foundation/nuts-node/test" "github.com/nuts-foundation/nuts-node/test/io" - "github.com/nuts-foundation/nuts-node/vdr/types" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -139,14 +140,14 @@ func TestVDRIntegration_Test(t *testing.T) { assert.NoError(t, err, "expected deactivation to succeed") - docB, _, err = ctx.didStore.Resolve(docB.ID, &types.ResolveMetadata{AllowDeactivated: true}) + docB, _, err = ctx.didStore.Resolve(docB.ID, &resolver.ResolveMetadata{AllowDeactivated: true}) assert.NoError(t, err) assert.Len(t, docB.CapabilityInvocation, 0, "expected document B to not have any CapabilityInvocation methods after deactivation") // try to deactivate the document again err = docUpdater.Deactivate(ctx.audit, docB.ID) - assert.ErrorIs(t, err, types.ErrDeactivated, + assert.ErrorIs(t, err, resolver.ErrDeactivated, "expected an error when trying to deactivate an already deactivated document") // try to update document A should fail since it no longer has an active controller @@ -225,9 +226,9 @@ func TestVDRIntegration_ReprocessEvents(t *testing.T) { } type testContext struct { - vdr *VDR + vdr *Module eventPublisher events.Event - docCreator types.DocCreator + docCreator management.DocCreator didStore didstore.Store cryptoInstance *crypto.Crypto audit context.Context diff --git a/vdr/interface.go b/vdr/interface.go new file mode 100644 index 0000000000..114c57eb63 --- /dev/null +++ b/vdr/interface.go @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package vdr + +import ( + "context" + "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" + "net/url" +) + +// VDR defines the public end facing methods for the Verifiable Data Registry. +type VDR interface { + management.DocumentOwner + management.DocCreator + management.DocUpdater + + // Resolver returns the resolver for getting the DID document for a DID. + Resolver() resolver.DIDResolver + + // ConflictedDocuments returns the DID Document and metadata of all documents with a conflict. + ConflictedDocuments() ([]did.Document, []resolver.DocumentMetadata, error) + + // DeriveWebDIDDocument returns the did:web equivalent of the given Nuts DID. If it doesn't exist or is not owned by this node it returns an error. + DeriveWebDIDDocument(ctx context.Context, baseURL url.URL, nutsDID did.DID) (*did.Document, error) +} diff --git a/vdr/didservice/finder.go b/vdr/management/finder.go similarity index 62% rename from vdr/didservice/finder.go rename to vdr/management/finder.go index ed925a2987..068c316050 100644 --- a/vdr/didservice/finder.go +++ b/vdr/management/finder.go @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Nuts community + * Copyright (C) 2023 Nuts community * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,18 +16,32 @@ * */ -package didservice +package management import ( "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "strings" "time" ) +// DocFinder is the interface that groups all methods for finding DID documents based on search conditions +type DocFinder interface { + Find(...Predicate) ([]did.Document, error) +} + +// Predicate is an interface for abstracting search options on DID documents +type Predicate interface { + // Match returns true if the given DID Document passes the predicate condition + Match(did.Document, resolver.DocumentMetadata) bool +} + +// DocIterator is the function type for iterating over the all current DID Documents in the store +type DocIterator func(doc did.Document, metadata resolver.DocumentMetadata) error + // ByServiceType returns a predicate that matches on service type // it only matches on DID Documents with a concrete endpoint (not starting with "did") -func ByServiceType(serviceType string) types.Predicate { +func ByServiceType(serviceType string) Predicate { return servicePredicate{serviceType: serviceType} } @@ -35,7 +49,7 @@ type servicePredicate struct { serviceType string } -func (s servicePredicate) Match(document did.Document, _ types.DocumentMetadata) bool { +func (s servicePredicate) Match(document did.Document, _ resolver.DocumentMetadata) bool { for _, service := range document.Service { if service.Type == s.serviceType { var nutsCommStr string @@ -48,7 +62,7 @@ func (s servicePredicate) Match(document did.Document, _ types.DocumentMetadata) } // ValidAt returns a predicate that matches on validity period. -func ValidAt(at time.Time) types.Predicate { +func ValidAt(at time.Time) Predicate { return validAtPredicate{validAt: at} } @@ -56,7 +70,7 @@ type validAtPredicate struct { validAt time.Time } -func (v validAtPredicate) Match(_ did.Document, metadata types.DocumentMetadata) bool { +func (v validAtPredicate) Match(_ did.Document, metadata resolver.DocumentMetadata) bool { if v.validAt.Before(metadata.Created) { return false } @@ -71,7 +85,7 @@ func (v validAtPredicate) Match(_ did.Document, metadata types.DocumentMetadata) } // IsActive returns a predicate that matches DID Documents that are not deactivated. -func IsActive() types.Predicate { +func IsActive() Predicate { return deactivatedPredicate{deactivated: false} } @@ -79,6 +93,6 @@ type deactivatedPredicate struct { deactivated bool } -func (d deactivatedPredicate) Match(_ did.Document, metadata types.DocumentMetadata) bool { +func (d deactivatedPredicate) Match(_ did.Document, metadata resolver.DocumentMetadata) bool { return d.deactivated == metadata.Deactivated } diff --git a/vdr/management/finder_mock.go b/vdr/management/finder_mock.go new file mode 100644 index 0000000000..ab666f7b96 --- /dev/null +++ b/vdr/management/finder_mock.go @@ -0,0 +1,96 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: vdr/management/finder.go +// +// Generated by this command: +// +// mockgen -destination=vdr/management/finder_mock.go -package=management -source=vdr/management/finder.go +// +// Package management is a generated GoMock package. +package management + +import ( + reflect "reflect" + + did "github.com/nuts-foundation/go-did/did" + resolver "github.com/nuts-foundation/nuts-node/vdr/resolver" + gomock "go.uber.org/mock/gomock" +) + +// MockDocFinder is a mock of DocFinder interface. +type MockDocFinder struct { + ctrl *gomock.Controller + recorder *MockDocFinderMockRecorder +} + +// MockDocFinderMockRecorder is the mock recorder for MockDocFinder. +type MockDocFinderMockRecorder struct { + mock *MockDocFinder +} + +// NewMockDocFinder creates a new mock instance. +func NewMockDocFinder(ctrl *gomock.Controller) *MockDocFinder { + mock := &MockDocFinder{ctrl: ctrl} + mock.recorder = &MockDocFinderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDocFinder) EXPECT() *MockDocFinderMockRecorder { + return m.recorder +} + +// Find mocks base method. +func (m *MockDocFinder) Find(arg0 ...Predicate) ([]did.Document, error) { + m.ctrl.T.Helper() + varargs := []any{} + for _, a := range arg0 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Find", varargs...) + ret0, _ := ret[0].([]did.Document) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Find indicates an expected call of Find. +func (mr *MockDocFinderMockRecorder) Find(arg0 ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockDocFinder)(nil).Find), arg0...) +} + +// MockPredicate is a mock of Predicate interface. +type MockPredicate struct { + ctrl *gomock.Controller + recorder *MockPredicateMockRecorder +} + +// MockPredicateMockRecorder is the mock recorder for MockPredicate. +type MockPredicateMockRecorder struct { + mock *MockPredicate +} + +// NewMockPredicate creates a new mock instance. +func NewMockPredicate(ctrl *gomock.Controller) *MockPredicate { + mock := &MockPredicate{ctrl: ctrl} + mock.recorder = &MockPredicateMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPredicate) EXPECT() *MockPredicateMockRecorder { + return m.recorder +} + +// Match mocks base method. +func (m *MockPredicate) Match(arg0 did.Document, arg1 resolver.DocumentMetadata) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Match", arg0, arg1) + ret0, _ := ret[0].(bool) + return ret0 +} + +// Match indicates an expected call of Match. +func (mr *MockPredicateMockRecorder) Match(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Match", reflect.TypeOf((*MockPredicate)(nil).Match), arg0, arg1) +} diff --git a/vdr/didservice/finder_test.go b/vdr/management/finder_test.go similarity index 78% rename from vdr/didservice/finder_test.go rename to vdr/management/finder_test.go index 7e5c60f8b5..0ed15f6158 100644 --- a/vdr/didservice/finder_test.go +++ b/vdr/management/finder_test.go @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Nuts community + * Copyright (C) 2023 Nuts community * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -13,42 +13,42 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * */ -package didservice +package management import ( - "testing" - "time" - "github.com/nuts-foundation/go-did/did" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "github.com/stretchr/testify/assert" + "testing" + "time" ) func TestIsActive(t *testing.T) { p := IsActive() t.Run("active", func(t *testing.T) { - assert.True(t, p.Match(did.Document{}, types.DocumentMetadata{Deactivated: false})) + assert.True(t, p.Match(did.Document{}, resolver.DocumentMetadata{Deactivated: false})) }) t.Run("deactivated", func(t *testing.T) { - assert.False(t, p.Match(did.Document{}, types.DocumentMetadata{Deactivated: true})) + assert.False(t, p.Match(did.Document{}, resolver.DocumentMetadata{Deactivated: true})) }) } func TestValidAt(t *testing.T) { now := time.Now() later := now.AddDate(0, 0, 1) - meta := types.DocumentMetadata{ + meta := resolver.DocumentMetadata{ Created: now.AddDate(0, 0, -1), Updated: &later, } t.Run("ok - latest", func(t *testing.T) { p := ValidAt(now) - assert.True(t, p.Match(did.Document{}, types.DocumentMetadata{Created: now.AddDate(0, 0, -1), Updated: nil})) + assert.True(t, p.Match(did.Document{}, resolver.DocumentMetadata{Created: now.AddDate(0, 0, -1), Updated: nil})) }) t.Run("ok - updated", func(t *testing.T) { @@ -79,7 +79,7 @@ func TestByServiceType(t *testing.T) { }, }} - assert.True(t, p.Match(doc, types.DocumentMetadata{})) + assert.True(t, p.Match(doc, resolver.DocumentMetadata{})) }) t.Run("ignore", func(t *testing.T) { @@ -90,6 +90,6 @@ func TestByServiceType(t *testing.T) { }, }} - assert.False(t, p.Match(docIgnore, types.DocumentMetadata{})) + assert.False(t, p.Match(docIgnore, resolver.DocumentMetadata{})) }) } diff --git a/vdr/management/management.go b/vdr/management/management.go new file mode 100644 index 0000000000..2bd7846ea6 --- /dev/null +++ b/vdr/management/management.go @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package management + +import ( + "context" + "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/nuts-node/crypto" +) + +// DocCreator is the interface that wraps the Create method +type DocCreator interface { + // Create creates a new DID document and returns it. + // The ID in the provided DID document will be ignored and a new one will be generated. + // If something goes wrong an error is returned. + // Implementors should generate private key and store it in a secure backend + Create(ctx context.Context, options DIDCreationOptions) (*did.Document, crypto.Key, error) +} + +// DocUpdater is the interface that defines functions that alter the state of a DID document +type DocUpdater interface { + // Update replaces the DID document identified by DID with the nextVersion + // If the DID Document is not found, ErrNotFound is returned + // If the DID Document is not managed by this node, ErrDIDNotManagedByThisNode is returned + Update(ctx context.Context, id did.DID, next did.Document) error +} + +// DocManipulator groups several higher level methods to alter the state of a DID document. +type DocManipulator interface { + // Deactivate deactivates a DID document + // Deactivation will be done in such a way that a DID doc cannot be used / activated anymore. + // Since the deactivation is definitive, no version is required + // If the DID Document is not found ErrNotFound is returned + // If the DID Document is not managed by this node, ErrDIDNotManagedByThisNode is returned + // If the DID Document is already deactivated ErrDeactivated is returned + Deactivate(ctx context.Context, id did.DID) error + + // RemoveVerificationMethod removes a VerificationMethod from a DID document. + // It accepts the id DID as identifier for the DID document. + // It accepts the kid DID as identifier for the VerificationMethod. + // It returns an ErrNotFound when the DID document could not be found. + // It returns an ErrNotFound when there is no VerificationMethod with the provided kid in the document. + // It returns an ErrDeactivated when the DID document has the deactivated state. + // It returns an ErrDIDNotManagedByThisNode if the DID document is not managed by this node. + RemoveVerificationMethod(ctx context.Context, id, keyID did.DID) error + + // AddVerificationMethod generates a new key and adds it, wrapped as a VerificationMethod, to a DID document. + // It accepts a DID as identifier for the DID document. + // It returns an ErrNotFound when the DID document could not be found. + // It returns an ErrDeactivated when the DID document has the deactivated state. + // It returns an ErrDIDNotManagedByThisNode if the DID document is not managed by this node. + AddVerificationMethod(ctx context.Context, id did.DID, keyUsage DIDKeyFlags) (*did.VerificationMethod, error) +} + +// DIDCreationOptions defines options for creating a DID Document. +type DIDCreationOptions struct { + // Controllers lists the DIDs that can control the new DID Document. If selfControl = true and controllers is not empty, + // the newly generated DID will be added to the list of controllers. + Controllers []did.DID + + // KeyFlags specifies for what purposes the generated key can be used + KeyFlags DIDKeyFlags + + // SelfControl indicates whether the generated DID Document can be altered with its own capabilityInvocation key. + // Defaults to true when not given. + SelfControl bool +} + +// DIDKeyFlags is a bitmask used for specifying for what purposes a key in a DID document can be used (a.k.a. Verification Method relationships). +type DIDKeyFlags uint + +// Is returns whether the specified DIDKeyFlags is enabled. +func (k DIDKeyFlags) Is(other DIDKeyFlags) bool { + return k&other == other +} + +const ( + // AssertionMethodUsage indicates if the generated key pair can be used for assertions. + AssertionMethodUsage DIDKeyFlags = 1 << iota + // AuthenticationUsage indicates if the generated key pair can be used for authentication. + AuthenticationUsage + // CapabilityDelegationUsage indicates if the generated key pair can be used for altering DID Documents. + CapabilityDelegationUsage + // CapabilityInvocationUsage indicates if the generated key pair can be used for capability invocations. + CapabilityInvocationUsage + // KeyAgreementUsage indicates if the generated key pair can be used for Key agreements. + KeyAgreementUsage +) + +// DocumentOwner is the interface for checking DID document ownership (presence of private keys). +type DocumentOwner interface { + // IsOwner returns true if the DID Document is owned by the node, meaning there are private keys present for the DID Document. + IsOwner(context.Context, did.DID) (bool, error) + // ListOwned returns all the DIDs owned by the node. + ListOwned(ctx context.Context) ([]did.DID, error) +} diff --git a/vdr/management/management_mock.go b/vdr/management/management_mock.go new file mode 100644 index 0000000000..29159293b4 --- /dev/null +++ b/vdr/management/management_mock.go @@ -0,0 +1,213 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: vdr/management/management.go +// +// Generated by this command: +// +// mockgen -destination=vdr/management/management_mock.go -package=management -source=vdr/management/management.go +// +// Package management is a generated GoMock package. +package management + +import ( + context "context" + reflect "reflect" + + did "github.com/nuts-foundation/go-did/did" + crypto "github.com/nuts-foundation/nuts-node/crypto" + gomock "go.uber.org/mock/gomock" +) + +// MockDocCreator is a mock of DocCreator interface. +type MockDocCreator struct { + ctrl *gomock.Controller + recorder *MockDocCreatorMockRecorder +} + +// MockDocCreatorMockRecorder is the mock recorder for MockDocCreator. +type MockDocCreatorMockRecorder struct { + mock *MockDocCreator +} + +// NewMockDocCreator creates a new mock instance. +func NewMockDocCreator(ctrl *gomock.Controller) *MockDocCreator { + mock := &MockDocCreator{ctrl: ctrl} + mock.recorder = &MockDocCreatorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDocCreator) EXPECT() *MockDocCreatorMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockDocCreator) Create(ctx context.Context, options DIDCreationOptions) (*did.Document, crypto.Key, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, options) + ret0, _ := ret[0].(*did.Document) + ret1, _ := ret[1].(crypto.Key) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Create indicates an expected call of Create. +func (mr *MockDocCreatorMockRecorder) Create(ctx, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockDocCreator)(nil).Create), ctx, options) +} + +// MockDocUpdater is a mock of DocUpdater interface. +type MockDocUpdater struct { + ctrl *gomock.Controller + recorder *MockDocUpdaterMockRecorder +} + +// MockDocUpdaterMockRecorder is the mock recorder for MockDocUpdater. +type MockDocUpdaterMockRecorder struct { + mock *MockDocUpdater +} + +// NewMockDocUpdater creates a new mock instance. +func NewMockDocUpdater(ctrl *gomock.Controller) *MockDocUpdater { + mock := &MockDocUpdater{ctrl: ctrl} + mock.recorder = &MockDocUpdaterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDocUpdater) EXPECT() *MockDocUpdaterMockRecorder { + return m.recorder +} + +// Update mocks base method. +func (m *MockDocUpdater) Update(ctx context.Context, id did.DID, next did.Document) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, id, next) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockDocUpdaterMockRecorder) Update(ctx, id, next any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockDocUpdater)(nil).Update), ctx, id, next) +} + +// MockDocManipulator is a mock of DocManipulator interface. +type MockDocManipulator struct { + ctrl *gomock.Controller + recorder *MockDocManipulatorMockRecorder +} + +// MockDocManipulatorMockRecorder is the mock recorder for MockDocManipulator. +type MockDocManipulatorMockRecorder struct { + mock *MockDocManipulator +} + +// NewMockDocManipulator creates a new mock instance. +func NewMockDocManipulator(ctrl *gomock.Controller) *MockDocManipulator { + mock := &MockDocManipulator{ctrl: ctrl} + mock.recorder = &MockDocManipulatorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDocManipulator) EXPECT() *MockDocManipulatorMockRecorder { + return m.recorder +} + +// AddVerificationMethod mocks base method. +func (m *MockDocManipulator) AddVerificationMethod(ctx context.Context, id did.DID, keyUsage DIDKeyFlags) (*did.VerificationMethod, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddVerificationMethod", ctx, id, keyUsage) + ret0, _ := ret[0].(*did.VerificationMethod) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddVerificationMethod indicates an expected call of AddVerificationMethod. +func (mr *MockDocManipulatorMockRecorder) AddVerificationMethod(ctx, id, keyUsage any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddVerificationMethod", reflect.TypeOf((*MockDocManipulator)(nil).AddVerificationMethod), ctx, id, keyUsage) +} + +// Deactivate mocks base method. +func (m *MockDocManipulator) Deactivate(ctx context.Context, id did.DID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Deactivate", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// Deactivate indicates an expected call of Deactivate. +func (mr *MockDocManipulatorMockRecorder) Deactivate(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deactivate", reflect.TypeOf((*MockDocManipulator)(nil).Deactivate), ctx, id) +} + +// RemoveVerificationMethod mocks base method. +func (m *MockDocManipulator) RemoveVerificationMethod(ctx context.Context, id, keyID did.DID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveVerificationMethod", ctx, id, keyID) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveVerificationMethod indicates an expected call of RemoveVerificationMethod. +func (mr *MockDocManipulatorMockRecorder) RemoveVerificationMethod(ctx, id, keyID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveVerificationMethod", reflect.TypeOf((*MockDocManipulator)(nil).RemoveVerificationMethod), ctx, id, keyID) +} + +// MockDocumentOwner is a mock of DocumentOwner interface. +type MockDocumentOwner struct { + ctrl *gomock.Controller + recorder *MockDocumentOwnerMockRecorder +} + +// MockDocumentOwnerMockRecorder is the mock recorder for MockDocumentOwner. +type MockDocumentOwnerMockRecorder struct { + mock *MockDocumentOwner +} + +// NewMockDocumentOwner creates a new mock instance. +func NewMockDocumentOwner(ctrl *gomock.Controller) *MockDocumentOwner { + mock := &MockDocumentOwner{ctrl: ctrl} + mock.recorder = &MockDocumentOwnerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDocumentOwner) EXPECT() *MockDocumentOwnerMockRecorder { + return m.recorder +} + +// IsOwner mocks base method. +func (m *MockDocumentOwner) IsOwner(arg0 context.Context, arg1 did.DID) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsOwner", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsOwner indicates an expected call of IsOwner. +func (mr *MockDocumentOwnerMockRecorder) IsOwner(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOwner", reflect.TypeOf((*MockDocumentOwner)(nil).IsOwner), arg0, arg1) +} + +// ListOwned mocks base method. +func (m *MockDocumentOwner) ListOwned(ctx context.Context) ([]did.DID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListOwned", ctx) + ret0, _ := ret[0].([]did.DID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListOwned indicates an expected call of ListOwned. +func (mr *MockDocumentOwnerMockRecorder) ListOwned(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOwned", reflect.TypeOf((*MockDocumentOwner)(nil).ListOwned), ctx) +} diff --git a/vdr/management/management_test.go b/vdr/management/management_test.go new file mode 100644 index 0000000000..7154587d16 --- /dev/null +++ b/vdr/management/management_test.go @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package management + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestKeyUsage_Is(t *testing.T) { + types := []DIDKeyFlags{AssertionMethodUsage, AuthenticationUsage, CapabilityDelegationUsage, CapabilityInvocationUsage, KeyAgreementUsage} + t.Run("one usage", func(t *testing.T) { + for _, usage := range types { + for _, other := range types { + if usage == other { + assert.True(t, usage.Is(other)) + assert.True(t, other.Is(usage)) // assert symmetry + } else { + assert.False(t, usage.Is(other)) + assert.False(t, other.Is(usage)) // assert symmetry + } + } + } + }) + t.Run("multiple usage", func(t *testing.T) { + value := AssertionMethodUsage | CapabilityDelegationUsage | KeyAgreementUsage + for _, other := range types { + switch other { + case AssertionMethodUsage: + fallthrough + case CapabilityDelegationUsage: + fallthrough + case KeyAgreementUsage: + assert.True(t, value.Is(other)) + default: + assert.False(t, value.Is(other)) + } + } + }) +} diff --git a/vdr/mock.go b/vdr/mock.go new file mode 100644 index 0000000000..dbe985c2c8 --- /dev/null +++ b/vdr/mock.go @@ -0,0 +1,149 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: vdr/interface.go +// +// Generated by this command: +// +// mockgen -destination=vdr/mock.go -package=vdr -source=vdr/interface.go +// +// Package vdr is a generated GoMock package. +package vdr + +import ( + context "context" + url "net/url" + reflect "reflect" + + did "github.com/nuts-foundation/go-did/did" + crypto "github.com/nuts-foundation/nuts-node/crypto" + management "github.com/nuts-foundation/nuts-node/vdr/management" + resolver "github.com/nuts-foundation/nuts-node/vdr/resolver" + gomock "go.uber.org/mock/gomock" +) + +// MockVDR is a mock of VDR interface. +type MockVDR struct { + ctrl *gomock.Controller + recorder *MockVDRMockRecorder +} + +// MockVDRMockRecorder is the mock recorder for MockVDR. +type MockVDRMockRecorder struct { + mock *MockVDR +} + +// NewMockVDR creates a new mock instance. +func NewMockVDR(ctrl *gomock.Controller) *MockVDR { + mock := &MockVDR{ctrl: ctrl} + mock.recorder = &MockVDRMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockVDR) EXPECT() *MockVDRMockRecorder { + return m.recorder +} + +// ConflictedDocuments mocks base method. +func (m *MockVDR) ConflictedDocuments() ([]did.Document, []resolver.DocumentMetadata, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConflictedDocuments") + ret0, _ := ret[0].([]did.Document) + ret1, _ := ret[1].([]resolver.DocumentMetadata) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ConflictedDocuments indicates an expected call of ConflictedDocuments. +func (mr *MockVDRMockRecorder) ConflictedDocuments() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConflictedDocuments", reflect.TypeOf((*MockVDR)(nil).ConflictedDocuments)) +} + +// Create mocks base method. +func (m *MockVDR) Create(ctx context.Context, options management.DIDCreationOptions) (*did.Document, crypto.Key, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, options) + ret0, _ := ret[0].(*did.Document) + ret1, _ := ret[1].(crypto.Key) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Create indicates an expected call of Create. +func (mr *MockVDRMockRecorder) Create(ctx, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockVDR)(nil).Create), ctx, options) +} + +// DeriveWebDIDDocument mocks base method. +func (m *MockVDR) DeriveWebDIDDocument(ctx context.Context, baseURL url.URL, nutsDID did.DID) (*did.Document, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeriveWebDIDDocument", ctx, baseURL, nutsDID) + ret0, _ := ret[0].(*did.Document) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeriveWebDIDDocument indicates an expected call of DeriveWebDIDDocument. +func (mr *MockVDRMockRecorder) DeriveWebDIDDocument(ctx, baseURL, nutsDID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeriveWebDIDDocument", reflect.TypeOf((*MockVDR)(nil).DeriveWebDIDDocument), ctx, baseURL, nutsDID) +} + +// IsOwner mocks base method. +func (m *MockVDR) IsOwner(arg0 context.Context, arg1 did.DID) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsOwner", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsOwner indicates an expected call of IsOwner. +func (mr *MockVDRMockRecorder) IsOwner(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOwner", reflect.TypeOf((*MockVDR)(nil).IsOwner), arg0, arg1) +} + +// ListOwned mocks base method. +func (m *MockVDR) ListOwned(ctx context.Context) ([]did.DID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListOwned", ctx) + ret0, _ := ret[0].([]did.DID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListOwned indicates an expected call of ListOwned. +func (mr *MockVDRMockRecorder) ListOwned(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOwned", reflect.TypeOf((*MockVDR)(nil).ListOwned), ctx) +} + +// Resolver mocks base method. +func (m *MockVDR) Resolver() resolver.DIDResolver { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Resolver") + ret0, _ := ret[0].(resolver.DIDResolver) + return ret0 +} + +// Resolver indicates an expected call of Resolver. +func (mr *MockVDRMockRecorder) Resolver() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resolver", reflect.TypeOf((*MockVDR)(nil).Resolver)) +} + +// Update mocks base method. +func (m *MockVDR) Update(ctx context.Context, id did.DID, next did.Document) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, id, next) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockVDRMockRecorder) Update(ctx, id, next any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockVDR)(nil).Update), ctx, id, next) +} diff --git a/vdr/types/common.go b/vdr/resolver/did.go similarity index 54% rename from vdr/types/common.go rename to vdr/resolver/did.go index 8661cc8864..9e1a93626e 100644 --- a/vdr/types/common.go +++ b/vdr/resolver/did.go @@ -1,6 +1,5 @@ /* - * Nuts node - * Copyright (C) 2021 Nuts community + * Copyright (C) 2023 Nuts community * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,20 +13,32 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * */ -package types +package resolver import ( "errors" - "time" - "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/crypto/hash" + "sync" + "time" ) -// ErrKeyNotFound is returned when a particular key or type of key is not found. -var ErrKeyNotFound = errors.New("key not found in DID document") +// DIDResolver is the interface for DID resolvers: the process of getting the backing document of a DID. +type DIDResolver interface { + // Resolve returns a DID Document for the provided DID. + // If metadata is not provided the latest version is returned. + // If metadata is provided then the result is filtered or scoped on that metadata. + // It returns ErrNotFound if there are no corresponding DID documents or when the DID Documents are disjoint with the provided ResolveMetadata + // It returns ErrDeactivated if the DID Document has been deactivated and metadata is unset or metadata.AllowDeactivated is false. + // It returns ErrNoActiveController if all of the DID Documents controllers have been deactivated and metadata is unset or metadata.AllowDeactivated is false. + Resolve(id did.DID, metadata *ResolveMetadata) (*did.Document, *DocumentMetadata, error) +} + +// ErrDIDMethodNotSupported is returned when a DID method is not supported by the DID resolver +var ErrDIDMethodNotSupported = errors.New("DID method not supported") // ErrDIDNotManagedByThisNode is returned when an operation needs the private key and if is not found on this host var ErrDIDNotManagedByThisNode = errors.New("DID document not managed by this node") @@ -41,18 +52,6 @@ var ErrDeactivated = deactivatedError{msg: "the DID document has been deactivate // ErrNoActiveController The DID supplied to the DID resolution does not have any active controllers. var ErrNoActiveController = deactivatedError{msg: "no active controllers for DID Document"} -// ErrDIDAlreadyExists is returned when a DID already exists. -var ErrDIDAlreadyExists = errors.New("DID document already exists in the store") - -// ErrDuplicateService is returned when a DID Document contains a multiple services with the same type -var ErrDuplicateService = errors.New("service type is duplicate") - -// ErrServiceNotFound is returned when the service is not found on a DID -var ErrServiceNotFound = errors.New("service not found in DID Document") - -// ErrServiceReferenceToDeep is returned when a service reference is chain is nested too deeply. -var ErrServiceReferenceToDeep = errors.New("service references are nested to deeply before resolving to a non-reference") - // BaseURLServiceType is type of the DID service which holds the base URL of the node's HTTP services, // exposed to other Nuts nodes. E.g. OpenID4VCI or OAuth2 endpoints. const BaseURLServiceType = "node-http-services-baseurl" @@ -118,40 +117,51 @@ type ResolveMetadata struct { AllowDeactivated bool } -// CompoundService is a service type that can be used as target for github.com/nuts-foundation/go-did/did/document.go#UnmarshalServiceEndpoint -type CompoundService map[string]string +var _ DIDResolver = &DIDResolverRouter{} -// DIDKeyFlags is a bitmask used for specifying for what purposes a key in a DID document can be used (a.k.a. Verification Method relationships). -type DIDKeyFlags uint +// DIDResolverRouter is a DID resolver that can route to different DID resolvers based on the DID method +type DIDResolverRouter struct { + resolvers sync.Map +} -// Is returns whether the specified DIDKeyFlags is enabled. -func (k DIDKeyFlags) Is(other DIDKeyFlags) bool { - return k&other == other +// Resolve looks up the right resolver for the given DID and delegates the resolution to it. +// If no resolver is registered for the given DID method, ErrDIDMethodNotSupported is returned. +func (r *DIDResolverRouter) Resolve(id did.DID, metadata *ResolveMetadata) (*did.Document, *DocumentMetadata, error) { + method := id.Method + didResolver, registered := r.resolvers.Load(method) + if !registered { + return nil, nil, ErrDIDMethodNotSupported + } + return didResolver.(DIDResolver).Resolve(id, metadata) } -const ( - // AssertionMethodUsage indicates if the generated key pair can be used for assertions. - AssertionMethodUsage DIDKeyFlags = 1 << iota - // AuthenticationUsage indicates if the generated key pair can be used for authentication. - AuthenticationUsage - // CapabilityDelegationUsage indicates if the generated key pair can be used for altering DID Documents. - CapabilityDelegationUsage - // CapabilityInvocationUsage indicates if the generated key pair can be used for capability invocations. - CapabilityInvocationUsage - // KeyAgreementUsage indicates if the generated key pair can be used for Key agreements. - KeyAgreementUsage -) +// Register registers a DID resolver for the given DID method. +func (r *DIDResolverRouter) Register(method string, resolver DIDResolver) { + r.resolvers.Store(method, resolver) +} -// DIDCreationOptions defines options for creating a DID Document. -type DIDCreationOptions struct { - // Controllers lists the DIDs that can control the new DID Document. If selfControl = true and controllers is not empty, - // the newly generated DID will be added to the list of controllers. - Controllers []did.DID +// IsFunctionalResolveError returns true if the given error indicates the DID or service not being found or invalid, +// e.g. because it is deactivated, referenced too deeply, etc. +func IsFunctionalResolveError(target error) bool { + return errors.Is(target, ErrNotFound) || + errors.Is(target, ErrDeactivated) || + errors.Is(target, ErrServiceNotFound) || + errors.Is(target, ErrNoActiveController) || + errors.Is(target, ErrServiceReferenceToDeep) || + errors.Is(target, did.InvalidDIDErr) || + errors.As(target, new(ServiceQueryError)) +} - // KeyFlags specifies for what purposes the generated key can be used - KeyFlags DIDKeyFlags +// GetDIDFromURL returns the DID from the given URL, stripping any query parameters, path segments and fragments. +func GetDIDFromURL(didURL string) (did.DID, error) { + parsed, err := did.ParseDIDURL(didURL) + if err != nil { + return did.DID{}, err + } + return parsed.WithoutURL(), nil +} - // SelfControl indicates whether the generated DID Document can be altered with its own capabilityInvocation key. - // Defaults to true when not given. - SelfControl bool +// IsDeactivated returns true if the DID.Document has already been deactivated +func IsDeactivated(document did.Document) bool { + return len(document.Controller) == 0 && len(document.CapabilityInvocation) == 0 } diff --git a/vdr/resolver/did_mock.go b/vdr/resolver/did_mock.go new file mode 100644 index 0000000000..6bf78a93b0 --- /dev/null +++ b/vdr/resolver/did_mock.go @@ -0,0 +1,55 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: vdr/resolver/did.go +// +// Generated by this command: +// +// mockgen -destination=vdr/resolver/did_mock.go -package=resolver -source=vdr/resolver/did.go +// +// Package resolver is a generated GoMock package. +package resolver + +import ( + reflect "reflect" + + did "github.com/nuts-foundation/go-did/did" + gomock "go.uber.org/mock/gomock" +) + +// MockDIDResolver is a mock of DIDResolver interface. +type MockDIDResolver struct { + ctrl *gomock.Controller + recorder *MockDIDResolverMockRecorder +} + +// MockDIDResolverMockRecorder is the mock recorder for MockDIDResolver. +type MockDIDResolverMockRecorder struct { + mock *MockDIDResolver +} + +// NewMockDIDResolver creates a new mock instance. +func NewMockDIDResolver(ctrl *gomock.Controller) *MockDIDResolver { + mock := &MockDIDResolver{ctrl: ctrl} + mock.recorder = &MockDIDResolverMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDIDResolver) EXPECT() *MockDIDResolverMockRecorder { + return m.recorder +} + +// Resolve mocks base method. +func (m *MockDIDResolver) Resolve(id did.DID, metadata *ResolveMetadata) (*did.Document, *DocumentMetadata, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Resolve", id, metadata) + ret0, _ := ret[0].(*did.Document) + ret1, _ := ret[1].(*DocumentMetadata) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Resolve indicates an expected call of Resolve. +func (mr *MockDIDResolverMockRecorder) Resolve(id, metadata any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resolve", reflect.TypeOf((*MockDIDResolver)(nil).Resolve), id, metadata) +} diff --git a/vdr/resolver/did_test.go b/vdr/resolver/did_test.go new file mode 100644 index 0000000000..98cabb7a4e --- /dev/null +++ b/vdr/resolver/did_test.go @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package resolver + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "errors" + "github.com/nuts-foundation/go-did" + "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/nuts-node/crypto/hash" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + "io" + "reflect" + "testing" + "time" +) + +func TestCopy(t *testing.T) { + timeBefore := time.Now().Add(time.Hour * -24) + timeNow := time.Now() + timeLater := time.Now().Add(time.Hour * +24) + h, _ := hash.ParseHex("452d9e89d5bd5d9225fb6daecd579e7388a166c7661ca04e47fd3cd8446e4620") + + meta := DocumentMetadata{ + Created: timeBefore, + Updated: &timeNow, + Hash: h, + PreviousHash: &h, + Deactivated: false, + SourceTransactions: []hash.SHA256Hash{h}, + } + numFields := 6 + + t.Run("returns error if metadata can be manipulated", func(t *testing.T) { + var metaCopy DocumentMetadata + + // Copy + metaCopy = meta.Copy() + assert.True(t, reflect.DeepEqual(meta, metaCopy)) + + // Updated + metaCopy = meta.Copy() + *metaCopy.Updated = timeLater + assert.False(t, reflect.DeepEqual(meta, metaCopy)) + + // Hash + metaCopy.Hash[0] = 0 + assert.NotEqual(t, metaCopy.Hash, meta.Hash, "Hash is not deep-copied") + + // PreviousHash + metaCopy.PreviousHash[0] = 0 + assert.NotEqual(t, metaCopy.PreviousHash, meta.PreviousHash, "PreviousHash is not deep-copied") + + // SourceTransactions + metaCopy.SourceTransactions[0] = hash.SHA256Hash{20} + assert.NotEqual(t, metaCopy.SourceTransactions, meta.SourceTransactions, "SourceTransactions is not deep-copied") + + // if this test fails, please make sure the Copy() method is updated as well! + assert.Equal(t, numFields, reflect.TypeOf(DocumentMetadata{}).NumField()) + }) +} + +func TestDocumentMetadata_IsConflicted(t *testing.T) { + t.Run("true", func(t *testing.T) { + assert.True(t, DocumentMetadata{SourceTransactions: []hash.SHA256Hash{hash.EmptyHash(), hash.EmptyHash()}}.IsConflicted()) + }) + + t.Run("false", func(t *testing.T) { + assert.False(t, DocumentMetadata{}.IsConflicted()) + }) +} +func Test_deactivatedError_Is(t *testing.T) { + assert.ErrorIs(t, ErrDeactivated, ErrDeactivated) + assert.ErrorIs(t, ErrNoActiveController, ErrDeactivated) + assert.NotErrorIs(t, io.EOF, ErrDeactivated) +} + +func newDidDoc() did.Document { + privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + id := did.MustParseDID("did:example:sakjsakldjsakld") + keyID := id + keyID.Fragment = "key-1" + vm, _ := did.NewVerificationMethod(keyID, ssi.JsonWebKey2020, id, privateKey.Public()) + doc := did.Document{ + ID: id, + } + doc.AddAssertionMethod(vm) + return doc +} + +func TestDIDResolverRouter_Resolve(t *testing.T) { + doc := newDidDoc() + t.Run("ok, 1 resolver", func(t *testing.T) { + ctrl := gomock.NewController(t) + resolver := NewMockDIDResolver(ctrl) + resolver.EXPECT().Resolve(doc.ID, gomock.Any()).Return(&doc, nil, nil) + router := &DIDResolverRouter{} + router.Register(doc.ID.Method, resolver) + + actual, _, err := router.Resolve(doc.ID, nil) + assert.NoError(t, err) + assert.Equal(t, &doc, actual) + }) + t.Run("ok, 2 resolvers", func(t *testing.T) { + ctrl := gomock.NewController(t) + otherResolver := NewMockDIDResolver(ctrl) + resolver := NewMockDIDResolver(ctrl) + resolver.EXPECT().Resolve(doc.ID, gomock.Any()).Return(&doc, nil, nil) + router := &DIDResolverRouter{} + router.Register(doc.ID.Method, resolver) + router.Register("test2", otherResolver) + + actual, _, err := router.Resolve(doc.ID, nil) + assert.NoError(t, err) + assert.Equal(t, &doc, actual) + }) + t.Run("error - resolver not found", func(t *testing.T) { + ctrl := gomock.NewController(t) + otherResolver := NewMockDIDResolver(ctrl) + router := &DIDResolverRouter{} + router.Register("other", otherResolver) + + actual, _, err := router.Resolve(doc.ID, nil) + assert.EqualError(t, err, "DID method not supported") + assert.Nil(t, actual) + }) +} + +func TestIsFunctionalResolveError(t *testing.T) { + assert.True(t, IsFunctionalResolveError(ErrNotFound)) + assert.True(t, IsFunctionalResolveError(ErrDeactivated)) + assert.True(t, IsFunctionalResolveError(ErrServiceNotFound)) + assert.True(t, IsFunctionalResolveError(ErrServiceReferenceToDeep)) + assert.True(t, IsFunctionalResolveError(ErrNoActiveController)) + assert.True(t, IsFunctionalResolveError(did.InvalidDIDErr)) + assert.True(t, IsFunctionalResolveError(ServiceQueryError{Err: errors.New("oops")})) + + assert.False(t, IsFunctionalResolveError(errors.New("some error"))) + assert.False(t, IsFunctionalResolveError(ErrDuplicateService)) +} + +func TestGetDIDFromURL(t *testing.T) { + t.Run("just it", func(t *testing.T) { + actual, err := GetDIDFromURL("did:nuts:abc") + assert.NoError(t, err) + assert.Equal(t, "did:nuts:abc", actual.String()) + }) + t.Run("with path", func(t *testing.T) { + actual, err := GetDIDFromURL("did:nuts:abc/serviceEndpoint") + assert.NoError(t, err) + assert.Equal(t, "did:nuts:abc", actual.String()) + }) + t.Run("with fragment", func(t *testing.T) { + actual, err := GetDIDFromURL("did:nuts:abc#key-1") + assert.NoError(t, err) + assert.Equal(t, "did:nuts:abc", actual.String()) + }) + t.Run("with params", func(t *testing.T) { + actual, err := GetDIDFromURL("did:nuts:abc?foo=bar") + assert.NoError(t, err) + assert.Equal(t, "did:nuts:abc", actual.String()) + }) + t.Run("invalid DID", func(t *testing.T) { + _, err := GetDIDFromURL("https://example.com") + assert.Error(t, err) + }) +} diff --git a/vdr/resolver/key.go b/vdr/resolver/key.go new file mode 100644 index 0000000000..26add0f0b7 --- /dev/null +++ b/vdr/resolver/key.go @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package resolver + +import ( + "crypto" + "errors" + "fmt" + "github.com/nuts-foundation/go-did" + "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/nuts-node/crypto/hash" + "time" +) + +// ErrKeyNotFound is returned when a particular key or type of key is not found. +var ErrKeyNotFound = errors.New("key not found in DID document") + +// NutsSigningKeyType defines the verification method relationship type for signing keys in Nuts DID Documents. +const NutsSigningKeyType = AssertionMethod + +// KeyResolver is the interface for resolving keys. +// This can be used for checking if a signing key is valid at a point in time or to just find a valid key for signing. +type KeyResolver interface { + // ResolveKeyByID looks up a specific key of the given RelationType and returns it as crypto.PublicKey. + // If multiple keys are valid, the first one is returned. + // An ErrKeyNotFound is returned when no key (of the specified type) is found. + ResolveKeyByID(keyID string, validAt *time.Time, relationType RelationType) (crypto.PublicKey, error) + // ResolveKey looks for a valid key of the given RelationType for the given DID, and returns its ID and the key itself. + // If multiple keys are valid, the first one is returned. + // An ErrKeyNotFound is returned when no key (of the specified type) is found. + ResolveKey(id did.DID, validAt *time.Time, relationType RelationType) (ssi.URI, crypto.PublicKey, error) +} + +// NutsKeyResolver is the interface for resolving keys from Nuts DID Documents, +// supporting Nuts-specific DID resolution parameters. +type NutsKeyResolver interface { + // ResolvePublicKey loads the key from a DID Document where the DID Document + // was created with one of the given tx refs + // It returns ErrKeyNotFound when the key could not be found in the DID Document. + // It returns ErrNotFound when the DID Document can't be found. + ResolvePublicKey(kid string, sourceTransactionsRefs []hash.SHA256Hash) (crypto.PublicKey, error) +} + +var _ KeyResolver = DIDKeyResolver{} + +// DIDKeyResolver implements the DIDKeyResolver interface that uses keys from resolved DIDs. +type DIDKeyResolver struct { + Resolver DIDResolver +} + +func (r DIDKeyResolver) ResolveKeyByID(keyID string, validAt *time.Time, relationType RelationType) (crypto.PublicKey, error) { + holder, err := GetDIDFromURL(keyID) + if err != nil { + return nil, fmt.Errorf("invalid key ID (id=%s): %w", keyID, err) + } + doc, _, err := r.Resolver.Resolve(holder, &ResolveMetadata{ + ResolveTime: validAt, + }) + if err != nil { + return nil, err + } + relationships, err := resolveRelationships(doc, relationType) + if err != nil { + return nil, err + } + for _, rel := range relationships { + if rel.ID.String() == keyID { + return rel.PublicKey() + } + } + return nil, ErrKeyNotFound +} + +func (r DIDKeyResolver) ResolveKey(id did.DID, validAt *time.Time, relationType RelationType) (ssi.URI, crypto.PublicKey, error) { + doc, _, err := r.Resolver.Resolve(id, &ResolveMetadata{ + ResolveTime: validAt, + }) + if err != nil { + return ssi.URI{}, nil, err + } + keys, err := resolveRelationships(doc, relationType) + if err != nil { + return ssi.URI{}, nil, err + } + if len(keys) == 0 { + return ssi.URI{}, nil, ErrKeyNotFound + } + publicKey, err := keys[0].PublicKey() + if err != nil { + return ssi.URI{}, nil, err + } + return keys[0].ID.URI(), publicKey, nil +} + +func resolveRelationships(doc *did.Document, relationType RelationType) (relationships did.VerificationRelationships, err error) { + switch relationType { + case Authentication: + return doc.Authentication, nil + case AssertionMethod: + return doc.AssertionMethod, nil + case KeyAgreement: + return doc.KeyAgreement, nil + case CapabilityInvocation: + return doc.CapabilityInvocation, nil + case CapabilityDelegation: + return doc.CapabilityDelegation, nil + default: + return nil, fmt.Errorf("unable to locate RelationType %v", relationType) + } +} + +// RelationType is the type that contains the different possible relationships between a DID Document and a VerificationMethod +// They are defined in the DID spec: https://www.w3.org/TR/did-core/#verification-relationships +type RelationType uint + +const ( + Authentication RelationType = iota + AssertionMethod RelationType = iota + KeyAgreement RelationType = iota + CapabilityInvocation RelationType = iota + CapabilityDelegation RelationType = iota +) diff --git a/vdr/resolver/key_mock.go b/vdr/resolver/key_mock.go new file mode 100644 index 0000000000..e62e4723f8 --- /dev/null +++ b/vdr/resolver/key_mock.go @@ -0,0 +1,112 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: vdr/resolver/key.go +// +// Generated by this command: +// +// mockgen -destination=vdr/resolver/key_mock.go -package=resolver -source=vdr/resolver/key.go +// +// Package resolver is a generated GoMock package. +package resolver + +import ( + crypto "crypto" + reflect "reflect" + time "time" + + ssi "github.com/nuts-foundation/go-did" + did "github.com/nuts-foundation/go-did/did" + hash "github.com/nuts-foundation/nuts-node/crypto/hash" + gomock "go.uber.org/mock/gomock" +) + +// MockKeyResolver is a mock of KeyResolver interface. +type MockKeyResolver struct { + ctrl *gomock.Controller + recorder *MockKeyResolverMockRecorder +} + +// MockKeyResolverMockRecorder is the mock recorder for MockKeyResolver. +type MockKeyResolverMockRecorder struct { + mock *MockKeyResolver +} + +// NewMockKeyResolver creates a new mock instance. +func NewMockKeyResolver(ctrl *gomock.Controller) *MockKeyResolver { + mock := &MockKeyResolver{ctrl: ctrl} + mock.recorder = &MockKeyResolverMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockKeyResolver) EXPECT() *MockKeyResolverMockRecorder { + return m.recorder +} + +// ResolveKey mocks base method. +func (m *MockKeyResolver) ResolveKey(id did.DID, validAt *time.Time, relationType RelationType) (ssi.URI, crypto.PublicKey, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResolveKey", id, validAt, relationType) + ret0, _ := ret[0].(ssi.URI) + ret1, _ := ret[1].(crypto.PublicKey) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ResolveKey indicates an expected call of ResolveKey. +func (mr *MockKeyResolverMockRecorder) ResolveKey(id, validAt, relationType any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveKey", reflect.TypeOf((*MockKeyResolver)(nil).ResolveKey), id, validAt, relationType) +} + +// ResolveKeyByID mocks base method. +func (m *MockKeyResolver) ResolveKeyByID(keyID string, validAt *time.Time, relationType RelationType) (crypto.PublicKey, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResolveKeyByID", keyID, validAt, relationType) + ret0, _ := ret[0].(crypto.PublicKey) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ResolveKeyByID indicates an expected call of ResolveKeyByID. +func (mr *MockKeyResolverMockRecorder) ResolveKeyByID(keyID, validAt, relationType any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveKeyByID", reflect.TypeOf((*MockKeyResolver)(nil).ResolveKeyByID), keyID, validAt, relationType) +} + +// MockNutsKeyResolver is a mock of NutsKeyResolver interface. +type MockNutsKeyResolver struct { + ctrl *gomock.Controller + recorder *MockNutsKeyResolverMockRecorder +} + +// MockNutsKeyResolverMockRecorder is the mock recorder for MockNutsKeyResolver. +type MockNutsKeyResolverMockRecorder struct { + mock *MockNutsKeyResolver +} + +// NewMockNutsKeyResolver creates a new mock instance. +func NewMockNutsKeyResolver(ctrl *gomock.Controller) *MockNutsKeyResolver { + mock := &MockNutsKeyResolver{ctrl: ctrl} + mock.recorder = &MockNutsKeyResolverMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockNutsKeyResolver) EXPECT() *MockNutsKeyResolverMockRecorder { + return m.recorder +} + +// ResolvePublicKey mocks base method. +func (m *MockNutsKeyResolver) ResolvePublicKey(kid string, sourceTransactionsRefs []hash.SHA256Hash) (crypto.PublicKey, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResolvePublicKey", kid, sourceTransactionsRefs) + ret0, _ := ret[0].(crypto.PublicKey) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ResolvePublicKey indicates an expected call of ResolvePublicKey. +func (mr *MockNutsKeyResolverMockRecorder) ResolvePublicKey(kid, sourceTransactionsRefs any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolvePublicKey", reflect.TypeOf((*MockNutsKeyResolver)(nil).ResolvePublicKey), kid, sourceTransactionsRefs) +} diff --git a/vdr/resolver/key_test.go b/vdr/resolver/key_test.go new file mode 100644 index 0000000000..a103ae0fdc --- /dev/null +++ b/vdr/resolver/key_test.go @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package resolver + +import ( + "github.com/nuts-foundation/go-did/did" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "testing" +) + +func TestKeyResolver_ResolveKey(t *testing.T) { + ctrl := gomock.NewController(t) + resolver := NewMockDIDResolver(ctrl) + keyResolver := DIDKeyResolver{Resolver: resolver} + + doc := newDidDoc() + resolver.EXPECT().Resolve(doc.ID, gomock.Any()).AnyTimes().Return(&doc, nil, nil) + + t.Run("ok - it finds the key", func(t *testing.T) { + keyId, key, err := keyResolver.ResolveKey(doc.ID, nil, AssertionMethod) + require.NoError(t, err) + assert.Equal(t, doc.VerificationMethod[0].ID.URI(), keyId) + assert.NotNil(t, key) + }) + + t.Run("error - document not found", func(t *testing.T) { + unknownDID := did.MustParseDID("did:example:123") + resolver.EXPECT().Resolve(unknownDID, gomock.Any()).Return(nil, nil, ErrNotFound) + keyId, key, err := keyResolver.ResolveKey(unknownDID, nil, AssertionMethod) + assert.EqualError(t, err, "unable to find the DID document") + assert.Empty(t, keyId) + assert.Nil(t, key) + }) + + t.Run("error - key not found", func(t *testing.T) { + keyId, key, err := keyResolver.ResolveKey(did.MustParseDIDURL(doc.ID.String()), nil, CapabilityDelegation) + assert.EqualError(t, err, "key not found in DID document") + assert.Empty(t, keyId) + assert.Nil(t, key) + }) + + t.Run("error - unknown relationship type", func(t *testing.T) { + keyId, key, err := keyResolver.ResolveKey(doc.ID, nil, 1000) + assert.EqualError(t, err, "unable to locate RelationType 1000") + assert.Empty(t, keyId) + assert.Nil(t, key) + }) +} + +func TestKeyResolver_ResolveKeyByID(t *testing.T) { + ctrl := gomock.NewController(t) + resolver := NewMockDIDResolver(ctrl) + keyResolver := DIDKeyResolver{Resolver: resolver} + doc := newDidDoc() + resolver.EXPECT().Resolve(doc.ID, gomock.Any()).AnyTimes().Return(&doc, nil, nil) + keyID := doc.VerificationMethod[0].ID + + t.Run("ok - it finds the key", func(t *testing.T) { + key, err := keyResolver.ResolveKeyByID(keyID.String(), nil, AssertionMethod) + assert.NoError(t, err) + assert.NotNil(t, key) + }) + + t.Run("error - invalid key ID", func(t *testing.T) { + key, err := keyResolver.ResolveKeyByID("abcdef", nil, AssertionMethod) + assert.EqualError(t, err, "invalid key ID (id=abcdef): invalid DID") + assert.Nil(t, key) + }) + + t.Run("error - document not found", func(t *testing.T) { + unknownDID := did.MustParseDIDURL("did:example:123") + resolver.EXPECT().Resolve(unknownDID, gomock.Any()).Return(nil, nil, ErrNotFound) + key, err := keyResolver.ResolveKeyByID(unknownDID.String()+"#456", nil, AssertionMethod) + assert.EqualError(t, err, "unable to find the DID document") + assert.Nil(t, key) + }) + + t.Run("error - key not found", func(t *testing.T) { + key, err := keyResolver.ResolveKeyByID(did.MustParseDIDURL(doc.ID.String()+"#123").String(), nil, AssertionMethod) + assert.EqualError(t, err, "key not found in DID document") + assert.Nil(t, key) + }) + + t.Run("error - unknown relationship type", func(t *testing.T) { + key, err := keyResolver.ResolveKeyByID(keyID.String(), nil, 1000) + assert.EqualError(t, err, "unable to locate RelationType 1000") + assert.Nil(t, key) + }) +} diff --git a/vdr/resolver/service.go b/vdr/resolver/service.go new file mode 100644 index 0000000000..c560521985 --- /dev/null +++ b/vdr/resolver/service.go @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package resolver + +import ( + "errors" + "fmt" + "github.com/nuts-foundation/go-did" + "github.com/nuts-foundation/go-did/did" + "strings" +) + +// ErrDuplicateService is returned when a DID Document contains a multiple services with the same type +var ErrDuplicateService = errors.New("service type is duplicate") + +// ErrServiceNotFound is returned when the service is not found on a DID +var ErrServiceNotFound = errors.New("service not found in DID Document") + +// ErrServiceReferenceToDeep is returned when a service reference is chain is nested too deeply. +var ErrServiceReferenceToDeep = errors.New("service references are nested to deeply before resolving to a non-reference") + +// ServiceResolver allows looking up DID document services, following references. +type ServiceResolver interface { + // Resolve looks up the DID document of the specified query and then tries to find the service with the specified type. + // The query must be in the form of a service query, e.g. `did:nuts:12345/serviceEndpoint?type=some-type`. + // The maxDepth indicates how deep references are followed. If maxDepth = 0, no references are followed (and an error is returned if the given query resolves to a reference). + // If the DID document or service is not found, a reference can't be resolved or the references exceed maxDepth, an error is returned. + Resolve(query ssi.URI, maxDepth int) (did.Service, error) + + // ResolveEx tries to resolve a DID service from the given endpoint URI, following references (URIs that begin with 'did:'). + // When the endpoint is a reference it resolves it up until the (per spec) max reference depth. When resolving a reference it recursively calls itself with depth + 1. + // The documentCache map is used to avoid resolving the same document over and over again, which might be a (slightly more) expensive operation. + ResolveEx(endpoint ssi.URI, depth int, maxDepth int, documentCache map[string]*did.Document) (did.Service, error) +} + +// DefaultMaxServiceReferenceDepth holds the default max. allowed depth for DID service references. +const DefaultMaxServiceReferenceDepth = 5 + +// DIDServiceResolver is a wrapper around a DID store that allows resolving services, following references. +type DIDServiceResolver struct { + Resolver DIDResolver +} + +func (s DIDServiceResolver) Resolve(query ssi.URI, maxDepth int) (did.Service, error) { + return s.ResolveEx(query, 0, maxDepth, map[string]*did.Document{}) +} + +func (s DIDServiceResolver) ResolveEx(endpoint ssi.URI, depth int, maxDepth int, documentCache map[string]*did.Document) (did.Service, error) { + if depth >= maxDepth { + return did.Service{}, ErrServiceReferenceToDeep + } + + referencedDID, err := GetDIDFromURL(endpoint.String()) + if err != nil { + // Shouldn't happen, because only DID URLs are passed? + return did.Service{}, err + } + var document *did.Document + if document = documentCache[referencedDID.String()]; document == nil { + document, _, err = s.Resolver.Resolve(referencedDID, nil) + if err != nil { + return did.Service{}, err + } + documentCache[referencedDID.String()] = document + } + + var service *did.Service + for _, curr := range document.Service { + if curr.Type == endpoint.Query().Get(serviceTypeQueryParameter) { + // If there are multiple services with the same type the document is conflicted. + // This can happen temporarily during a service update (delete old, add new). + // Both endpoints are likely to be active in the timeframe that the conflict exists, so picking the first entry is preferred for availability over an error. + service = &curr + break + } + } + if service == nil { + return did.Service{}, ErrServiceNotFound + } + + var endpointURL string + if service.UnmarshalServiceEndpoint(&endpointURL) == nil { + // Service endpoint is a string, if it's a reference we need to resolve it + if IsServiceReference(endpointURL) { + // Looks like a reference, recurse + resolvedEndpointURI, err := ssi.ParseURI(endpointURL) + if err != nil { + return did.Service{}, err + } + err = ValidateServiceReference(*resolvedEndpointURI) + if err != nil { + return did.Service{}, err + } + return s.ResolveEx(*resolvedEndpointURI, depth+1, maxDepth, documentCache) + } + } + return *service, nil +} + +const serviceTypeQueryParameter = "type" +const serviceEndpointPath = "/serviceEndpoint" + +// MakeServiceReference creates a service reference, which can be used as query when looking up services. +func MakeServiceReference(subjectDID did.DID, serviceType string) ssi.URI { + ref := subjectDID.URI() + ref.Opaque += serviceEndpointPath + ref.Fragment = "" + ref.RawQuery = fmt.Sprintf("%s=%s", serviceTypeQueryParameter, serviceType) + return ref +} + +// IsServiceReference checks whether the given endpoint string looks like a service reference (e.g. did:nuts:1234/serviceType?type=HelloWorld). +func IsServiceReference(endpoint string) bool { + return strings.HasPrefix(endpoint, "did:") +} + +// ServiceQueryError denies the query based on validation constraints. +type ServiceQueryError struct { + Err error // cause +} + +// Error implements the error interface. +func (e ServiceQueryError) Error() string { + return "DID service query invalid: " + e.Err.Error() +} + +// Unwrap implements the errors.Unwrap convention. +func (e ServiceQueryError) Unwrap() error { return e.Err } + +// ValidateServiceReference checks whether the given URI matches the format for a service reference. +func ValidateServiceReference(endpointURI ssi.URI) error { + // Parse it as DID URL since DID URLs are rootless and thus opaque (RFC 3986), meaning the path will be part of the URI body, rather than the URI path. + // For DID URLs the path is parsed properly. + didEndpointURL, err := did.ParseDIDURL(endpointURI.String()) + if err != nil { + return ServiceQueryError{err} + } + + if "/"+didEndpointURL.Path != serviceEndpointPath { + return ServiceQueryError{errors.New("endpoint URI path must be " + serviceEndpointPath)} + } + + q := endpointURI.Query() + switch len(q[serviceTypeQueryParameter]) { + case 1: + break // good + case 0: + return ServiceQueryError{errors.New("endpoint URI without " + serviceTypeQueryParameter + " query parameter")} + default: + return ServiceQueryError{errors.New("endpoint URI with multiple " + serviceTypeQueryParameter + " query parameters")} + } + + // “Other query parameters, paths or fragments SHALL NOT be used.” + // — RFC006, subsection 4.2 + if len(q) > 1 { + return ServiceQueryError{errors.New("endpoint URI with query parameter other than " + serviceTypeQueryParameter)} + } + + return nil +} diff --git a/vdr/resolver/service_mock.go b/vdr/resolver/service_mock.go new file mode 100644 index 0000000000..5c6f0ee04b --- /dev/null +++ b/vdr/resolver/service_mock.go @@ -0,0 +1,70 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: vdr/resolver/service.go +// +// Generated by this command: +// +// mockgen -destination=vdr/resolver/service_mock.go -package=resolver -source=vdr/resolver/service.go +// +// Package resolver is a generated GoMock package. +package resolver + +import ( + reflect "reflect" + + ssi "github.com/nuts-foundation/go-did" + did "github.com/nuts-foundation/go-did/did" + gomock "go.uber.org/mock/gomock" +) + +// MockServiceResolver is a mock of ServiceResolver interface. +type MockServiceResolver struct { + ctrl *gomock.Controller + recorder *MockServiceResolverMockRecorder +} + +// MockServiceResolverMockRecorder is the mock recorder for MockServiceResolver. +type MockServiceResolverMockRecorder struct { + mock *MockServiceResolver +} + +// NewMockServiceResolver creates a new mock instance. +func NewMockServiceResolver(ctrl *gomock.Controller) *MockServiceResolver { + mock := &MockServiceResolver{ctrl: ctrl} + mock.recorder = &MockServiceResolverMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockServiceResolver) EXPECT() *MockServiceResolverMockRecorder { + return m.recorder +} + +// Resolve mocks base method. +func (m *MockServiceResolver) Resolve(query ssi.URI, maxDepth int) (did.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Resolve", query, maxDepth) + ret0, _ := ret[0].(did.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Resolve indicates an expected call of Resolve. +func (mr *MockServiceResolverMockRecorder) Resolve(query, maxDepth any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resolve", reflect.TypeOf((*MockServiceResolver)(nil).Resolve), query, maxDepth) +} + +// ResolveEx mocks base method. +func (m *MockServiceResolver) ResolveEx(endpoint ssi.URI, depth, maxDepth int, documentCache map[string]*did.Document) (did.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResolveEx", endpoint, depth, maxDepth, documentCache) + ret0, _ := ret[0].(did.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ResolveEx indicates an expected call of ResolveEx. +func (mr *MockServiceResolverMockRecorder) ResolveEx(endpoint, depth, maxDepth, documentCache any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveEx", reflect.TypeOf((*MockServiceResolver)(nil).ResolveEx), endpoint, depth, maxDepth, documentCache) +} diff --git a/vdr/resolver/service_test.go b/vdr/resolver/service_test.go new file mode 100644 index 0000000000..ebce4f4737 --- /dev/null +++ b/vdr/resolver/service_test.go @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package resolver + +import ( + "fmt" + ssi "github.com/nuts-foundation/go-did" + "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/nuts-node/crypto/hash" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + "testing" +) + +func Test_MakeServiceReference(t *testing.T) { + d, _ := did.ParseDID("did:nuts:abc") + assert.Equal(t, "did:nuts:abc/serviceEndpoint?type=hello", MakeServiceReference(*d, "hello").String()) +} + +func Test_IsServiceReference(t *testing.T) { + assert.True(t, IsServiceReference("did:nuts:bla")) + assert.False(t, IsServiceReference("nuts:did:not-a-did")) +} + +func Test_ValidateServiceReference(t *testing.T) { + t.Run("ok", func(t *testing.T) { + ref := ssi.MustParseURI("did:nuts:abc/serviceEndpoint?type=t") + err := ValidateServiceReference(ref) + assert.NoError(t, err) + }) + t.Run("error - invalid path", func(t *testing.T) { + ref := ssi.MustParseURI("did:nuts:abc/serviceEndpointWithInvalidPostfix?type=sajdklsad") + err := ValidateServiceReference(ref) + assert.ErrorAs(t, err, new(ServiceQueryError)) + assert.ErrorContains(t, err, "DID service query invalid") + assert.ErrorContains(t, err, "endpoint URI path must be /serviceEndpoint") + }) + t.Run("error - too many type params", func(t *testing.T) { + ref := ssi.MustParseURI("did:nuts:abc/serviceEndpoint?type=t1&type=t2") + err := ValidateServiceReference(ref) + assert.ErrorAs(t, err, new(ServiceQueryError)) + assert.ErrorContains(t, err, "DID service query invalid") + assert.ErrorContains(t, err, "endpoint URI with multiple type query parameters") + }) + t.Run("error - no type params", func(t *testing.T) { + ref := ssi.MustParseURI("did:nuts:abc/serviceEndpoint") + err := ValidateServiceReference(ref) + assert.ErrorAs(t, err, new(ServiceQueryError)) + assert.ErrorContains(t, err, "DID service query invalid") + assert.ErrorContains(t, err, "endpoint URI without type query parameter") + }) + t.Run("error - invalid params", func(t *testing.T) { + ref := ssi.MustParseURI("did:nuts:abc/serviceEndpoint?type=t1&someOther=not-allowed") + err := ValidateServiceReference(ref) + assert.ErrorAs(t, err, new(ServiceQueryError)) + assert.ErrorContains(t, err, "DID service query invalid") + assert.ErrorContains(t, err, "endpoint URI with query parameter other than type") + }) +} + +func TestServiceResolver_Resolve(t *testing.T) { + meta := &DocumentMetadata{Hash: hash.EmptyHash()} + + didA, _ := did.ParseDID("did:nuts:A") + didB, _ := did.ParseDID("did:nuts:B") + + serviceID := ssi.MustParseURI(fmt.Sprintf("%s#1", didA.String())) + docA := did.Document{ + Context: []ssi.URI{did.DIDContextV1URI()}, + ID: *didA, + Service: []did.Service{{ + ID: serviceID, + Type: "hello", + ServiceEndpoint: "http://hello", + }}, + } + docB := did.Document{ + Context: []ssi.URI{did.DIDContextV1URI()}, + ID: *didA, + Service: []did.Service{ + { + Type: "simple", + ServiceEndpoint: "http://world", + }, + { + Type: "cyclic-ref", + ServiceEndpoint: didB.String() + "/serviceEndpoint?type=cyclic-ref", + }, + { + Type: "invalid-ref", + ServiceEndpoint: didB.String() + "?type=invalid-ref", + }, + { + Type: "external", + ServiceEndpoint: MakeServiceReference(docA.ID, "hello").String(), + }, + }, + } + + t.Run("ok", func(t *testing.T) { + ctrl := gomock.NewController(t) + resolver := NewMockDIDResolver(ctrl) + + resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(&docB, meta, nil) + + actual, err := DIDServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "simple"), DefaultMaxServiceReferenceDepth) + + assert.NoError(t, err) + assert.Equal(t, docB.Service[0], actual) + }) + t.Run("ok - external", func(t *testing.T) { + ctrl := gomock.NewController(t) + resolver := NewMockDIDResolver(ctrl) + + resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(&docB, meta, nil) + resolver.EXPECT().Resolve(*didA, nil).MinTimes(1).Return(&docA, meta, nil) + + actual, err := DIDServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "external"), DefaultMaxServiceReferenceDepth) + + assert.NoError(t, err) + assert.Equal(t, docA.Service[0], actual) + }) + t.Run("error - cyclic reference (yields refs too deep)", func(t *testing.T) { + ctrl := gomock.NewController(t) + resolver := NewMockDIDResolver(ctrl) + + resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(&docB, meta, nil) + + actual, err := DIDServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "cyclic-ref"), DefaultMaxServiceReferenceDepth) + + assert.EqualError(t, err, "service references are nested to deeply before resolving to a non-reference") + assert.Empty(t, actual) + }) + t.Run("error - invalid ref", func(t *testing.T) { + ctrl := gomock.NewController(t) + resolver := NewMockDIDResolver(ctrl) + + resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(&docB, meta, nil) + + actual, err := DIDServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "invalid-ref"), DefaultMaxServiceReferenceDepth) + + assert.EqualError(t, err, "DID service query invalid: endpoint URI path must be /serviceEndpoint") + assert.Empty(t, actual) + }) + t.Run("error - service not found", func(t *testing.T) { + ctrl := gomock.NewController(t) + resolver := NewMockDIDResolver(ctrl) + + resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(&docB, meta, nil) + + actual, err := DIDServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "non-existent"), DefaultMaxServiceReferenceDepth) + + assert.EqualError(t, err, "service not found in DID Document") + assert.Empty(t, actual) + }) + t.Run("error - DID not found", func(t *testing.T) { + ctrl := gomock.NewController(t) + resolver := NewMockDIDResolver(ctrl) + + resolver.EXPECT().Resolve(*didB, nil).MinTimes(1).Return(nil, nil, ErrNotFound) + + actual, err := DIDServiceResolver{Resolver: resolver}.Resolve(MakeServiceReference(*didB, "non-existent"), DefaultMaxServiceReferenceDepth) + + assert.EqualError(t, err, "unable to find the DID document") + assert.Empty(t, actual) + }) +} diff --git a/vdr/types/common_test.go b/vdr/types/common_test.go deleted file mode 100644 index a40db4d924..0000000000 --- a/vdr/types/common_test.go +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Nuts node - * Copyright (C) 2021 Nuts community - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package types - -import ( - "io" - "reflect" - "testing" - "time" - - "github.com/nuts-foundation/nuts-node/crypto/hash" - "github.com/stretchr/testify/assert" -) - -func TestCopy(t *testing.T) { - timeBefore := time.Now().Add(time.Hour * -24) - timeNow := time.Now() - timeLater := time.Now().Add(time.Hour * +24) - h, _ := hash.ParseHex("452d9e89d5bd5d9225fb6daecd579e7388a166c7661ca04e47fd3cd8446e4620") - - meta := DocumentMetadata{ - Created: timeBefore, - Updated: &timeNow, - Hash: h, - PreviousHash: &h, - Deactivated: false, - SourceTransactions: []hash.SHA256Hash{h}, - } - numFields := 6 - - t.Run("returns error if metadata can be manipulated", func(t *testing.T) { - var metaCopy DocumentMetadata - - // Copy - metaCopy = meta.Copy() - assert.True(t, reflect.DeepEqual(meta, metaCopy)) - - // Updated - metaCopy = meta.Copy() - *metaCopy.Updated = timeLater - assert.False(t, reflect.DeepEqual(meta, metaCopy)) - - // Hash - metaCopy.Hash[0] = 0 - assert.NotEqual(t, metaCopy.Hash, meta.Hash, "Hash is not deep-copied") - - // PreviousHash - metaCopy.PreviousHash[0] = 0 - assert.NotEqual(t, metaCopy.PreviousHash, meta.PreviousHash, "PreviousHash is not deep-copied") - - // SourceTransactions - metaCopy.SourceTransactions[0] = hash.SHA256Hash{20} - assert.NotEqual(t, metaCopy.SourceTransactions, meta.SourceTransactions, "SourceTransactions is not deep-copied") - - // if this test fails, please make sure the Copy() method is updated as well! - assert.Equal(t, numFields, reflect.TypeOf(DocumentMetadata{}).NumField()) - }) -} - -func TestDocumentMetadata_IsConflicted(t *testing.T) { - t.Run("true", func(t *testing.T) { - assert.True(t, DocumentMetadata{SourceTransactions: []hash.SHA256Hash{hash.EmptyHash(), hash.EmptyHash()}}.IsConflicted()) - }) - - t.Run("false", func(t *testing.T) { - assert.False(t, DocumentMetadata{}.IsConflicted()) - }) -} - -func TestKeyUsage_Is(t *testing.T) { - types := []DIDKeyFlags{AssertionMethodUsage, AuthenticationUsage, CapabilityDelegationUsage, CapabilityInvocationUsage, KeyAgreementUsage} - t.Run("one usage", func(t *testing.T) { - for _, usage := range types { - for _, other := range types { - if usage == other { - assert.True(t, usage.Is(other)) - assert.True(t, other.Is(usage)) // assert symmetry - } else { - assert.False(t, usage.Is(other)) - assert.False(t, other.Is(usage)) // assert symmetry - } - } - } - }) - t.Run("multiple usage", func(t *testing.T) { - value := AssertionMethodUsage | CapabilityDelegationUsage | KeyAgreementUsage - for _, other := range types { - switch other { - case AssertionMethodUsage: - fallthrough - case CapabilityDelegationUsage: - fallthrough - case KeyAgreementUsage: - assert.True(t, value.Is(other)) - default: - assert.False(t, value.Is(other)) - } - } - }) -} - -func Test_deactivatedError_Is(t *testing.T) { - assert.ErrorIs(t, ErrDeactivated, ErrDeactivated) - assert.ErrorIs(t, ErrNoActiveController, ErrDeactivated) - assert.NotErrorIs(t, io.EOF, ErrDeactivated) -} diff --git a/vdr/types/interface.go b/vdr/types/interface.go deleted file mode 100644 index 249bfa875f..0000000000 --- a/vdr/types/interface.go +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Nuts node - * Copyright (C) 2021 Nuts community - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package types - -import ( - "context" - "crypto" - "errors" - "github.com/nuts-foundation/nuts-node/crypto/hash" - "net/url" - "time" - - ssi "github.com/nuts-foundation/go-did" - "github.com/nuts-foundation/go-did/did" - crypto2 "github.com/nuts-foundation/nuts-node/crypto" -) - -// DIDResolver is the interface for DID resolvers: the process of getting the backing document of a DID. -type DIDResolver interface { - // Resolve returns a DID Document for the provided DID. - // If metadata is not provided the latest version is returned. - // If metadata is provided then the result is filtered or scoped on that metadata. - // It returns ErrNotFound if there are no corresponding DID documents or when the DID Documents are disjoint with the provided ResolveMetadata - // It returns ErrDeactivated if the DID Document has been deactivated and metadata is unset or metadata.AllowDeactivated is false. - // It returns ErrNoActiveController if all of the DID Documents controllers have been deactivated and metadata is unset or metadata.AllowDeactivated is false. - Resolve(id did.DID, metadata *ResolveMetadata) (*did.Document, *DocumentMetadata, error) -} - -// Predicate is an interface for abstracting search options on DID documents -type Predicate interface { - // Match returns true if the given DID Document passes the predicate condition - Match(did.Document, DocumentMetadata) bool -} - -// DocFinder is the interface that groups all methods for finding DID documents based on search conditions -type DocFinder interface { - Find(...Predicate) ([]did.Document, error) -} - -// DocCreator is the interface that wraps the Create method -type DocCreator interface { - // Create creates a new DID document and returns it. - // The ID in the provided DID document will be ignored and a new one will be generated. - // If something goes wrong an error is returned. - // Implementors should generate private key and store it in a secure backend - Create(ctx context.Context, options DIDCreationOptions) (*did.Document, crypto2.Key, error) -} - -// DocWriter is the interface that groups al the DID Document write methods -type DocWriter interface { - // Write writes a DID Document. - // Returns ErrDIDAlreadyExists when DID already exists - // When a document already exists, the Update should be used instead - Write(document did.Document, metadata DocumentMetadata) error -} - -// DocUpdater is the interface that defines functions that alter the state of a DID document -type DocUpdater interface { - // Update replaces the DID document identified by DID with the nextVersion - // If the DID Document is not found, ErrNotFound is returned - // If the DID Document is not managed by this node, ErrDIDNotManagedByThisNode is returned - Update(ctx context.Context, id did.DID, next did.Document) error -} - -// KeyResolver is the interface for resolving keys. -// This can be used for checking if a signing key is valid at a point in time or to just find a valid key for signing. -type KeyResolver interface { - // ResolveKeyByID looks up a specific key of the given RelationType and returns it as crypto.PublicKey. - // If multiple keys are valid, the first one is returned. - // An ErrKeyNotFound is returned when no key (of the specified type) is found. - ResolveKeyByID(keyID string, validAt *time.Time, relationType RelationType) (crypto.PublicKey, error) - // ResolveKey looks for a valid key of the given RelationType for the given DID, and returns its ID and the key itself. - // If multiple keys are valid, the first one is returned. - // An ErrKeyNotFound is returned when no key (of the specified type) is found. - ResolveKey(id did.DID, validAt *time.Time, relationType RelationType) (ssi.URI, crypto.PublicKey, error) -} - -// NutsSigningKeyType defines the verification method relationship type for signing keys in Nuts DID Documents. -const NutsSigningKeyType = AssertionMethod - -// NutsKeyResolver is the interface for resolving keys from Nuts DID Documents, -// supporting Nuts-specific DID resolution parameters. -type NutsKeyResolver interface { - // ResolvePublicKey loads the key from a DID Document where the DID Document - // was created with one of the given tx refs - // It returns ErrKeyNotFound when the key could not be found in the DID Document. - // It returns ErrNotFound when the DID Document can't be found. - ResolvePublicKey(kid string, sourceTransactionsRefs []hash.SHA256Hash) (crypto.PublicKey, error) -} - -// DocIterator is the function type for iterating over the all current DID Documents in the store -type DocIterator func(doc did.Document, metadata DocumentMetadata) error - -// VDR defines the public end facing methods for the Verifiable Data Registry. -type VDR interface { - DocumentOwner - DocCreator - DocUpdater - - // Resolver returns the resolver for getting the DID document for a DID. - Resolver() DIDResolver - - // ConflictedDocuments returns the DID Document and metadata of all documents with a conflict. - ConflictedDocuments() ([]did.Document, []DocumentMetadata, error) - - // DeriveWebDIDDocument returns the did:web equivalent of the given Nuts DID. If it doesn't exist or is not owned by this node it returns an error. - DeriveWebDIDDocument(ctx context.Context, baseURL url.URL, nutsDID did.DID) (*did.Document, error) -} - -// DocumentOwner is the interface for checking DID document ownership (presence of private keys). -type DocumentOwner interface { - // IsOwner returns true if the DID Document is owned by the node, meaning there are private keys present for the DID Document. - IsOwner(context.Context, did.DID) (bool, error) - // ListOwned returns all the DIDs owned by the node. - ListOwned(ctx context.Context) ([]did.DID, error) -} - -// DocManipulator groups several higher level methods to alter the state of a DID document. -type DocManipulator interface { - // Deactivate deactivates a DID document - // Deactivation will be done in such a way that a DID doc cannot be used / activated anymore. - // Since the deactivation is definitive, no version is required - // If the DID Document is not found ErrNotFound is returned - // If the DID Document is not managed by this node, ErrDIDNotManagedByThisNode is returned - // If the DID Document is already deactivated ErrDeactivated is returned - Deactivate(ctx context.Context, id did.DID) error - - // RemoveVerificationMethod removes a VerificationMethod from a DID document. - // It accepts the id DID as identifier for the DID document. - // It accepts the kid DID as identifier for the VerificationMethod. - // It returns an ErrNotFound when the DID document could not be found. - // It returns an ErrNotFound when there is no VerificationMethod with the provided kid in the document. - // It returns an ErrDeactivated when the DID document has the deactivated state. - // It returns an ErrDIDNotManagedByThisNode if the DID document is not managed by this node. - RemoveVerificationMethod(ctx context.Context, id, keyID did.DID) error - - // AddVerificationMethod generates a new key and adds it, wrapped as a VerificationMethod, to a DID document. - // It accepts a DID as identifier for the DID document. - // It returns an ErrNotFound when the DID document could not be found. - // It returns an ErrDeactivated when the DID document has the deactivated state. - // It returns an ErrDIDNotManagedByThisNode if the DID document is not managed by this node. - AddVerificationMethod(ctx context.Context, id did.DID, keyUsage DIDKeyFlags) (*did.VerificationMethod, error) -} - -// ErrDIDMethodNotSupported is returned when a DID method is not supported by the DID resolver -var ErrDIDMethodNotSupported = errors.New("DID method not supported") - -// RelationType is the type that contains the different possible relationships between a DID Document and a VerificationMethod -// They are defined in the DID spec: https://www.w3.org/TR/did-core/#verification-relationships -type RelationType uint - -const ( - Authentication RelationType = iota - AssertionMethod RelationType = iota - KeyAgreement RelationType = iota - CapabilityInvocation RelationType = iota - CapabilityDelegation RelationType = iota -) - -// ServiceResolver allows looking up DID document services, following references. -type ServiceResolver interface { - // Resolve looks up the DID document of the specified query and then tries to find the service with the specified type. - // The query must be in the form of a service query, e.g. `did:nuts:12345/serviceEndpoint?type=some-type`. - // The maxDepth indicates how deep references are followed. If maxDepth = 0, no references are followed (and an error is returned if the given query resolves to a reference). - // If the DID document or service is not found, a reference can't be resolved or the references exceed maxDepth, an error is returned. - Resolve(query ssi.URI, maxDepth int) (did.Service, error) - - // ResolveEx tries to resolve a DID service from the given endpoint URI, following references (URIs that begin with 'did:'). - // When the endpoint is a reference it resolves it up until the (per spec) max reference depth. When resolving a reference it recursively calls itself with depth + 1. - // The documentCache map is used to avoid resolving the same document over and over again, which might be a (slightly more) expensive operation. - ResolveEx(endpoint ssi.URI, depth int, maxDepth int, documentCache map[string]*did.Document) (did.Service, error) -} diff --git a/vdr/types/mock.go b/vdr/types/mock.go deleted file mode 100644 index 1cb83e7606..0000000000 --- a/vdr/types/mock.go +++ /dev/null @@ -1,646 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: vdr/types/interface.go -// -// Generated by this command: -// -// mockgen -destination=vdr/types/mock.go -package=types -source=vdr/types/interface.go -self_package github.com/nuts-foundation/nuts-node/vdr/types --imports did=github.com/nuts-foundation/go-did/did -// -// Package types is a generated GoMock package. -package types - -import ( - context "context" - crypto "crypto" - url "net/url" - reflect "reflect" - time "time" - - ssi "github.com/nuts-foundation/go-did" - did "github.com/nuts-foundation/go-did/did" - crypto0 "github.com/nuts-foundation/nuts-node/crypto" - hash "github.com/nuts-foundation/nuts-node/crypto/hash" - gomock "go.uber.org/mock/gomock" -) - -// MockDIDResolver is a mock of DIDResolver interface. -type MockDIDResolver struct { - ctrl *gomock.Controller - recorder *MockDIDResolverMockRecorder -} - -// MockDIDResolverMockRecorder is the mock recorder for MockDIDResolver. -type MockDIDResolverMockRecorder struct { - mock *MockDIDResolver -} - -// NewMockDIDResolver creates a new mock instance. -func NewMockDIDResolver(ctrl *gomock.Controller) *MockDIDResolver { - mock := &MockDIDResolver{ctrl: ctrl} - mock.recorder = &MockDIDResolverMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDIDResolver) EXPECT() *MockDIDResolverMockRecorder { - return m.recorder -} - -// Resolve mocks base method. -func (m *MockDIDResolver) Resolve(id did.DID, metadata *ResolveMetadata) (*did.Document, *DocumentMetadata, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Resolve", id, metadata) - ret0, _ := ret[0].(*did.Document) - ret1, _ := ret[1].(*DocumentMetadata) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Resolve indicates an expected call of Resolve. -func (mr *MockDIDResolverMockRecorder) Resolve(id, metadata any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resolve", reflect.TypeOf((*MockDIDResolver)(nil).Resolve), id, metadata) -} - -// MockPredicate is a mock of Predicate interface. -type MockPredicate struct { - ctrl *gomock.Controller - recorder *MockPredicateMockRecorder -} - -// MockPredicateMockRecorder is the mock recorder for MockPredicate. -type MockPredicateMockRecorder struct { - mock *MockPredicate -} - -// NewMockPredicate creates a new mock instance. -func NewMockPredicate(ctrl *gomock.Controller) *MockPredicate { - mock := &MockPredicate{ctrl: ctrl} - mock.recorder = &MockPredicateMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockPredicate) EXPECT() *MockPredicateMockRecorder { - return m.recorder -} - -// Match mocks base method. -func (m *MockPredicate) Match(arg0 did.Document, arg1 DocumentMetadata) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Match", arg0, arg1) - ret0, _ := ret[0].(bool) - return ret0 -} - -// Match indicates an expected call of Match. -func (mr *MockPredicateMockRecorder) Match(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Match", reflect.TypeOf((*MockPredicate)(nil).Match), arg0, arg1) -} - -// MockDocFinder is a mock of DocFinder interface. -type MockDocFinder struct { - ctrl *gomock.Controller - recorder *MockDocFinderMockRecorder -} - -// MockDocFinderMockRecorder is the mock recorder for MockDocFinder. -type MockDocFinderMockRecorder struct { - mock *MockDocFinder -} - -// NewMockDocFinder creates a new mock instance. -func NewMockDocFinder(ctrl *gomock.Controller) *MockDocFinder { - mock := &MockDocFinder{ctrl: ctrl} - mock.recorder = &MockDocFinderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDocFinder) EXPECT() *MockDocFinderMockRecorder { - return m.recorder -} - -// Find mocks base method. -func (m *MockDocFinder) Find(arg0 ...Predicate) ([]did.Document, error) { - m.ctrl.T.Helper() - varargs := []any{} - for _, a := range arg0 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Find", varargs...) - ret0, _ := ret[0].([]did.Document) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Find indicates an expected call of Find. -func (mr *MockDocFinderMockRecorder) Find(arg0 ...any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockDocFinder)(nil).Find), arg0...) -} - -// MockDocCreator is a mock of DocCreator interface. -type MockDocCreator struct { - ctrl *gomock.Controller - recorder *MockDocCreatorMockRecorder -} - -// MockDocCreatorMockRecorder is the mock recorder for MockDocCreator. -type MockDocCreatorMockRecorder struct { - mock *MockDocCreator -} - -// NewMockDocCreator creates a new mock instance. -func NewMockDocCreator(ctrl *gomock.Controller) *MockDocCreator { - mock := &MockDocCreator{ctrl: ctrl} - mock.recorder = &MockDocCreatorMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDocCreator) EXPECT() *MockDocCreatorMockRecorder { - return m.recorder -} - -// Create mocks base method. -func (m *MockDocCreator) Create(ctx context.Context, options DIDCreationOptions) (*did.Document, crypto0.Key, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", ctx, options) - ret0, _ := ret[0].(*did.Document) - ret1, _ := ret[1].(crypto0.Key) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Create indicates an expected call of Create. -func (mr *MockDocCreatorMockRecorder) Create(ctx, options any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockDocCreator)(nil).Create), ctx, options) -} - -// MockDocWriter is a mock of DocWriter interface. -type MockDocWriter struct { - ctrl *gomock.Controller - recorder *MockDocWriterMockRecorder -} - -// MockDocWriterMockRecorder is the mock recorder for MockDocWriter. -type MockDocWriterMockRecorder struct { - mock *MockDocWriter -} - -// NewMockDocWriter creates a new mock instance. -func NewMockDocWriter(ctrl *gomock.Controller) *MockDocWriter { - mock := &MockDocWriter{ctrl: ctrl} - mock.recorder = &MockDocWriterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDocWriter) EXPECT() *MockDocWriterMockRecorder { - return m.recorder -} - -// Write mocks base method. -func (m *MockDocWriter) Write(document did.Document, metadata DocumentMetadata) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Write", document, metadata) - ret0, _ := ret[0].(error) - return ret0 -} - -// Write indicates an expected call of Write. -func (mr *MockDocWriterMockRecorder) Write(document, metadata any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockDocWriter)(nil).Write), document, metadata) -} - -// MockDocUpdater is a mock of DocUpdater interface. -type MockDocUpdater struct { - ctrl *gomock.Controller - recorder *MockDocUpdaterMockRecorder -} - -// MockDocUpdaterMockRecorder is the mock recorder for MockDocUpdater. -type MockDocUpdaterMockRecorder struct { - mock *MockDocUpdater -} - -// NewMockDocUpdater creates a new mock instance. -func NewMockDocUpdater(ctrl *gomock.Controller) *MockDocUpdater { - mock := &MockDocUpdater{ctrl: ctrl} - mock.recorder = &MockDocUpdaterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDocUpdater) EXPECT() *MockDocUpdaterMockRecorder { - return m.recorder -} - -// Update mocks base method. -func (m *MockDocUpdater) Update(ctx context.Context, id did.DID, next did.Document) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", ctx, id, next) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockDocUpdaterMockRecorder) Update(ctx, id, next any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockDocUpdater)(nil).Update), ctx, id, next) -} - -// MockKeyResolver is a mock of KeyResolver interface. -type MockKeyResolver struct { - ctrl *gomock.Controller - recorder *MockKeyResolverMockRecorder -} - -// MockKeyResolverMockRecorder is the mock recorder for MockKeyResolver. -type MockKeyResolverMockRecorder struct { - mock *MockKeyResolver -} - -// NewMockKeyResolver creates a new mock instance. -func NewMockKeyResolver(ctrl *gomock.Controller) *MockKeyResolver { - mock := &MockKeyResolver{ctrl: ctrl} - mock.recorder = &MockKeyResolverMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockKeyResolver) EXPECT() *MockKeyResolverMockRecorder { - return m.recorder -} - -// ResolveKey mocks base method. -func (m *MockKeyResolver) ResolveKey(id did.DID, validAt *time.Time, relationType RelationType) (ssi.URI, crypto.PublicKey, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ResolveKey", id, validAt, relationType) - ret0, _ := ret[0].(ssi.URI) - ret1, _ := ret[1].(crypto.PublicKey) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ResolveKey indicates an expected call of ResolveKey. -func (mr *MockKeyResolverMockRecorder) ResolveKey(id, validAt, relationType any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveKey", reflect.TypeOf((*MockKeyResolver)(nil).ResolveKey), id, validAt, relationType) -} - -// ResolveKeyByID mocks base method. -func (m *MockKeyResolver) ResolveKeyByID(keyID string, validAt *time.Time, relationType RelationType) (crypto.PublicKey, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ResolveKeyByID", keyID, validAt, relationType) - ret0, _ := ret[0].(crypto.PublicKey) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ResolveKeyByID indicates an expected call of ResolveKeyByID. -func (mr *MockKeyResolverMockRecorder) ResolveKeyByID(keyID, validAt, relationType any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveKeyByID", reflect.TypeOf((*MockKeyResolver)(nil).ResolveKeyByID), keyID, validAt, relationType) -} - -// MockNutsKeyResolver is a mock of NutsKeyResolver interface. -type MockNutsKeyResolver struct { - ctrl *gomock.Controller - recorder *MockNutsKeyResolverMockRecorder -} - -// MockNutsKeyResolverMockRecorder is the mock recorder for MockNutsKeyResolver. -type MockNutsKeyResolverMockRecorder struct { - mock *MockNutsKeyResolver -} - -// NewMockNutsKeyResolver creates a new mock instance. -func NewMockNutsKeyResolver(ctrl *gomock.Controller) *MockNutsKeyResolver { - mock := &MockNutsKeyResolver{ctrl: ctrl} - mock.recorder = &MockNutsKeyResolverMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockNutsKeyResolver) EXPECT() *MockNutsKeyResolverMockRecorder { - return m.recorder -} - -// ResolvePublicKey mocks base method. -func (m *MockNutsKeyResolver) ResolvePublicKey(kid string, sourceTransactionsRefs []hash.SHA256Hash) (crypto.PublicKey, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ResolvePublicKey", kid, sourceTransactionsRefs) - ret0, _ := ret[0].(crypto.PublicKey) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ResolvePublicKey indicates an expected call of ResolvePublicKey. -func (mr *MockNutsKeyResolverMockRecorder) ResolvePublicKey(kid, sourceTransactionsRefs any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolvePublicKey", reflect.TypeOf((*MockNutsKeyResolver)(nil).ResolvePublicKey), kid, sourceTransactionsRefs) -} - -// MockVDR is a mock of VDR interface. -type MockVDR struct { - ctrl *gomock.Controller - recorder *MockVDRMockRecorder -} - -// MockVDRMockRecorder is the mock recorder for MockVDR. -type MockVDRMockRecorder struct { - mock *MockVDR -} - -// NewMockVDR creates a new mock instance. -func NewMockVDR(ctrl *gomock.Controller) *MockVDR { - mock := &MockVDR{ctrl: ctrl} - mock.recorder = &MockVDRMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockVDR) EXPECT() *MockVDRMockRecorder { - return m.recorder -} - -// ConflictedDocuments mocks base method. -func (m *MockVDR) ConflictedDocuments() ([]did.Document, []DocumentMetadata, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConflictedDocuments") - ret0, _ := ret[0].([]did.Document) - ret1, _ := ret[1].([]DocumentMetadata) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ConflictedDocuments indicates an expected call of ConflictedDocuments. -func (mr *MockVDRMockRecorder) ConflictedDocuments() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConflictedDocuments", reflect.TypeOf((*MockVDR)(nil).ConflictedDocuments)) -} - -// Create mocks base method. -func (m *MockVDR) Create(ctx context.Context, options DIDCreationOptions) (*did.Document, crypto0.Key, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", ctx, options) - ret0, _ := ret[0].(*did.Document) - ret1, _ := ret[1].(crypto0.Key) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Create indicates an expected call of Create. -func (mr *MockVDRMockRecorder) Create(ctx, options any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockVDR)(nil).Create), ctx, options) -} - -// DeriveWebDIDDocument mocks base method. -func (m *MockVDR) DeriveWebDIDDocument(ctx context.Context, baseURL url.URL, nutsDID did.DID) (*did.Document, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeriveWebDIDDocument", ctx, baseURL, nutsDID) - ret0, _ := ret[0].(*did.Document) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DeriveWebDIDDocument indicates an expected call of DeriveWebDIDDocument. -func (mr *MockVDRMockRecorder) DeriveWebDIDDocument(ctx, baseURL, nutsDID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeriveWebDIDDocument", reflect.TypeOf((*MockVDR)(nil).DeriveWebDIDDocument), ctx, baseURL, nutsDID) -} - -// IsOwner mocks base method. -func (m *MockVDR) IsOwner(arg0 context.Context, arg1 did.DID) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsOwner", arg0, arg1) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsOwner indicates an expected call of IsOwner. -func (mr *MockVDRMockRecorder) IsOwner(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOwner", reflect.TypeOf((*MockVDR)(nil).IsOwner), arg0, arg1) -} - -// ListOwned mocks base method. -func (m *MockVDR) ListOwned(ctx context.Context) ([]did.DID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListOwned", ctx) - ret0, _ := ret[0].([]did.DID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListOwned indicates an expected call of ListOwned. -func (mr *MockVDRMockRecorder) ListOwned(ctx any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOwned", reflect.TypeOf((*MockVDR)(nil).ListOwned), ctx) -} - -// Resolver mocks base method. -func (m *MockVDR) Resolver() DIDResolver { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Resolver") - ret0, _ := ret[0].(DIDResolver) - return ret0 -} - -// Resolver indicates an expected call of Resolver. -func (mr *MockVDRMockRecorder) Resolver() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resolver", reflect.TypeOf((*MockVDR)(nil).Resolver)) -} - -// Update mocks base method. -func (m *MockVDR) Update(ctx context.Context, id did.DID, next did.Document) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", ctx, id, next) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update. -func (mr *MockVDRMockRecorder) Update(ctx, id, next any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockVDR)(nil).Update), ctx, id, next) -} - -// MockDocumentOwner is a mock of DocumentOwner interface. -type MockDocumentOwner struct { - ctrl *gomock.Controller - recorder *MockDocumentOwnerMockRecorder -} - -// MockDocumentOwnerMockRecorder is the mock recorder for MockDocumentOwner. -type MockDocumentOwnerMockRecorder struct { - mock *MockDocumentOwner -} - -// NewMockDocumentOwner creates a new mock instance. -func NewMockDocumentOwner(ctrl *gomock.Controller) *MockDocumentOwner { - mock := &MockDocumentOwner{ctrl: ctrl} - mock.recorder = &MockDocumentOwnerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDocumentOwner) EXPECT() *MockDocumentOwnerMockRecorder { - return m.recorder -} - -// IsOwner mocks base method. -func (m *MockDocumentOwner) IsOwner(arg0 context.Context, arg1 did.DID) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsOwner", arg0, arg1) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsOwner indicates an expected call of IsOwner. -func (mr *MockDocumentOwnerMockRecorder) IsOwner(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOwner", reflect.TypeOf((*MockDocumentOwner)(nil).IsOwner), arg0, arg1) -} - -// ListOwned mocks base method. -func (m *MockDocumentOwner) ListOwned(ctx context.Context) ([]did.DID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListOwned", ctx) - ret0, _ := ret[0].([]did.DID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListOwned indicates an expected call of ListOwned. -func (mr *MockDocumentOwnerMockRecorder) ListOwned(ctx any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOwned", reflect.TypeOf((*MockDocumentOwner)(nil).ListOwned), ctx) -} - -// MockDocManipulator is a mock of DocManipulator interface. -type MockDocManipulator struct { - ctrl *gomock.Controller - recorder *MockDocManipulatorMockRecorder -} - -// MockDocManipulatorMockRecorder is the mock recorder for MockDocManipulator. -type MockDocManipulatorMockRecorder struct { - mock *MockDocManipulator -} - -// NewMockDocManipulator creates a new mock instance. -func NewMockDocManipulator(ctrl *gomock.Controller) *MockDocManipulator { - mock := &MockDocManipulator{ctrl: ctrl} - mock.recorder = &MockDocManipulatorMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDocManipulator) EXPECT() *MockDocManipulatorMockRecorder { - return m.recorder -} - -// AddVerificationMethod mocks base method. -func (m *MockDocManipulator) AddVerificationMethod(ctx context.Context, id did.DID, keyUsage DIDKeyFlags) (*did.VerificationMethod, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AddVerificationMethod", ctx, id, keyUsage) - ret0, _ := ret[0].(*did.VerificationMethod) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// AddVerificationMethod indicates an expected call of AddVerificationMethod. -func (mr *MockDocManipulatorMockRecorder) AddVerificationMethod(ctx, id, keyUsage any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddVerificationMethod", reflect.TypeOf((*MockDocManipulator)(nil).AddVerificationMethod), ctx, id, keyUsage) -} - -// Deactivate mocks base method. -func (m *MockDocManipulator) Deactivate(ctx context.Context, id did.DID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Deactivate", ctx, id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Deactivate indicates an expected call of Deactivate. -func (mr *MockDocManipulatorMockRecorder) Deactivate(ctx, id any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deactivate", reflect.TypeOf((*MockDocManipulator)(nil).Deactivate), ctx, id) -} - -// RemoveVerificationMethod mocks base method. -func (m *MockDocManipulator) RemoveVerificationMethod(ctx context.Context, id, keyID did.DID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemoveVerificationMethod", ctx, id, keyID) - ret0, _ := ret[0].(error) - return ret0 -} - -// RemoveVerificationMethod indicates an expected call of RemoveVerificationMethod. -func (mr *MockDocManipulatorMockRecorder) RemoveVerificationMethod(ctx, id, keyID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveVerificationMethod", reflect.TypeOf((*MockDocManipulator)(nil).RemoveVerificationMethod), ctx, id, keyID) -} - -// MockServiceResolver is a mock of ServiceResolver interface. -type MockServiceResolver struct { - ctrl *gomock.Controller - recorder *MockServiceResolverMockRecorder -} - -// MockServiceResolverMockRecorder is the mock recorder for MockServiceResolver. -type MockServiceResolverMockRecorder struct { - mock *MockServiceResolver -} - -// NewMockServiceResolver creates a new mock instance. -func NewMockServiceResolver(ctrl *gomock.Controller) *MockServiceResolver { - mock := &MockServiceResolver{ctrl: ctrl} - mock.recorder = &MockServiceResolverMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockServiceResolver) EXPECT() *MockServiceResolverMockRecorder { - return m.recorder -} - -// Resolve mocks base method. -func (m *MockServiceResolver) Resolve(query ssi.URI, maxDepth int) (did.Service, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Resolve", query, maxDepth) - ret0, _ := ret[0].(did.Service) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Resolve indicates an expected call of Resolve. -func (mr *MockServiceResolverMockRecorder) Resolve(query, maxDepth any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resolve", reflect.TypeOf((*MockServiceResolver)(nil).Resolve), query, maxDepth) -} - -// ResolveEx mocks base method. -func (m *MockServiceResolver) ResolveEx(endpoint ssi.URI, depth, maxDepth int, documentCache map[string]*did.Document) (did.Service, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ResolveEx", endpoint, depth, maxDepth, documentCache) - ret0, _ := ret[0].(did.Service) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ResolveEx indicates an expected call of ResolveEx. -func (mr *MockServiceResolverMockRecorder) ResolveEx(endpoint, depth, maxDepth, documentCache any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveEx", reflect.TypeOf((*MockServiceResolver)(nil).ResolveEx), endpoint, depth, maxDepth, documentCache) -} diff --git a/vdr/vdr.go b/vdr/vdr.go index abe73d56b3..257f146d77 100644 --- a/vdr/vdr.go +++ b/vdr/vdr.go @@ -40,37 +40,37 @@ import ( "github.com/nuts-foundation/nuts-node/vdr/didjwk" "github.com/nuts-foundation/nuts-node/vdr/didnuts" didnutsStore "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/didservice" "github.com/nuts-foundation/nuts-node/vdr/didweb" "github.com/nuts-foundation/nuts-node/vdr/log" - "github.com/nuts-foundation/nuts-node/vdr/types" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "net/url" ) // ModuleName is the name of the engine const ModuleName = "VDR" -var _ types.VDR = (*VDR)(nil) -var _ core.Named = (*VDR)(nil) -var _ core.Configurable = (*VDR)(nil) +var _ VDR = (*Module)(nil) +var _ core.Named = (*Module)(nil) +var _ core.Configurable = (*Module)(nil) -// VDR stands for the Nuts Verifiable Data Registry. It is the public entrypoint to work with W3C DID documents. +// Module implements VDR, which stands for the Verifiable Data Registry. It is the public entrypoint to work with W3C DID documents. // It connects the Resolve, Create and Update DID methods to the network, and receives events back from the network which are processed in the store. // It is also a Runnable, Diagnosable and Configurable Nuts Engine. -type VDR struct { +type Module struct { store didnutsStore.Store network network.Transactions networkAmbassador didnuts.Ambassador - didDocCreator types.DocCreator - didResolver *didservice.DIDResolverRouter - serviceResolver types.ServiceResolver - documentOwner types.DocumentOwner + didDocCreator management.DocCreator + didResolver *resolver.DIDResolverRouter + serviceResolver resolver.ServiceResolver + documentOwner management.DocumentOwner keyStore crypto.KeyStore storageProvider storage.Provider eventManager events.Event } -func (r *VDR) DeriveWebDIDDocument(ctx context.Context, baseURL url.URL, nutsDID did.DID) (*did.Document, error) { +func (r *Module) DeriveWebDIDDocument(ctx context.Context, baseURL url.URL, nutsDID did.DID) (*did.Document, error) { nutsDIDDocument, _, err := r.Resolver().Resolve(nutsDID, nil) if err != nil { return nil, err @@ -84,7 +84,7 @@ func (r *VDR) DeriveWebDIDDocument(ctx context.Context, baseURL url.URL, nutsDID WithError(err). WithField(core.LogFieldDID, nutsDID). Info("Tried to derive did:web document from Nuts DID that is not owned by this node") - return nil, types.ErrNotFound + return nil, resolver.ErrNotFound } resultDIDDocumentData, _ := nutsDIDDocument.MarshalJSON() @@ -107,32 +107,32 @@ func (r *VDR) DeriveWebDIDDocument(ctx context.Context, baseURL url.URL, nutsDID return &result, nil } -func (r *VDR) Resolver() types.DIDResolver { +func (r *Module) Resolver() resolver.DIDResolver { return r.didResolver } -// NewVDR creates a new VDR with provided params +// NewVDR creates a new Module with provided params func NewVDR(cryptoClient crypto.KeyStore, networkClient network.Transactions, - didStore didnutsStore.Store, eventManager events.Event) *VDR { - didResolver := &didservice.DIDResolverRouter{} - return &VDR{ + didStore didnutsStore.Store, eventManager events.Event) *Module { + didResolver := &resolver.DIDResolverRouter{} + return &Module{ network: networkClient, eventManager: eventManager, didDocCreator: didnuts.Creator{KeyStore: cryptoClient}, didResolver: didResolver, store: didStore, - serviceResolver: didservice.ServiceResolver{Resolver: didResolver}, + serviceResolver: resolver.DIDServiceResolver{Resolver: didResolver}, documentOwner: newCachingDocumentOwner(privateKeyDocumentOwner{keyResolver: cryptoClient}, didResolver), keyStore: cryptoClient, } } -func (r *VDR) Name() string { +func (r *Module) Name() string { return ModuleName } -// Configure configures the VDR engine. -func (r *VDR) Configure(_ core.ServerConfig) error { +// Configure configures the Module engine. +func (r *Module) Configure(_ core.ServerConfig) error { r.networkAmbassador = didnuts.NewAmbassador(r.network, r.store, r.eventManager) // Register DID methods @@ -145,7 +145,7 @@ func (r *VDR) Configure(_ core.ServerConfig) error { return nil } -func (r *VDR) Start() error { +func (r *Module) Start() error { err := r.networkAmbassador.Start() if err != nil { return err @@ -164,15 +164,15 @@ func (r *VDR) Start() error { return err } -func (r *VDR) Shutdown() error { +func (r *Module) Shutdown() error { return nil } -func (r *VDR) ConflictedDocuments() ([]did.Document, []types.DocumentMetadata, error) { +func (r *Module) ConflictedDocuments() ([]did.Document, []resolver.DocumentMetadata, error) { conflictedDocs := make([]did.Document, 0) - conflictedMeta := make([]types.DocumentMetadata, 0) + conflictedMeta := make([]resolver.DocumentMetadata, 0) - err := r.store.Conflicted(func(doc did.Document, metadata types.DocumentMetadata) error { + err := r.store.Conflicted(func(doc did.Document, metadata resolver.DocumentMetadata) error { conflictedDocs = append(conflictedDocs, doc) conflictedMeta = append(conflictedMeta, metadata) return nil @@ -180,18 +180,18 @@ func (r *VDR) ConflictedDocuments() ([]did.Document, []types.DocumentMetadata, e return conflictedDocs, conflictedMeta, err } -func (r *VDR) IsOwner(ctx context.Context, id did.DID) (bool, error) { +func (r *Module) IsOwner(ctx context.Context, id did.DID) (bool, error) { return r.documentOwner.IsOwner(ctx, id) } -func (r *VDR) ListOwned(ctx context.Context) ([]did.DID, error) { +func (r *Module) ListOwned(ctx context.Context) ([]did.DID, error) { return r.documentOwner.ListOwned(ctx) } // newOwnConflictedDocIterator accepts two counters and returns a new DocIterator that counts the total number of // conflicted documents, both total and owned by this node. -func (r *VDR) newOwnConflictedDocIterator(totalCount, ownedCount *int) types.DocIterator { - return func(doc did.Document, metadata types.DocumentMetadata) error { +func (r *Module) newOwnConflictedDocIterator(totalCount, ownedCount *int) management.DocIterator { + return func(doc did.Document, metadata resolver.DocumentMetadata) error { *totalCount++ controllers, err := didnuts.ResolveControllers(r.store, doc, nil) if err != nil { @@ -219,7 +219,7 @@ func (r *VDR) newOwnConflictedDocIterator(totalCount, ownedCount *int) types.Doc } // Diagnostics returns the diagnostics for this engine -func (r *VDR) Diagnostics() []core.DiagnosticResult { +func (r *Module) Diagnostics() []core.DiagnosticResult { // return # conflicted docs totalCount := 0 ownedCount := 0 @@ -262,12 +262,12 @@ func (r *VDR) Diagnostics() []core.DiagnosticResult { } // Create generates a new DID Document -func (r *VDR) Create(ctx context.Context, options types.DIDCreationOptions) (*did.Document, crypto.Key, error) { +func (r *Module) Create(ctx context.Context, options management.DIDCreationOptions) (*did.Document, crypto.Key, error) { log.Logger().Debug("Creating new DID Document.") // for all controllers given in the options, we need to capture the metadata so the new transaction can reference to it // holder for all metadata of the controllers - controllerMetadata := make([]types.DocumentMetadata, len(options.Controllers)) + controllerMetadata := make([]resolver.DocumentMetadata, len(options.Controllers)) // if any controllers have been added, check if they exist through the didResolver if len(options.Controllers) > 0 { @@ -310,11 +310,11 @@ func (r *VDR) Create(ctx context.Context, options types.DIDCreationOptions) (*di } // Update updates a DID Document based on the DID -func (r *VDR) Update(ctx context.Context, id did.DID, next did.Document) error { +func (r *Module) Update(ctx context.Context, id did.DID, next did.Document) error { log.Logger(). WithField(core.LogFieldDID, id). Debug("Updating DID Document") - resolverMetadata := &types.ResolveMetadata{ + resolverMetadata := &resolver.ResolveMetadata{ AllowDeactivated: true, } @@ -328,8 +328,8 @@ func (r *VDR) Update(ctx context.Context, id did.DID, next did.Document) error { if err != nil { return fmt.Errorf("update DID document: %w", err) } - if didservice.IsDeactivated(*currentDIDDocument) { - return fmt.Errorf("update DID document: %w", types.ErrDeactivated) + if resolver.IsDeactivated(*currentDIDDocument) { + return fmt.Errorf("update DID document: %w", resolver.ErrDeactivated) } // #1530: add nuts and JWS context if not present @@ -365,7 +365,7 @@ func (r *VDR) Update(ctx context.Context, id did.DID, next did.Document) error { if err != nil { log.Logger().WithError(err).Warn("Unable to update DID document") if errors.Is(err, crypto.ErrPrivateKeyNotFound) { - err = types.ErrDIDNotManagedByThisNode + err = resolver.ErrDIDNotManagedByThisNode } return fmt.Errorf("update DID document: %w", err) } @@ -377,7 +377,7 @@ func (r *VDR) Update(ctx context.Context, id did.DID, next did.Document) error { return nil } -func (r *VDR) resolveControllerWithKey(ctx context.Context, doc did.Document) (did.Document, crypto.Key, error) { +func (r *Module) resolveControllerWithKey(ctx context.Context, doc did.Document) (did.Document, crypto.Key, error) { controllers, err := didnuts.ResolveControllers(r.store, doc, nil) if err != nil { return did.Document{}, nil, fmt.Errorf("error while finding controllers for document: %w", err) @@ -397,7 +397,7 @@ func (r *VDR) resolveControllerWithKey(ctx context.Context, doc did.Document) (d } if errors.Is(err, crypto.ErrPrivateKeyNotFound) { - return did.Document{}, nil, types.ErrDIDNotManagedByThisNode + return did.Document{}, nil, resolver.ErrDIDNotManagedByThisNode } return did.Document{}, nil, fmt.Errorf("could not find capabilityInvocation key for updating the DID document: %w", err) diff --git a/vdr/vdr_test.go b/vdr/vdr_test.go index 59c2634f16..30cab85a79 100644 --- a/vdr/vdr_test.go +++ b/vdr/vdr_test.go @@ -31,7 +31,8 @@ import ( "github.com/nuts-foundation/nuts-node/core" "github.com/nuts-foundation/nuts-node/vdr/didnuts" "github.com/nuts-foundation/nuts-node/vdr/didnuts/didstore" - "github.com/nuts-foundation/nuts-node/vdr/didservice" + "github.com/nuts-foundation/nuts-node/vdr/management" + "github.com/nuts-foundation/nuts-node/vdr/resolver" "io" "net/http" "net/url" @@ -43,7 +44,6 @@ import ( "github.com/nuts-foundation/nuts-node/crypto" "github.com/nuts-foundation/nuts-node/crypto/hash" "github.com/nuts-foundation/nuts-node/network" - "github.com/nuts-foundation/nuts-node/vdr/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -54,13 +54,13 @@ const expectedPayloadType = "application/did+json" // testCtx contains the controller and mocks needed fot testing the Manipulator type vdrTestCtx struct { ctrl *gomock.Controller - vdr VDR + vdr Module mockStore *didstore.MockStore mockNetwork *network.MockTransactions mockKeyStore *crypto.MockKeyStore mockAmbassador *didnuts.MockAmbassador ctx context.Context - mockOwner *types.MockDocumentOwner + mockOwner *management.MockDocumentOwner } func newVDRTestCtx(t *testing.T) vdrTestCtx { @@ -70,9 +70,9 @@ func newVDRTestCtx(t *testing.T) vdrTestCtx { mockStore := didstore.NewMockStore(ctrl) mockNetwork := network.NewMockTransactions(ctrl) mockKeyStore := crypto.NewMockKeyStore(ctrl) - mockDocumentOwner := types.NewMockDocumentOwner(ctrl) - resolverRouter := &didservice.DIDResolverRouter{} - vdr := VDR{ + mockDocumentOwner := management.NewMockDocumentOwner(ctrl) + resolverRouter := &resolver.DIDResolverRouter{} + vdr := Module{ store: mockStore, network: mockNetwork, networkAmbassador: mockAmbassador, @@ -107,10 +107,10 @@ func TestVDR_Update(t *testing.T) { nextDIDDocument := didnuts.CreateDocument() nextDIDDocument.ID = *id - expectedResolverMetadata := &types.ResolveMetadata{ + expectedResolverMetadata := &resolver.ResolveMetadata{ AllowDeactivated: true, } - resolvedMetadata := types.DocumentMetadata{ + resolvedMetadata := resolver.DocumentMetadata{ SourceTransactions: []hash.SHA256Hash{currentHash}, } test.mockStore.EXPECT().Resolve(*id, expectedResolverMetadata).Return(¤tDIDDocument, &resolvedMetadata, nil) @@ -130,17 +130,17 @@ func TestVDR_Update(t *testing.T) { currentDIDDocument.Controller = []did.DID{currentDIDDocument.ID} nextDIDDocument := did.Document{} - expectedResolverMetadata := &types.ResolveMetadata{ + expectedResolverMetadata := &resolver.ResolveMetadata{ AllowDeactivated: true, } - resolvedMetadata := types.DocumentMetadata{} + resolvedMetadata := resolver.DocumentMetadata{} test.mockStore.EXPECT().Resolve(*id, expectedResolverMetadata).Return(¤tDIDDocument, &resolvedMetadata, nil) err := test.vdr.Update(test.ctx, *id, nextDIDDocument) assert.EqualError(t, err, "update DID document: DID Document validation failed: invalid context") }) t.Run("error - can only update did:nuts methods", func(t *testing.T) { - err := (&VDR{}).Update(context.Background(), did.MustParseDID("did:web:example.com"), did.Document{}) + err := (&Module{}).Update(context.Background(), did.MustParseDID("did:web:example.com"), did.Document{}) assert.EqualError(t, err, "can't update DID document of type: web") }) @@ -149,10 +149,10 @@ func TestVDR_Update(t *testing.T) { document := didnuts.CreateDocument() document.ID = *id - expectedResolverMetadata := &types.ResolveMetadata{ + expectedResolverMetadata := &resolver.ResolveMetadata{ AllowDeactivated: true, } - resolvedMetadata := types.DocumentMetadata{} + resolvedMetadata := resolver.DocumentMetadata{} test.mockStore.EXPECT().Resolve(*id, expectedResolverMetadata).Return(&document, &resolvedMetadata, nil) err := test.vdr.Update(test.ctx, *id, document) assert.EqualError(t, err, "update DID document: the DID document has been deactivated") @@ -160,10 +160,10 @@ func TestVDR_Update(t *testing.T) { t.Run("error - could not resolve current document", func(t *testing.T) { test := newVDRTestCtx(t) nextDIDDocument := did.Document{} - expectedResolverMetadata := &types.ResolveMetadata{ + expectedResolverMetadata := &resolver.ResolveMetadata{ AllowDeactivated: true, } - test.mockStore.EXPECT().Resolve(*id, expectedResolverMetadata).Return(nil, nil, types.ErrNotFound) + test.mockStore.EXPECT().Resolve(*id, expectedResolverMetadata).Return(nil, nil, resolver.ErrNotFound) err := test.vdr.Update(test.ctx, *id, nextDIDDocument) assert.EqualError(t, err, "update DID document: unable to find the DID document") }) @@ -174,15 +174,15 @@ func TestVDR_Update(t *testing.T) { nextDIDDocument.ID = *id currentDIDDocument := nextDIDDocument currentDIDDocument.AddCapabilityInvocation(&did.VerificationMethod{ID: *keyID}) - test.mockStore.EXPECT().Resolve(*id, gomock.Any()).Times(1).Return(¤tDIDDocument, &types.DocumentMetadata{}, nil) + test.mockStore.EXPECT().Resolve(*id, gomock.Any()).Times(1).Return(¤tDIDDocument, &resolver.DocumentMetadata{}, nil) test.mockKeyStore.EXPECT().Resolve(test.ctx, keyID.String()).Return(nil, crypto.ErrPrivateKeyNotFound) err := test.vdr.Update(test.ctx, *id, nextDIDDocument) assert.Error(t, err) assert.EqualError(t, err, "update DID document: DID document not managed by this node") - assert.ErrorIs(t, err, types.ErrDIDNotManagedByThisNode) - assert.True(t, errors.Is(err, types.ErrDIDNotManagedByThisNode), + assert.ErrorIs(t, err, resolver.ErrDIDNotManagedByThisNode) + assert.True(t, errors.Is(err, resolver.ErrDIDNotManagedByThisNode), "expected ErrDIDNotManagedByThisNode error when the document is not managed by this node") }) } @@ -221,13 +221,13 @@ func TestVDR_Create(t *testing.T) { copiedDocument.Controller = []did.DID{controllerID, id} expectedPayload, _ := json.Marshal(copiedDocument) refs := []hash.SHA256Hash{hash.EmptyHash()} - creationOptions := types.DIDCreationOptions{ + creationOptions := management.DIDCreationOptions{ Controllers: []did.DID{controllerID}, - KeyFlags: types.AssertionMethodUsage | types.CapabilityInvocationUsage | types.KeyAgreementUsage, + KeyFlags: management.AssertionMethodUsage | management.CapabilityInvocationUsage | management.KeyAgreementUsage, SelfControl: true, } test.mockKeyStore.EXPECT().New(test.ctx, gomock.Any()).Return(key, nil) - test.mockStore.EXPECT().Resolve(controllerID, gomock.Any()).Return(&controllerDocument, &types.DocumentMetadata{SourceTransactions: refs}, nil) + test.mockStore.EXPECT().Resolve(controllerID, gomock.Any()).Return(&controllerDocument, &resolver.DocumentMetadata{SourceTransactions: refs}, nil) test.mockNetwork.EXPECT().CreateTransaction(test.ctx, network.TransactionTemplate(expectedPayloadType, expectedPayload, key).WithAttachKey().WithAdditionalPrevs(refs)) didDoc, key, err := test.vdr.Create(test.ctx, creationOptions) @@ -239,12 +239,12 @@ func TestVDR_Create(t *testing.T) { t.Run("error - unknown controllers", func(t *testing.T) { test := newVDRTestCtx(t) - creationOptions := types.DIDCreationOptions{ + creationOptions := management.DIDCreationOptions{ Controllers: []did.DID{controllerID}, - KeyFlags: types.AssertionMethodUsage | types.CapabilityInvocationUsage | types.KeyAgreementUsage, + KeyFlags: management.AssertionMethodUsage | management.CapabilityInvocationUsage | management.KeyAgreementUsage, SelfControl: true, } - test.mockStore.EXPECT().Resolve(controllerID, gomock.Any()).Return(nil, nil, types.ErrNotFound) + test.mockStore.EXPECT().Resolve(controllerID, gomock.Any()).Return(nil, nil, resolver.ErrNotFound) _, _, err := test.vdr.Create(test.ctx, creationOptions) @@ -274,7 +274,7 @@ func TestVDR_Create(t *testing.T) { func TestNewVDR(t *testing.T) { vdr := NewVDR(nil, nil, nil, nil) - assert.IsType(t, &VDR{}, vdr) + assert.IsType(t, &Module{}, vdr) } func TestVDR_Start(t *testing.T) { @@ -368,10 +368,10 @@ func TestVDR_ConflictingDocuments(t *testing.T) { // organization keyOrg := crypto.NewTestKey("did:nuts:org#keyOrg-1") test.mockKeyStore.EXPECT().New(test.ctx, gomock.Any()).Return(keyOrg, nil).Times(2) - test.mockStore.EXPECT().Resolve(didDocVendor.ID, nil).Return(didDocVendor, &types.DocumentMetadata{}, nil) - didDocOrg, keyOrg, err := test.vdr.Create(test.ctx, types.DIDCreationOptions{ + test.mockStore.EXPECT().Resolve(didDocVendor.ID, nil).Return(didDocVendor, &resolver.DocumentMetadata{}, nil) + didDocOrg, keyOrg, err := test.vdr.Create(test.ctx, management.DIDCreationOptions{ Controllers: []did.DID{didDocVendor.ID}, - KeyFlags: types.AssertionMethodUsage | types.KeyAgreementUsage, + KeyFlags: management.AssertionMethodUsage | management.KeyAgreementUsage, SelfControl: false, }) require.NoError(t, err) @@ -475,7 +475,7 @@ func TestVDR_resolveControllerKey(t *testing.T) { _, _, err := test.vdr.resolveControllerWithKey(test.ctx, currentDIDDocument) - assert.Equal(t, types.ErrDIDNotManagedByThisNode, err) + assert.Equal(t, resolver.ErrDIDNotManagedByThisNode, err) }) } @@ -502,10 +502,10 @@ func TestVDR_IsOwner(t *testing.T) { id := did.MustParseDID("did:nuts:123") t.Run("delegates the call to the underlying DocumentOwner", func(t *testing.T) { ctrl := gomock.NewController(t) - owner := types.NewMockDocumentOwner(ctrl) + owner := management.NewMockDocumentOwner(ctrl) owner.EXPECT().IsOwner(gomock.Any(), id).Return(true, nil) - result, err := (&VDR{documentOwner: owner}).IsOwner(context.Background(), id) + result, err := (&Module{documentOwner: owner}).IsOwner(context.Background(), id) assert.NoError(t, err) assert.True(t, result) @@ -635,17 +635,17 @@ func TestVDR_DeriveWebDIDDocument(t *testing.T) { actual, err := ctx.vdr.DeriveWebDIDDocument(nil, *baseURL, nutsDID) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Nil(t, actual) }) t.Run("resolver error", func(t *testing.T) { ctx := newVDRTestCtx(t) - ctx.mockStore.EXPECT().Resolve(nutsDID, nil).Return(nil, nil, types.ErrNotFound) + ctx.mockStore.EXPECT().Resolve(nutsDID, nil).Return(nil, nil, resolver.ErrNotFound) actual, err := ctx.vdr.DeriveWebDIDDocument(nil, *baseURL, nutsDID) - assert.ErrorIs(t, err, types.ErrNotFound) + assert.ErrorIs(t, err, resolver.ErrNotFound) assert.Nil(t, actual) }) t.Run("ownership check error", func(t *testing.T) {