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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ gen-mocks:
mockgen -destination=vcr/issuer/openid_mock.go -package=issuer -source=vcr/issuer/openid.go
mockgen -destination=vcr/holder/openid_mock.go -package=holder -source=vcr/holder/openid.go
mockgen -destination=vcr/openid4vci/identifiers_mock.go -package=openid4vci -source=vcr/openid4vci/identifiers.go
mockgen -destination=vcr/revocation/mock.go -package=revocation -source=vcr/revocation/types.go
mockgen -destination=vcr/signature/mock.go -package=signature -source=vcr/signature/signature.go
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
Expand Down
12 changes: 9 additions & 3 deletions vcr/api/vcr/v2/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,15 @@ func (w *Wrapper) SearchIssuedVCs(ctx context.Context, request SearchIssuedVCsRe
if err != nil {
return nil, err
}
result, err := w.vcsWithRevocationsToSearchResults(foundVCs)
if err != nil {
return nil, err

result := make([]SearchVCResult, len(foundVCs))
for i, resolvedVC := range foundVCs {
var revocation *Revocation
revocation, err := w.VCR.Issuer().GetRevocation(*resolvedVC.ID)
if err != nil && !errors.Is(err, vcrTypes.ErrNotFound) {
return nil, err
}
result[i] = SearchVCResult{VerifiableCredential: resolvedVC, Revocation: revocation}
}
return SearchIssuedVCs200JSONResponse(SearchVCResults{result}), nil
}
Expand Down
9 changes: 5 additions & 4 deletions vcr/api/vcr/v2/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/labstack/echo/v4"
"github.com/nuts-foundation/nuts-node/vcr/types"
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/labstack/echo/v4"
"github.com/nuts-foundation/nuts-node/vcr/types"

ssi "github.com/nuts-foundation/go-did"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
Expand Down Expand Up @@ -518,7 +519,7 @@ func TestWrapper_SearchIssuedVCs(t *testing.T) {
t.Run("ok - without subject, 1 result", func(t *testing.T) {
testContext := newMockContext(t)
testContext.mockIssuer.EXPECT().SearchCredential(testCredential, *issuerDID, nil).Return([]VerifiableCredential{foundVC}, nil)
testContext.mockVerifier.EXPECT().GetRevocation(vcID).Return(nil, verifier.ErrNotFound)
testContext.mockIssuer.EXPECT().GetRevocation(vcID).Return(nil, types.ErrNotFound)
expectedResponse := SearchIssuedVCs200JSONResponse(SearchVCResults{VerifiableCredentials: []SearchVCResult{{VerifiableCredential: foundVC}}})
params := SearchIssuedVCsParams{
CredentialType: "TestCredential",
Expand All @@ -535,7 +536,7 @@ func TestWrapper_SearchIssuedVCs(t *testing.T) {
revocation := &Revocation{Reason: "because of reasons"}
testContext := newMockContext(t)
testContext.mockIssuer.EXPECT().SearchCredential(testCredential, *issuerDID, nil).Return([]VerifiableCredential{foundVC}, nil)
testContext.mockVerifier.EXPECT().GetRevocation(vcID).Return(revocation, nil)
testContext.mockIssuer.EXPECT().GetRevocation(vcID).Return(revocation, nil)
expectedResponse := SearchIssuedVCs200JSONResponse(SearchVCResults{VerifiableCredentials: []SearchVCResult{{VerifiableCredential: foundVC, Revocation: revocation}}})
params := SearchIssuedVCsParams{
CredentialType: "TestCredential",
Expand Down
10 changes: 6 additions & 4 deletions vcr/issuer/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ type Issuer interface {
// StatusList returns the StatusList2021Credential tracking status list revocations for this issuer at /iam/issuerID/status/page.
// Returns types.ErrNotFound when no credential statuses have been published using the issuer and page combination.
StatusList(ctx context.Context, issuer did.DID, page int) (*vc.VerifiableCredential, error)
// GetRevocation returns a revocation for a credential ID.
// Returns nil when no revocation is found.
GetRevocation(id ssi.URI) (*credential.Revocation, error)
CredentialSearcher
}

Expand All @@ -63,10 +66,9 @@ type Store interface {
GetCredential(id ssi.URI) (*vc.VerifiableCredential, error)
// StoreCredential writes a VC to storage.
StoreCredential(vc vc.VerifiableCredential) error
// GetRevocation returns a revocation for a credential ID
// Returns a types.ErrNotFound when the revocation is not in the store
// Returns a types.ErrMultipleFound when there are multiple revocations for this credential ID in the store
GetRevocation(id ssi.URI) (*credential.Revocation, error)
// GetRevocation returns all revocations for a credential ID.
// Returns an empty slice when no revocations are found.
GetRevocation(id ssi.URI) ([]credential.Revocation, error)
// StoreRevocation writes a revocation to storage.
StoreRevocation(r credential.Revocation) error
CredentialSearcher
Expand Down
56 changes: 44 additions & 12 deletions vcr/issuer/issuer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"

"github.com/nuts-foundation/go-stoabs"
"github.com/nuts-foundation/nuts-node/vcr/openid4vci"
"github.com/nuts-foundation/nuts-node/vcr/revocation"
"github.com/nuts-foundation/nuts-node/vdr/didnuts"
"github.com/nuts-foundation/nuts-node/vdr/resolver"
"gorm.io/gorm"
"strings"
"time"

"github.com/google/uuid"
ssi "github.com/nuts-foundation/go-did"
Expand Down Expand Up @@ -93,6 +94,43 @@ type issuer struct {
statusList revocation.StatusList2021Issuer
}

func (i issuer) GetRevocation(credentialID ssi.URI) (*credential.Revocation, error) {
// did:nuts; use store.GetRevocation()
// otherwise, use statusList
credentialDIDURL, err := did.ParseDIDURL(credentialID.String())
if err != nil {
return nil, err
}
if credentialDIDURL.Method == didnuts.MethodName {
revocations, err := i.store.GetRevocation(credentialID)
if err != nil {
return nil, err
}
if len(revocations) == 0 {
return nil, nil
}
return &revocations[0], nil
}
// other DID method; use statusList
cred, err := i.store.GetCredential(credentialID)
if err != nil {
return nil, err
}
rev, err := i.statusList.GetRevocation(credentialID)
if err != nil {
return nil, err
}
if rev == nil {
return nil, nil
}
return &credential.Revocation{
Issuer: cred.Issuer,
Subject: credentialID,
Reason: rev.Purpose,
Date: rev.RevokedAt,
}, nil
}

// Issue creates a new credential, signs, stores it.
// If publish is true, it publishes the credential to the network using the configured Publisher
// Use the public flag to pass the visibility settings to the Publisher.
Expand Down Expand Up @@ -411,17 +449,11 @@ func (i issuer) buildRevocation(ctx context.Context, credentialID ssi.URI) (*cre
// isRevoked returns false if no credential.Revocation can be found, all other cases default to true.
// Only applies to did:nuts revocations.
func (i issuer) isRevoked(credentialID ssi.URI) (bool, error) {
_, err := i.store.GetRevocation(credentialID)
switch err {
case nil: // revocation found
return true, nil
case types.ErrMultipleFound:
return true, nil
case types.ErrNotFound:
return false, nil
default:
revocations, err := i.store.GetRevocation(credentialID)
if err != nil {
return true, err
}
return len(revocations) > 0, nil
}

func (i issuer) SearchCredential(credentialType ssi.URI, issuer did.DID, subject *ssi.URI) ([]vc.VerifiableCredential, error) {
Expand Down Expand Up @@ -486,7 +518,7 @@ func (c combinedStore) StoreCredential(vc vc.VerifiableCredential) error {
return c.otherDIDsStore.StoreCredential(vc)
}

func (c combinedStore) GetRevocation(id ssi.URI) (*credential.Revocation, error) {
func (c combinedStore) GetRevocation(id ssi.URI) ([]credential.Revocation, error) {
if strings.HasPrefix(id.String(), "did:nuts:") {
return c.didNutsStore.GetRevocation(id)
}
Expand Down
Loading
Loading