From c2763cf9ada7224b834d9578d23c5e3db0808bc8 Mon Sep 17 00:00:00 2001 From: Gerard Snaauw Date: Wed, 16 Oct 2024 16:42:05 +0200 Subject: [PATCH] Add statuslist revocation to e2e-test and fix failing code --- e2e-tests/oauth-flow/rfc021/do-test.sh | 29 +++++++++++++++++++++- vcr/revocation/bitstring.go | 11 ++++++--- vcr/revocation/statuslist2021_issuer.go | 33 +++++++++++-------------- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/e2e-tests/oauth-flow/rfc021/do-test.sh b/e2e-tests/oauth-flow/rfc021/do-test.sh index a91234153..b6b42ab03 100755 --- a/e2e-tests/oauth-flow/rfc021/do-test.sh +++ b/e2e-tests/oauth-flow/rfc021/do-test.sh @@ -71,7 +71,7 @@ echo "Status list credential: $STATUS_LIST_CREDENTIAL" # Get status list credential RESPONSE=$($db_dc exec nodeB-backend curl -s -k $STATUS_LIST_CREDENTIAL) # Check response HTTP 200 OK -if [ $? -eq 0 ]; then +if echo $RESPONSE | grep -q "\"id\":\"$STATUS_LIST_CREDENTIAL\"" ; then echo "Status list credential retrieved" else echo "FAILED: Could not retrieve status list credential" 1>&2 @@ -195,6 +195,33 @@ else exitWithDockerLogs 1 fi +echo "------------------------------------" +echo "Revoking credential..." +echo "------------------------------------" +# revoke credential +VENDOR_B_CREDENTIAL_ID=$(echo $VENDOR_B_CREDENTIAL | jq -r .id) +echo $VENDOR_B_CREDENTIAL_ID +RESPONSE=$(curl -s -X DELETE "http://localhost:28081/internal/vcr/v2/issuer/vc/${VENDOR_B_CREDENTIAL_ID//#/%23}") +if [ -z "${RESPONSE}" ]; then + echo "VendorB NutsOrganizationCredential revoked" +else + echo "FAILED: NutsOrganizationCredential not revoked" 1>&2 + echo $RESPONSE + exitWithDockerLogs 1 +fi + +echo "------------------------------------" +echo "Retrieving data fails..." +echo "------------------------------------" +RESPONSE=$($db_dc exec nodeB curl --http1.1 --insecure --cert /etc/nginx/ssl/server.pem --key /etc/nginx/ssl/key.pem https://nodeA:443/resource -H "Authorization: DPoP $ACCESS_TOKEN" -H "DPoP: $DPOP") +if [ "${RESPONSE}" == "Unauthorized" ]; then + echo "Access denied!" +else + echo "FAILED: Retrieved data with revoked credential" 1>&2 + echo $RESPONSE + exitWithDockerLogs 1 +fi + echo "------------------------------------" echo "Stopping Docker containers..." echo "------------------------------------" diff --git a/vcr/revocation/bitstring.go b/vcr/revocation/bitstring.go index 9529876f7..952a897c2 100644 --- a/vcr/revocation/bitstring.go +++ b/vcr/revocation/bitstring.go @@ -54,9 +54,14 @@ func (bs *bitstring) Scan(value any) error { *bs = nil return nil } - asString, ok := value.(string) - if !ok { - return fmt.Errorf("bitstring unmarshal from DB: expected []uint8, got %T", value) + var asString string + switch v := value.(type) { + case string: // sqlite, postgress, sqlserver + asString = v + case []uint8: // mysql + asString = string(v) + default: + return fmt.Errorf("bitstring unmarshal from DB: expected []uint8 or string, got %T", value) } expanded, err := expand(asString) if err != nil { diff --git a/vcr/revocation/statuslist2021_issuer.go b/vcr/revocation/statuslist2021_issuer.go index 74d482c8c..6588deecd 100644 --- a/vcr/revocation/statuslist2021_issuer.go +++ b/vcr/revocation/statuslist2021_issuer.go @@ -166,18 +166,7 @@ func (cs *StatusList2021) Credential(ctx context.Context, issuerDID did.DID, pag var cred *vc.VerifiableCredential // is nil, so if this panics outside this method the var name is probably shadowed in the db.Transaction. err = cs.db.Transaction(func(tx *gorm.DB) error { // lock credentialRecord row for statusListCredentialURL since it will be updated. - // Revoke does the same to guarantee the DB always contains all revocations. - // Microsoft SQL server does not support the locking clause, so we have to use a raw query instead. - // See https://github.com/nuts-foundation/nuts-node/issues/3393 - if tx.Dialector.Name() == "sqlserver" { - err = tx.Raw("SELECT * FROM status_list_credential WITH (UPDLOCK, ROWLOCK) WHERE subject_id = ?", statusListCredentialURL). - Scan(new(credentialRecord)). - Error - } else { - err = tx.Clauses(clause.Locking{Strength: clause.LockingStrengthUpdate}). - Find(new(credentialRecord), "subject_id = ?", statusListCredentialURL). - Error - } + err = lockCredentialRecord(tx, statusListCredentialURL) if err != nil { return err } @@ -421,12 +410,7 @@ func (cs *StatusList2021) Revoke(ctx context.Context, credentialID ssi.URI, entr return cs.db.Transaction(func(tx *gorm.DB) error { // lock relevant credentialRecord. It was created when the first entry was issued for this StatusList2021Credential. - err = tx.Model(new(credentialRecord)). - Clauses(clause.Locking{Strength: clause.LockingStrengthUpdate}). - Select("subject_id"). - Where("subject_id = ?", entry.StatusListCredential). - Find(new([]string)). - Error + err = lockCredentialRecord(tx, entry.StatusListCredential) if err != nil { return err } @@ -478,3 +462,16 @@ func (cs *StatusList2021) statusListURL(issuer did.DID, page int) string { result, _ := url.Parse(cs.baseURL) return result.JoinPath("statuslist", issuer.String(), strconv.Itoa(page)).String() } + +func lockCredentialRecord(tx *gorm.DB, statusListCredentialURL string) error { + // Microsoft SQL server does not support the locking clause, so we have to use a raw query instead. + // See https://github.com/nuts-foundation/nuts-node/issues/3393 + if tx.Dialector.Name() == "sqlserver" { + return tx.Raw("SELECT * FROM status_list_credential WITH (UPDLOCK, ROWLOCK) WHERE subject_id = ?", statusListCredentialURL). + Scan(new(credentialRecord)). + Error + } + return tx.Clauses(clause.Locking{Strength: clause.LockingStrengthUpdate}). + Find(new(credentialRecord), "subject_id = ?", statusListCredentialURL). + Error +}