Skip to content

Commit

Permalink
added checks to VCR.Load() (#3417)
Browse files Browse the repository at this point in the history
  • Loading branch information
woutslakhorst authored Sep 27, 2024
1 parent 443e5a3 commit 7d6613e
Show file tree
Hide file tree
Showing 12 changed files with 345 additions and 271 deletions.
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func CreateSystem(shutdownCallback context.CancelFunc) *core.System {
system.RegisterRoutes(&networkAPI.Wrapper{Service: networkInstance})
system.RegisterRoutes(&vdrAPI.Wrapper{VDR: vdrInstance, SubjectManager: vdrInstance})
system.RegisterRoutes(&vdrAPIv2.Wrapper{VDR: vdrInstance, SubjectManager: vdrInstance})
system.RegisterRoutes(&vcrAPI.Wrapper{VCR: credentialInstance, ContextManager: jsonld})
system.RegisterRoutes(&vcrAPI.Wrapper{VCR: credentialInstance, ContextManager: jsonld, SubjectManager: vdrInstance})
system.RegisterRoutes(&openid4vciAPI.Wrapper{
VCR: credentialInstance,
VDR: vdrInstance,
Expand Down
12 changes: 6 additions & 6 deletions docs/_static/vcr/vcr_v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -479,17 +479,17 @@ paths:
$ref: "#/components/schemas/VerifiablePresentation"
default:
$ref: '../common/error_response.yaml'
/internal/vcr/v2/holder/{did}/vc:
/internal/vcr/v2/holder/{subjectID}/vc:
parameters:
- name: did
- name: subjectID
in: path
description: URL encoded DID.
description: Subject ID of the wallet owner at this node.
required: true
content:
plain/text:
schema:
type: string
example: "did:web:example.com:iam:123"
example: 90BC1AE9-752B-432F-ADC3-DD9F9C61843C
get:
summary: List all Verifiable Credentials in the holder's wallet.
operationId: getCredentialsInWallet
Expand All @@ -510,8 +510,8 @@ paths:
summary: Load a VerifiableCredential into the holders wallet.
description: |
If a VerifiableCredential is not directly issued to the wallet through e.g. OpenID4VCI, this API allows to add it to a wallet.
The DID of the holder has to be provided in the path.
It's assumed that the credentialSubject.id equals the holder DID.
The subject identifier of the holder has to be provided in the path.
The credentialSubject.id must equal to a DID of the holder, the signature must be valid and the credential must not be expired or revoked.
error returns:
* 400 - Invalid credential
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func Test_UserAccessToken_EmployeeCredential(t *testing.T) {
func setupNode(t testing.TB, ctx context.Context, config core.ClientConfig) (string, did.DID, OpenID4VP) {
subject, didDoc, err := createDID(config)
require.NoError(t, err)
err = browser.IssueOrganizationCredential(didDoc, fmt.Sprintf("%s Organization", didDoc.ID.String()), "Testland", config)
err = browser.IssueOrganizationCredential(subject, didDoc, fmt.Sprintf("%s Organization", didDoc.ID.String()), "Testland", config)
require.NoError(t, err)

iamClientB, err := iamAPI.NewClient(config.GetAddress())
Expand Down
4 changes: 2 additions & 2 deletions e2e-tests/browser/rfc019_selfsigned/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ func Test_LoginWithSelfSignedMeans(t *testing.T) {
require.NoError(t, err)
err = registerCompoundService(verifyingOrganization.ID, purposeOfUse)
require.NoError(t, err)
err = browser.IssueOrganizationCredential(verifyingOrganization, "Verifying Organization", "Testland", apps.NodeClientConfig)
err = browser.IssueOrganizationCredential("", verifyingOrganization, "Verifying Organization", "Testland", apps.NodeClientConfig)
require.NoError(t, err)

issuingOrganization, err := createDID()
require.NoError(t, err)
err = registerCompoundService(issuingOrganization.ID, purposeOfUse)
require.NoError(t, err)
err = browser.IssueOrganizationCredential(issuingOrganization, "Issuing Organization", "Testland", apps.NodeClientConfig)
err = browser.IssueOrganizationCredential("", issuingOrganization, "Issuing Organization", "Testland", apps.NodeClientConfig)
require.NoError(t, err)

selfSigned := apps.SelfSigned{
Expand Down
4 changes: 2 additions & 2 deletions e2e-tests/browser/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
vcrAPI "github.com/nuts-foundation/nuts-node/vcr/api/vcr/v2"
)

func IssueOrganizationCredential(organization *did.Document, name, city string, clientConfig core.ClientConfig) error {
func IssueOrganizationCredential(subject string, organization *did.Document, name, city string, clientConfig core.ClientConfig) error {
vcrClient := vcrAPI.HTTPClient{ClientConfig: clientConfig}
request := vcrAPI.IssueVCRequest{
Issuer: organization.ID.String(),
Expand Down Expand Up @@ -56,7 +56,7 @@ func IssueOrganizationCredential(organization *did.Document, name, city string,
}
if organization.ID.Method == "web" {
// Need to load it into tbe wallet
return vcrClient.LoadVC(organization.ID, *issuedCredential)
return vcrClient.LoadVC(subject, *issuedCredential)
}
return nil
}
2 changes: 1 addition & 1 deletion e2e-tests/discovery/run-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ else
exitWithDockerLogs 1
fi

RESPONSE=$(echo $RESPONSE | curl --insecure -s -X POST --data-binary @- http://localhost:28081/internal/vcr/v2/holder/${DID}/vc -H "Content-Type:application/json")
RESPONSE=$(echo $RESPONSE | curl --insecure -s -X POST --data-binary @- http://localhost:28081/internal/vcr/v2/holder/${SUBJECT}/vc -H "Content-Type:application/json")
if [[ $RESPONSE -eq "" ]]; then
echo "VC stored in wallet"
else
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/oauth-flow/openid4vp/do-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ else
exitWithDockerLogs 1
fi

RESPONSE=$(echo $RESPONSE | curl -X POST --data-binary @- http://localhost:28081/internal/vcr/v2/holder/${PARTY_B_DID}/vc -H "Content-Type:application/json")
RESPONSE=$(echo $RESPONSE | curl -X POST --data-binary @- http://localhost:28081/internal/vcr/v2/holder/subjectB/vc -H "Content-Type:application/json")
if echo $RESPONSE == ""; then
echo "VC stored in wallet"
else
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/oauth-flow/rfc021/do-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ else
exitWithDockerLogs 1
fi

RESPONSE=$(echo $RESPONSE | curl -X POST --data-binary @- http://localhost:28081/internal/vcr/v2/holder/${VENDOR_B_DID}/vc -H "Content-Type:application/json")
RESPONSE=$(echo $RESPONSE | curl -X POST --data-binary @- http://localhost:28081/internal/vcr/v2/holder/vendorB/vc -H "Content-Type:application/json")
if echo $RESPONSE == ""; then
echo "VC stored in wallet"
else
Expand Down
72 changes: 55 additions & 17 deletions vcr/api/vcr/v2/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/nuts-foundation/nuts-node/vcr/issuer"
vcrTypes "github.com/nuts-foundation/nuts-node/vcr/types"
"github.com/nuts-foundation/nuts-node/vcr/verifier"
"github.com/nuts-foundation/nuts-node/vdr/didsubject"
"github.com/nuts-foundation/nuts-node/vdr/resolver"
"net/http"
"strings"
Expand All @@ -55,6 +56,7 @@ var _ StrictServerInterface = (*Wrapper)(nil)
type Wrapper struct {
ContextManager jsonld.JSONLD
VCR vcr.VCR
SubjectManager didsubject.Manager
}

// Routes registers the handler to the echo router
Expand All @@ -77,13 +79,14 @@ func (w *Wrapper) Routes(router core.EchoRouter) {
// 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{
vcrTypes.ErrNotFound: http.StatusNotFound,
resolver.ErrServiceNotFound: http.StatusPreconditionFailed,
vcrTypes.ErrRevoked: http.StatusConflict,
resolver.ErrNotFound: http.StatusBadRequest,
resolver.ErrKeyNotFound: http.StatusBadRequest,
did.ErrInvalidDID: http.StatusBadRequest,
vcrTypes.ErrStatusNotFound: http.StatusBadRequest,
vcrTypes.ErrNotFound: http.StatusNotFound,
resolver.ErrServiceNotFound: http.StatusPreconditionFailed,
vcrTypes.ErrRevoked: http.StatusConflict,
resolver.ErrNotFound: http.StatusBadRequest,
resolver.ErrKeyNotFound: http.StatusBadRequest,
did.ErrInvalidDID: http.StatusBadRequest,
vcrTypes.ErrStatusNotFound: http.StatusBadRequest,
didsubject.ErrSubjectNotFound: http.StatusNotFound,
})
}

Expand Down Expand Up @@ -395,14 +398,42 @@ func (w *Wrapper) VerifyVP(ctx context.Context, request VerifyVPRequestObject) (
}

func (w *Wrapper) LoadVC(ctx context.Context, request LoadVCRequestObject) (LoadVCResponseObject, error) {
// the actual holder is ignored for now, since we only support a single wallet...
_, err := did.ParseDID(request.Did)
if err != nil {
return nil, core.InvalidInputError("invalid holder DID: %w", err)
}
if request.Body == nil {
return nil, core.InvalidInputError("missing credential in body")
}

// get DIDs for holder
dids, err := w.SubjectManager.ListDIDs(ctx, request.SubjectID)
if err != nil {
return nil, err
}

// get credentialSubject.ID for credential
credentialSubject, err := credential.ResolveSubjectDID(*request.Body)
if err != nil {
return nil, core.InvalidInputError("invalid credentialSubject.ID: %w", err)
}

// check if the credentialSubject.ID is in the list of DIDs
found := false
for _, did := range dids {
if did.Equals(*credentialSubject) {
found = true
break
}
}
if !found {
return nil, core.InvalidInputError("subject does not own DID specified by credentialSubject.ID")
}

// validate credential
if err = w.VCR.Verifier().Verify(*request.Body, true, true, nil); err != nil {
if errors.Is(err, verifier.VerificationError{}) {
return nil, core.InvalidInputError(err.Error())
}
return nil, err
}

err = w.VCR.Wallet().Put(ctx, *request.Body)
if err != nil {
return nil, err
Expand All @@ -411,14 +442,21 @@ func (w *Wrapper) LoadVC(ctx context.Context, request LoadVCRequestObject) (Load
}

func (w *Wrapper) GetCredentialsInWallet(ctx context.Context, request GetCredentialsInWalletRequestObject) (GetCredentialsInWalletResponseObject, error) {
holderDID, err := did.ParseDID(request.Did)
if err != nil {
return nil, core.InvalidInputError("invalid holder DID: %w", err)
}
credentials, err := w.VCR.Wallet().List(ctx, *holderDID)
// get DIDs for holder
dids, err := w.SubjectManager.ListDIDs(ctx, request.SubjectID)
if err != nil {
return nil, err
}

credentials := make([]vc.VerifiableCredential, 0)
for _, did := range dids {
creds, err := w.VCR.Wallet().List(ctx, did)
if err != nil {
return nil, err
}
credentials = append(credentials, creds...)
}

return GetCredentialsInWallet200JSONResponse(credentials), nil
}

Expand Down
Loading

0 comments on commit 7d6613e

Please sign in to comment.