diff --git a/.env.docker b/.env.docker index d2a47214..d5faa7cb 100644 --- a/.env.docker +++ b/.env.docker @@ -3,6 +3,7 @@ MIW_NAME=Base-Wallet MIW_BPN=BPNL000000000000 MIW_SHORT_DID=replace-short-did-of-endorser MIW_VERKEY=replace-verjkey-of-endorser +MIW_ALLOWLIST_DIDS= MIW_MEMBERSHIP_ORG=replace-name-of-organisation MIW_DB_JDBC_URL=jdbc:postgresql://db-host-placeholder:5432/db-name-placeholder?user=db-user-placeholder&password=db-password-placeholder diff --git a/.env.example b/.env.example index 47fb708c..79260a82 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,7 @@ MIW_BPN="BPNL000000000000" MIW_SHORT_DID="replace-short-did-of-endorser" # The verkey of the base wallet DID MIW_VERKEY="replace-verjkey-of-endorser" +MIW_ALLOWLIST_DIDS= MIW_MEMBERSHIP_ORG="replace-name-of-organisation" # Database connection diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml index b5b5a195..0bddf2bb 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -63,7 +63,7 @@ jobs: # GITHUB_TOKEN enables this github action to access github API and post comments in a pull request # token: ${{ secrets.GITHUB_TOKEN }} # enable_comments: true - exclude_paths: "docs/openapi_v200.json" + exclude_paths: "docs/openapi_v310.json" # Upload findings to GitHub Advanced Security Dashboard - name: Upload SARIF file for GitHub Advanced Security Dashboard diff --git a/README.md b/README.md index 9b89281c..92e36fb8 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ below. Here a few hints on how to set it up: | `MIW_DID` | String | DID of the base wallet, this wallet must be registered on ledger with the endorser role | | `MIW_VERKEY` | String | Verification key of the base wallet, this wallet must be registered on ledger with the endorser role | | `MIW_NAME` | String | Name of the base wallet | +|`MIW_ALLOWLIST_DIDS` | String | List of full DIDs seperated by comma ",". Those DIDs are allowed to send a connection request to managed wallets. Empty for public invitation allowance | | `MIW_MEMBERSHIP_ORG` | String | The name used in the Membership credential | | `BPDM_DATAPOOL_URL` | String | BPDM data pool API endpoint | | `BPDM_AUTH_CLIENT_ID` | String | client id for accessing the BPDM data pool endpoint | diff --git a/charts/managed-identity-wallets/Chart.yaml b/charts/managed-identity-wallets/Chart.yaml index 18dda4db..3990790b 100644 --- a/charts/managed-identity-wallets/Chart.yaml +++ b/charts/managed-identity-wallets/Chart.yaml @@ -15,8 +15,8 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.6.8 -appVersion: 3.1.2 +version: 0.6.9 +appVersion: 3.2.0 dependencies: - name: postgresql diff --git a/charts/managed-identity-wallets/README.md b/charts/managed-identity-wallets/README.md index 0718a4de..15a12742 100644 --- a/charts/managed-identity-wallets/README.md +++ b/charts/managed-identity-wallets/README.md @@ -1,6 +1,6 @@ # managed-identity-wallets -![Version: 0.6.8](https://img.shields.io/badge/Version-0.6.8-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 3.1.2](https://img.shields.io/badge/AppVersion-3.1.2-informational?style=flat-square) +![Version: 0.6.9](https://img.shields.io/badge/Version-0.6.9-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 3.2.0](https://img.shields.io/badge/AppVersion-3.2.0-informational?style=flat-square) Managed Identity Wallets Service @@ -42,6 +42,7 @@ Managed Identity Wallets Service | wallet.baseWalletShortDid | string | `""` | The short DID of the base wallet. It can be created with its verkey as described in https://github.com/eclipse-tractusx/managed-identity-wallets#Integrate-with-Indy-Ledger. It should be registered on the Indy ledger with role endorser. | | wallet.baseWalletVerkey | string | `""` | The verkey (public key) of the base wallet | | wallet.baseWalletName | string | `""` | The name of the base wallet | +| wallet.allowlistDids | string | `""` | A list of full DIDs seperated by comma ",". Those DIDs are allowed to send a connection request to managed wallets. Empty for public invitation allowance | | wallet.membershipOrganisation | string | `"Platform-A"` | The name used in the Membership credential | | revocation.refreshHour | string | `"3"` | At which hour (24-hour clock) the cron job should issue/update status-list credentials | | revocation.revocationServiceUrl | string | `"http://localhost:8086"` | The url of the revocation service | @@ -103,7 +104,7 @@ Managed Identity Wallets Service | postgresql.primary.extraVolumes[0].name | string | `"initdb"` | | | postgresql.primary.extraVolumes[0].emptyDir | object | `{}` | | | postgresql.primary.initContainers[0].name | string | `"initdb"` | | -| postgresql.primary.initContainers[0].image | string | `"ghcr.io/catenax-ng/tx-managed-identity-wallets_initdb:3.1.2"` | The image is built and used to initialize the database of MIW. The tag must equal the appVersion in Chart.yaml | +| postgresql.primary.initContainers[0].image | string | `"ghcr.io/catenax-ng/tx-managed-identity-wallets_initdb:3.2.0"` | The image is built and used to initialize the database of MIW. The tag must equal the appVersion in Chart.yaml | | postgresql.primary.initContainers[0].imagePullPolicy | string | `"Always"` | | | postgresql.primary.initContainers[0].command[0] | string | `"sh"` | | | postgresql.primary.initContainers[0].args[0] | string | `"-c"` | | diff --git a/charts/managed-identity-wallets/templates/deployment.yaml b/charts/managed-identity-wallets/templates/deployment.yaml index e9b71d2d..bd82cd3d 100644 --- a/charts/managed-identity-wallets/templates/deployment.yaml +++ b/charts/managed-identity-wallets/templates/deployment.yaml @@ -106,6 +106,8 @@ spec: value: {{ .Values.wallet.baseWalletVerkey }} - name: MIW_NAME value: {{ .Values.wallet.baseWalletName }} + - name: MIW_ALLOWLIST_DIDS + value: {{ .Values.wallet.allowlistDids }} - name: MIW_MEMBERSHIP_ORG value: {{ .Values.wallet.membershipOrganisation }} - name: MIW_OPENAPI_TITLE diff --git a/charts/managed-identity-wallets/values.yaml b/charts/managed-identity-wallets/values.yaml index 012361d5..d88a4820 100644 --- a/charts/managed-identity-wallets/values.yaml +++ b/charts/managed-identity-wallets/values.yaml @@ -50,6 +50,8 @@ wallet: baseWalletVerkey: "" # -- The name of the base wallet baseWalletName: "" + # -- A list of full DIDs seperated by comma ",". Those DIDs are allowed to send a connection request to managed wallets. Empty for public invitation allowance + allowlistDids: "" # -- The name used in the Membership credential membershipOrganisation: "Platform-A" # The configuration of revocation service in MIW @@ -167,7 +169,7 @@ postgresql: initContainers: - name: initdb # -- The image is built and used to initialize the database of MIW. The tag must equal the appVersion in Chart.yaml - image: ghcr.io/catenax-ng/tx-managed-identity-wallets_initdb:3.1.2 + image: ghcr.io/catenax-ng/tx-managed-identity-wallets_initdb:3.2.0 imagePullPolicy: Always command: - sh diff --git a/docs/ExternalWalletInteraction.md b/docs/ExternalWalletInteraction.md index 9840bd68..1b4e6b74 100644 --- a/docs/ExternalWalletInteraction.md +++ b/docs/ExternalWalletInteraction.md @@ -6,8 +6,9 @@ - Send a presentation to external wallet as defined in [Aries RFC 0454](https://github.com/hyperledger/aries-rfcs/tree/main/features/0454-present-proof-v2) - Current limitation: - - The managed wallets accept all invitations and credentials + - The managed wallets can accept invitations and credentials from other stored wallets and allowed DIDs. Currently, allowed DIDs are given in the configuration using the environment property `MIW_ALLOWLIST_DIDS`. This restriction can be deactivated by keeping the `MIW_ALLOWLIST_DIDS` empty - The managed wallets can issue credentials only to other managed or registered self-managed wallets after a connection is established + - The base wallet accepts connections from stored wallets - The issuer must be an Indy DID on the same ledger as the MIW - Credential revocation is not supported for credentials issued using the flows - Extensible credentials with extra properties are not supported https://www.w3.org/TR/vc-data-model/#extensibility. The only exception is the property `provenanceProof` which is a list of any type @@ -32,7 +33,7 @@ A Credential-Offer is sent from the external wallet using the established connec not implemented yet! ### Local Test Steps: -1. Follow the steps in `Steps for initial local deployment and wallet Creation` section in the `README.md` file +1. Follow the steps in `Steps for initial local deployment and wallet Creation` section in the `README.md` file. Make sure that either `MIW_ALLOWLIST_DIDS` is empty, or it includes the full DID of the external wallet 1. Import a new postman collection `Test-Acapy-SelfManagedWallet-Or-ExternalWallet.postman_collection.json` from `./dev-asset` 1. Run `Test-Acapy-SelfManagedWallet-Or-ExternalWallet/Get Connections` and make sure there are no connections. If there are any please delete them using `Remove Connection` 1. From `Test-Acapy-SelfManagedWallet-Or-ExternalWallet/Send Connection Request` using the public DID of the managed wallet diff --git a/gradle.properties b/gradle.properties index 9b5a3d11..2c4b7197 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,6 @@ acapy_java_library_version=0.7.33 kotlin.code.style=official kompendium_version=2.3.5 exposed_version=0.38.2 -version=3.1.2 +version=3.2.0 coverage_excludes=**/models/**,**/entities/**,**/Application*,**/services/IWalletService*,**/services/IAcaPyService*,**/services/AcaPyService*,**/services/IBusinessPartnerDataService*,**/services/IRevocationService*,**/services/RevocationService* diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/Application.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/Application.kt index 65c8bfc0..4be8714b 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/Application.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/Application.kt @@ -89,6 +89,12 @@ fun Application.module(testing: Boolean = false) { val utilsService = UtilsService(networkIdentifier = networkIdentifier) val baseWalletBpn = environment.config.property("wallet.baseWalletBpn").getString() + val allowlistDidsAsString = environment.config.property("wallet.allowlistDids").getString() + val allowlistDids = if (allowlistDidsAsString.isBlank()) { + emptyList() + } else { + allowlistDidsAsString.split(",") + } val acaPyConfig = WalletAndAcaPyConfig( networkIdentifier = networkIdentifier, baseWalletBpn = baseWalletBpn, @@ -99,6 +105,7 @@ fun Application.module(testing: Boolean = false) { adminApiKey = environment.config.property("acapy.adminApiKey").getString(), baseWalletAdminUrl = environment.config.property("acapy.baseWalletApiAdminUrl").getString(), baseWalletAdminApiKey = environment.config.property("acapy.baseWalletAdminApiKey").getString(), + allowlistDids = allowlistDids ) val revocationUrl = environment.config.property("revocation.baseUrl").getString() val revocationService = IRevocationService.createRevocationService(revocationUrl) diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/models/ssi/acapy/AcaPyDto.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/models/ssi/acapy/AcaPyDto.kt index e6b944ae..ee8c0d3e 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/models/ssi/acapy/AcaPyDto.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/models/ssi/acapy/AcaPyDto.kt @@ -75,7 +75,8 @@ data class WalletAndAcaPyConfig( val apiAdminUrl: String, val adminApiKey: String, val baseWalletAdminUrl: String, - val baseWalletAdminApiKey: String + val baseWalletAdminApiKey: String, + val allowlistDids: List ) @Serializable diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/models/ssi/acapy/AriesLdFormats.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/models/ssi/acapy/AriesLdFormats.kt new file mode 100644 index 00000000..4c967fa5 --- /dev/null +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/models/ssi/acapy/AriesLdFormats.kt @@ -0,0 +1,27 @@ +/******************************************************************************** + * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +package org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy + +class AriesLdFormats { + companion object { + val ARIES_LD_PROOF_VC_DETAIL_V_1_0 = "aries/ld-proof-vc-detail@v1.0" + val ARIES_LD_PROOF_VC_V_1_0 = "aries/ld-proof-vc@v1.0" + } +} diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/persistence/repositories/CredentialRepository.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/persistence/repositories/CredentialRepository.kt index cb92269c..8c2ec7b5 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/persistence/repositories/CredentialRepository.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/persistence/repositories/CredentialRepository.kt @@ -51,7 +51,7 @@ class CredentialRepository { query.andWhere { VerifiableCredentials.holderDid eq it } } type?.let { - query.andWhere { VerifiableCredentials.type eq it } + query.andWhere { VerifiableCredentials.type like "%$it%" } } credentialId?.let { query.andWhere { VerifiableCredentials.credentialId eq it } diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/persistence/repositories/WalletRepository.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/persistence/repositories/WalletRepository.kt index 587b8335..535af833 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/persistence/repositories/WalletRepository.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/persistence/repositories/WalletRepository.kt @@ -43,6 +43,14 @@ class WalletRepository { } } + fun getWalletOrNull(identifier: String): Wallet? { + return transaction { + Wallet.find { + (Wallets.did eq identifier) or (Wallets.bpn eq identifier) or (Wallets.walletId eq identifier) + }.firstOrNull() + } + } + @Throws(ConflictException::class) fun checkWalletAlreadyExists(identifier: String) { if (!Wallet.find { (Wallets.did eq identifier) or (Wallets.bpn eq identifier) }.empty()) { @@ -51,7 +59,7 @@ class WalletRepository { } fun isWalletExists(identifier: String): Boolean { - return transaction{ + return transaction { !Wallet.find { (Wallets.did eq identifier) or (Wallets.bpn eq identifier) }.empty() } } diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/AcaPyService.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/AcaPyService.kt index f76e67d6..4de3adb2 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/AcaPyService.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/AcaPyService.kt @@ -47,6 +47,8 @@ import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.VerifyReques import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.VerifyResponse import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.WalletAndAcaPyConfig import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.WalletKey +import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.WalletList +import org.hyperledger.acy_py.generated.model.DID import org.hyperledger.acy_py.generated.model.TransactionJobs import org.hyperledger.acy_py.generated.model.V20CredRequestRequest import org.hyperledger.acy_py.generated.model.V20CredStoreRequest @@ -65,6 +67,7 @@ import org.hyperledger.aries.api.issue_credential_v2.V2CredentialExchangeFree import org.hyperledger.aries.api.jsonld.ProofType import org.hyperledger.aries.api.jsonld.VerifiableCredential import org.hyperledger.aries.api.multitenancy.RemoveWalletRequest +import org.hyperledger.aries.api.wallet.ListWalletDidFilter import java.util.* /** @@ -88,7 +91,8 @@ class AcaPyService( baseWalletVerkey = acaPyConfig.baseWalletVerkey, adminApiKey = "", // don't expose the api key outside the AcaPyService baseWalletAdminUrl = acaPyConfig.baseWalletAdminUrl, - baseWalletAdminApiKey = "" // don't expose the api key outside the AcaPyService + baseWalletAdminApiKey = "", // don't expose the api key outside the AcaPyService + allowlistDids = acaPyConfig.allowlistDids ) } @@ -131,6 +135,13 @@ class AcaPyService( } } + override suspend fun isDidOfWallet(did: String, tokenOfWallet: String?): Boolean { + val acapyClient = getAcapyClient(tokenOfWallet) + val filter = ListWalletDidFilter.builder().did(did).method(DID.MethodEnum.SOV).build() + val listOfDids = acapyClient.walletDid(filter) + return !listOfDids.isEmpty && listOfDids.get().isNotEmpty() + } + override suspend fun registerDidOnLedgerUsingBaseWallet( didRegistration: DidRegistration ): DidRegistrationResult { diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/AcaPyWalletServiceImpl.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/AcaPyWalletServiceImpl.kt index d4930509..2ce5abcb 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/AcaPyWalletServiceImpl.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/AcaPyWalletServiceImpl.kt @@ -19,7 +19,6 @@ package org.eclipse.tractusx.managedidentitywallets.services -import com.google.gson.GsonBuilder import foundation.identity.jsonld.JsonLDUtils import kotlinx.coroutines.runBlocking import kotlinx.serialization.decodeFromString @@ -106,7 +105,6 @@ class AcaPyWalletServiceImpl( companion object { private val log = LoggerFactory.getLogger(this::class.java) - private val gson = GsonBuilder().create() } override fun getWallet(identifier: String, withCredentials: Boolean): WalletDto { @@ -216,8 +214,8 @@ class AcaPyWalletServiceImpl( } acaPyService.sendConnectionRequest( - didOfTheirWallet = getBaseWallet().did, - usePublicDid = false, + didOfTheirWallet = utilsService.getIdentifierOfDid(getBaseWallet().did), + usePublicDid = false, // It has no public DID yet alias = "endorser", token = createdSubWalletDto.token, label = walletToCreate.bpn @@ -481,7 +479,7 @@ class AcaPyWalletServiceImpl( override suspend fun addService(identifier: String, serviceDto: DidServiceDto) { log.debug("Add Service Endpoint for $identifier") - utilsService.checkSupportedId(serviceDto.id) + utilsService.checkSupportedServiceId(serviceDto.id) val walletData = getWalletExtendedInformation(identifier) val didDoc = resolveDocument(walletData.did) if (!didDoc.services.isNullOrEmpty()) { @@ -518,7 +516,7 @@ class AcaPyWalletServiceImpl( serviceUpdateRequestDto: DidServiceUpdateRequestDto ) { log.debug("Update Service Endpoint for $identifier") - utilsService.checkSupportedId(id) + utilsService.checkSupportedServiceId(id) val walletData = getWalletExtendedInformation(identifier) val didDoc = resolveDocument(walletData.did) if (!didDoc.services.isNullOrEmpty()) { @@ -1052,6 +1050,52 @@ class AcaPyWalletServiceImpl( ) } + override fun validateConnectionRequestForManagedWallets(connection: ConnectionRecord): Boolean { + val allowlistDids = acaPyService.getWalletAndAcaPyConfig().allowlistDids + val filteredAllowlistShortDids = allowlistDids.filter { + it.startsWith(utilsService.getOldDidMethodPrefixWithNetworkIdentifier()) + || it.startsWith(utilsService.getDidMethodPrefixWithNetworkIdentifier()) + } + .map { did -> utilsService.getIdentifierOfDid(did) } + val connectionDid = utilsService.getIdentifierOfDid( + did = connection.theirPublicDid ?: connection.theirDid + ) + if (allowlistDids.isNotEmpty() + && !filteredAllowlistShortDids.contains(connectionDid) + && !walletRepository.isWalletExists(utilsService.convertToFullDidIfShort(connectionDid)) + ) { + log.warn( + "Connection request ${connection.connectionId} and DID $connectionDid " + + "has been rejected due to unfulfilled conditions" + ) + return false + } + return true + } + + override suspend fun validateConnectionRequestForBaseWallet( + connection: ConnectionRecord, + bpn: String + ): WalletDto? { + val connectionDid = utilsService.getIdentifierOfDid( + did = connection.theirPublicDid ?: connection.theirDid + ) + val wallet = walletRepository.getWalletOrNull(bpn) + if (wallet != null) { + val walletData = walletRepository.toWalletCompleteDataObject(wallet) + // It could be an internal AcaPy DID (e.g. when a managed wallet is created) + return if (acaPyService.isDidOfWallet(connectionDid, wallet.walletToken)) { + getWallet(walletData.did) + } else { + log.warn("The used did $connectionDid in connection ${connection.connectionId} " + + "does not belong to the given BPN $bpn") + null + } + } + log.warn("A connection ${connection.connectionId} from a not stored wallet $bpn is forbidden") + return null + } + override suspend fun setAuthorMetaData( walletId: String, connectionId: String diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/BaseWalletAriesEventHandler.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/BaseWalletAriesEventHandler.kt index 15e4541b..d5d7f264 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/BaseWalletAriesEventHandler.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/BaseWalletAriesEventHandler.kt @@ -24,6 +24,7 @@ import kotlinx.serialization.json.Json import org.eclipse.tractusx.managedidentitywallets.models.WalletDto import org.eclipse.tractusx.managedidentitywallets.models.ssi.IssuedVerifiableCredentialRequestDto import org.eclipse.tractusx.managedidentitywallets.models.ssi.JsonLdTypes +import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.AriesLdFormats import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.Rfc23State import org.hyperledger.aries.api.connection.ConnectionRecord import org.hyperledger.aries.api.issue_credential_v1.CredentialExchangeState @@ -47,27 +48,36 @@ class BaseWalletAriesEventHandler( private val baseWalletCredentialTypes = JsonLdTypes.getBaseWalletCredentialTypes() private val log = LoggerFactory.getLogger(this::class.java) - // Connection only with registered wallets override fun handleConnection(walletId: String?, connection: ConnectionRecord) { super.handleConnection(null, connection) log.debug("Connection ${connection.connectionId} is in state ${connection.rfc23State}") when(connection.rfc23State) { Rfc23State.REQUEST_RECEIVED.toString() -> { - //TODO accept only from whitelisted public DIDs transaction { - val theirWallet = walletService.getWallet( - identifier = connection.theirLabel, // The BPN - withCredentials = false - ) - walletService.addConnection( - connectionId = connection.connectionId, - connectionOwnerDid = walletService.getBaseWallet().did, - connectionTargetDid = theirWallet.did, - connectionState = connection.rfc23State - ) runBlocking { - walletService.setEndorserMetaDataForConnection(connection.connectionId) - walletService.acceptConnectionRequest(walletService.getBaseWallet().did, connection) + // Accept only requests from managed wallets where the BPN included as a label + if (!connection.theirLabel.isNullOrBlank()) { + val wallet = walletService.validateConnectionRequestForBaseWallet( + connection = connection, + bpn = connection.theirLabel + ) + if (wallet != null) { + walletService.addConnection( + connectionId = connection.connectionId, + connectionOwnerDid = walletService.getBaseWallet().did, + connectionTargetDid = wallet.did, + connectionState = connection.rfc23State + ) + if (!wallet.isSelfManaged) { + walletService.setEndorserMetaDataForConnection(connection.connectionId) + } + walletService.acceptConnectionRequest(walletService.getBaseWallet().did, connection) + } + } else { + log.warn("Connection request ${connection.connectionId} from " + + "${connection.theirPublicDid ?: connection.theirDid} " + + "has been rejected due to missing `theirLabel` property") + } } } } @@ -136,10 +146,20 @@ class BaseWalletAriesEventHandler( when(v20Credential.state) { CredentialExchangeState.OFFER_RECEIVED -> { runBlocking { - walletService.acceptReceivedOfferVc(walletService.getBaseWallet().did, v20Credential) + if (v20Credential.credOffer.formats[0].format == AriesLdFormats.ARIES_LD_PROOF_VC_DETAIL_V_1_0) { + walletService.acceptReceivedOfferVc(walletService.getBaseWallet().did, v20Credential) + } else { + log.warn("CredExRecord ${v20Credential.credentialExchangeId} has unsupported format " + + "${v20Credential.credOffer.formats[0].format}") + } } } CredentialExchangeState.CREDENTIAL_ISSUED -> { + if (v20Credential.credIssue.formats[0].format != AriesLdFormats.ARIES_LD_PROOF_VC_V_1_0) { + log.warn("CredExRecord ${v20Credential.credentialExchangeId} has unsupported format " + + "${v20Credential.credIssue.formats[0].format}") + return + } try { val issuedCred: IssuedVerifiableCredentialRequestDto = Json.decodeFromString( IssuedVerifiableCredentialRequestDto.serializer(), diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/IAcaPyService.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/IAcaPyService.kt index 7589aaaa..22e4b275 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/IAcaPyService.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/IAcaPyService.kt @@ -249,6 +249,14 @@ interface IAcaPyService { */ suspend fun setDidAsPublicUsingEndorser(did: String, token: String) + /** + * Checks if the DID belongs to the wallet. + * @param did the DID to check + * @param tokenOfWallet the token of the wallet that is supposed to be the owner of the DID. null for the base wallet. + * @return true if the DID belong to the wallet. otherwise false + */ + suspend fun isDidOfWallet(did: String, tokenOfWallet: String?): Boolean + companion object { /** * Creates the internal Acapy service that is used by the wallet service. diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/IWalletService.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/IWalletService.kt index 31b97eea..140873e2 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/IWalletService.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/IWalletService.kt @@ -406,6 +406,21 @@ interface IWalletService { */ suspend fun setCommunicationEndpointUsingEndorsement(walletId: String) + /** + * Checks if a received connection request to managed wallet is allowed to be processed. + * @param connection the received connection request [ConnectionRecord] + * @return true if the connection request is allowed to be processed + */ + fun validateConnectionRequestForManagedWallets(connection: ConnectionRecord): Boolean + + /** + * Checks if a received connection request to base wallet is allowed to be processed. + * @param connection the received connection request [ConnectionRecord] + * @param bpn the allegedly BPN of the requester wallet. It must be verified + * @return the stored wallet of the requester, null in case of errors + */ + suspend fun validateConnectionRequestForBaseWallet(connection: ConnectionRecord, bpn: String): WalletDto? + companion object { private val log = LoggerFactory.getLogger(this::class.java) diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/ManagedWalletsAriesEventHandler.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/ManagedWalletsAriesEventHandler.kt index a5d6e9e7..123364f3 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/ManagedWalletsAriesEventHandler.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/ManagedWalletsAriesEventHandler.kt @@ -20,6 +20,7 @@ package org.eclipse.tractusx.managedidentitywallets.services import kotlinx.coroutines.runBlocking +import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.AriesLdFormats import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.Rfc23State import org.hyperledger.aries.api.connection.ConnectionRecord import org.hyperledger.aries.api.connection.ConnectionTheirRole @@ -52,15 +53,16 @@ class ManagedWalletsAriesEventHandler( ) when (connection.rfc23State) { Rfc23State.REQUEST_RECEIVED.toString() -> { - //TODO accept only from whitelisted public DIDs runBlocking { - walletService.acceptConnectionRequest(walletId, connection) + if (walletService.validateConnectionRequestForManagedWallets(connection)) { + walletService.acceptConnectionRequest(walletId, connection) + } } } Rfc23State.COMPLETED.toString() -> { runBlocking { val theirDid = - utilsService.convertIfShortDid( + utilsService.convertToFullDidIfShort( did = connection.theirPublicDid ?: connection.theirDid ) transaction { @@ -103,12 +105,22 @@ class ManagedWalletsAriesEventHandler( when (v20Credential.state) { CredentialExchangeState.OFFER_RECEIVED -> { runBlocking { - walletService.acceptReceivedOfferVc(walletId!!, v20Credential) + if (v20Credential.credOffer.formats[0].format == AriesLdFormats.ARIES_LD_PROOF_VC_DETAIL_V_1_0) { + walletService.acceptReceivedOfferVc(walletId!!, v20Credential) + } else { + log.warn("CredExRecord ${v20Credential.credentialExchangeId} has unsupported format " + + "${v20Credential.credOffer.formats[0].format}") + } } } CredentialExchangeState.CREDENTIAL_RECEIVED -> { runBlocking { - walletService.acceptAndStoreReceivedIssuedVc(walletId!!, v20Credential) + if (v20Credential.credIssue.formats[0].format == AriesLdFormats.ARIES_LD_PROOF_VC_V_1_0) { + walletService.acceptAndStoreReceivedIssuedVc(walletId!!, v20Credential) + } else { + log.warn("CredExRecord ${v20Credential.credentialExchangeId} has unsupported format " + + "${v20Credential.credIssue.formats[0].format}") + } } } CredentialExchangeState.CREDENTIAL_ISSUED -> { diff --git a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/UtilsService.kt b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/UtilsService.kt index b349cf14..1479bec0 100644 --- a/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/UtilsService.kt +++ b/src/main/kotlin/org/eclipse/tractusx/managedidentitywallets/services/UtilsService.kt @@ -81,7 +81,7 @@ class UtilsService(private val networkIdentifier: String) { * @param id the Id of the service to be checked * @throws NotImplementedException if the given id is not supported */ - fun checkSupportedId(id: String) { + fun checkSupportedServiceId(id: String) { if (!arrayOfSupportedIds.contains(id)) { throw NotImplementedException("The Id $id of the service is not supported") } @@ -172,8 +172,10 @@ class UtilsService(private val networkIdentifier: String) { * @param did the DID as string * @return a full DID if the input is a short DID, the input otherwise */ - fun convertIfShortDid(did: String): String { - return if (!did.startsWith("did:")) { + fun convertToFullDidIfShort(did: String): String { + return if (!did.startsWith(getOldDidMethodPrefixWithNetworkIdentifier()) + && !did.startsWith(getDidMethodPrefixWithNetworkIdentifier()) + ) { getDidMethodPrefixWithNetworkIdentifier() + did } else { did diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index f3fe1930..ab0f7ed1 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -29,6 +29,7 @@ wallet { baseWalletShortDid = ${MIW_SHORT_DID} baseWalletVerkey = ${MIW_VERKEY} baseWalletName = ${MIW_NAME} + allowlistDids = ${MIW_ALLOWLIST_DIDS} membershipOrganisation= ${MIW_MEMBERSHIP_ORG} } diff --git a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/AcaPyMockedService.kt b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/AcaPyMockedService.kt index 80c0c5f6..e922c4fe 100644 --- a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/AcaPyMockedService.kt +++ b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/AcaPyMockedService.kt @@ -43,7 +43,6 @@ import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.SignRequest import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.VerifyRequest import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.VerifyResponse import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.WalletAndAcaPyConfig -import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.WalletList import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.WalletSettings import org.eclipse.tractusx.managedidentitywallets.services.IAcaPyService import org.hyperledger.acy_py.generated.model.AttachDecorator @@ -79,7 +78,8 @@ class AcaPyMockedService( apiAdminUrl = "", adminApiKey = "TestAdminApiKey", baseWalletAdminUrl = "", - baseWalletAdminApiKey = "" + baseWalletAdminApiKey = "", + allowlistDids = listOf() ) } @@ -330,6 +330,10 @@ class AcaPyMockedService( TODO("Not yet implemented") } + override suspend fun isDidOfWallet(did: String, tokenOfWallet: String?): Boolean { + TODO("Not yet implemented") + } + private fun createRandomString(): String { return (1..25) .map { SecureRandom().nextInt(charPool.size) } diff --git a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/BaseWalletAriesEventHandlerTest.kt b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/BaseWalletAriesEventHandlerTest.kt index f8702862..2fd53cd4 100644 --- a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/BaseWalletAriesEventHandlerTest.kt +++ b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/BaseWalletAriesEventHandlerTest.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.runBlocking import okhttp3.internal.toImmutableList import org.eclipse.tractusx.managedidentitywallets.models.WalletExtendedData import org.eclipse.tractusx.managedidentitywallets.models.ssi.InvitationRequestDto +import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.AriesLdFormats import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.Rfc23State import org.eclipse.tractusx.managedidentitywallets.persistence.repositories.ConnectionRepository import org.eclipse.tractusx.managedidentitywallets.persistence.repositories.CredentialRepository @@ -35,6 +36,7 @@ import org.eclipse.tractusx.managedidentitywallets.plugins.configurePersistence import org.eclipse.tractusx.managedidentitywallets.services.AcaPyService import org.eclipse.tractusx.managedidentitywallets.services.AcaPyWalletServiceImpl import org.eclipse.tractusx.managedidentitywallets.services.BaseWalletAriesEventHandler +import org.eclipse.tractusx.managedidentitywallets.services.IAcaPyService import org.eclipse.tractusx.managedidentitywallets.services.IBusinessPartnerDataService import org.eclipse.tractusx.managedidentitywallets.services.IRevocationService import org.eclipse.tractusx.managedidentitywallets.services.IWalletService @@ -43,6 +45,7 @@ import org.eclipse.tractusx.managedidentitywallets.services.UtilsService import org.eclipse.tractusx.managedidentitywallets.services.WebhookServiceImpl import org.hyperledger.acy_py.generated.model.AttachDecorator import org.hyperledger.acy_py.generated.model.AttachDecoratorData +import org.hyperledger.acy_py.generated.model.V20CredFormat import org.hyperledger.acy_py.generated.model.V20CredIssue import org.hyperledger.aries.api.connection.ConnectionRecord import org.hyperledger.aries.api.issue_credential_v1.CredentialExchangeState @@ -121,6 +124,7 @@ class BaseWalletAriesEventHandlerTest { private lateinit var webhookService: IWebhookService private lateinit var walletServiceSpy: IWalletService private lateinit var ariesEventHandler: BaseWalletAriesEventHandler + private lateinit var acaPyServiceMock: IAcaPyService @BeforeTest fun setup() { @@ -137,10 +141,11 @@ class BaseWalletAriesEventHandlerTest { webhookService = WebhookServiceImpl( webhookRepository, HttpClient(mockEngine) ) - val acaPyServiceMock = mock() + acaPyServiceMock = mock() doNothing().whenever(acaPyServiceMock).subscribeBaseWalletForWebSocket() whenever(acaPyServiceMock.getWalletAndAcaPyConfig()).thenReturn(EnvironmentTestSetup.walletAcapyConfig) runBlocking { + whenever(acaPyServiceMock.isDidOfWallet(any(), anyOrNull())).thenReturn(true) val connectionRecordRequest = ConnectionRecord() connectionRecordRequest.connectionId = "connection-id-123" connectionRecordRequest.rfc23State = Rfc23State.REQUEST_SENT.toString() @@ -208,6 +213,7 @@ class BaseWalletAriesEventHandlerTest { newConnectionRecord.connectionId = connectionId newConnectionRecord.requestId = connectionThreadId newConnectionRecord.theirLabel = holderWallet.bpn + newConnectionRecord.theirPublicDid = holderWallet.did // Test `handleConnection` for state COMPLETED ariesEventHandler.handleConnection(null, newConnectionRecord) @@ -231,6 +237,57 @@ class BaseWalletAriesEventHandlerTest { } } + @Test + fun testHandleReceivedConnectionRequestUnknownWallet() { + withTestApplication({ + EnvironmentTestSetup.setupEnvironment(environment) + configurePersistence() + }) { + runBlocking { + // Setup + addWallets(walletRepo, walletServiceSpy, listOf(issuerWallet, holderWallet)) + + assertDoesNotThrow { + runBlocking { + walletServiceSpy.sendInvitation( + holderWallet.did, + invitationRequestDto = InvitationRequestDto( + theirPublicDid = issuerWallet.did, + myLabel = "testLabel", + alias = "ToBaseWallet" + ) + ) + } + } + + // Mock super call to tenantAwareEventHandler + val tenantAwareEventHandler = mock() + doNothing().whenever(tenantAwareEventHandler).handleConnection(anyOrNull(), any()) + + whenever(acaPyServiceMock.isDidOfWallet(any(), anyOrNull())).thenReturn(false) + + val newConnectionRecord = ConnectionRecord() + newConnectionRecord.rfc23State = Rfc23State.REQUEST_RECEIVED.toString() + newConnectionRecord.connectionId = connectionId + newConnectionRecord.requestId = connectionThreadId + newConnectionRecord.theirLabel = holderWallet.bpn + newConnectionRecord.theirPublicDid = "did:sov:unknowwallet" + + ariesEventHandler.handleConnection(null, newConnectionRecord) + + verify(walletServiceSpy, times(0)).setEndorserMetaDataForConnection(any()) + verify(walletServiceSpy, times(0)).acceptConnectionRequest(any(), any()) + + transaction { + walletRepo.deleteWallet(issuerWallet.bpn) + walletRepo.deleteWallet(holderWallet.bpn) + connectionRepository.deleteConnections(issuerWallet.did) + connectionRepository.deleteConnections(holderWallet.did) + } + } + } + } + @Test fun testHandleAriesEventsWithManagedWallet() { withTestApplication({ @@ -478,6 +535,9 @@ class BaseWalletAriesEventHandlerTest { dataDecorator.data = data val credentialTildeAttach = listOf(dataDecorator) val credIssue = V20CredIssue() + val format = V20CredFormat() + format.format = AriesLdFormats.ARIES_LD_PROOF_VC_V_1_0 + credIssue.formats = listOf(format) credIssue.credentialsTildeAttach = credentialTildeAttach.toImmutableList() val v20CredentialExchange = V20CredExRecord() v20CredentialExchange.credIssue = credIssue diff --git a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/BusinessPartnerServiceTest.kt b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/BusinessPartnerServiceTest.kt index 90e28dbc..74fa4fb5 100644 --- a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/BusinessPartnerServiceTest.kt +++ b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/BusinessPartnerServiceTest.kt @@ -457,7 +457,7 @@ class BusinessPartnerServiceTest { assertEquals(false, resultWithException) assertEquals(false, resultWithEmptyProofForIssuedCred) - // Test no credentials are created and stored due mocked exceptions + // Test no credentials are created and stored due to mocked exceptions transaction { val extractedWallet = walletServiceSpy.getWallet(holderWallet.did, true) assertEquals(0, extractedWallet.vcs.size) @@ -617,7 +617,7 @@ class BusinessPartnerServiceTest { val spyBpdmService = spy(bpdmService) // Test `pullDataAndUpdateBaseWalletCredentialsAsync` for a created Wallet - // no credentials will be created because The HTTP.OK state is never reached due the mockEngine + // no credentials will be created because The HTTP.OK state is never reached due to the mockEngine assertDoesNotThrow { runBlocking { spyBpdmService.pullDataAndUpdateBaseWalletCredentialsAsync(holderWallet.did).await() diff --git a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/EnvironmentTestSetup.kt b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/EnvironmentTestSetup.kt index 8e31b8a7..94b1d14a 100644 --- a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/EnvironmentTestSetup.kt +++ b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/EnvironmentTestSetup.kt @@ -51,7 +51,8 @@ object EnvironmentTestSetup { baseWalletVerkey = DEFAULT_VERKEY, adminApiKey = "adminApiKey", baseWalletAdminUrl = "baseWalletAdminUrl", - baseWalletAdminApiKey = "baseWalletAdminApiKey" + baseWalletAdminApiKey = "baseWalletAdminApiKey", + allowlistDids = emptyList() ) const val EXTRA_TEST_BPN = "BPNL0Test" @@ -64,7 +65,7 @@ object EnvironmentTestSetup { val webhookRepository = WebhookRepository() private val acaPyMockedService = AcaPyMockedService(DEFAULT_BPN, NETWORK_ID) - val revocationMockedService = RevocationMockedService(NETWORK_ID) + val revocationMockedService = RevocationMockedService() val webhookService = IWebhookService.createWebhookService(webhookRepository) val utilsService = UtilsService(NETWORK_ID) @@ -135,6 +136,8 @@ object EnvironmentTestSetup { put("revocation.baseUrl", System.getenv("REVOCATION_URL") ?: "http://0.0.0.0:8086") put("revocation.createStatusListCredentialAtHour", System.getenv("REVOCATION_CREATE_STATUS_LIST_CREDENTIAL_AT_HOUR") ?: "3") + put("wallet.allowlistDids", System.getenv("MIW_ALLOWLIST_DIDS") ?: "") + } // just a keepAliveConnection DriverManager.getConnection(jdbcUrl) diff --git a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/ManagedWalletAriesEventHandlerTest.kt b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/ManagedWalletAriesEventHandlerTest.kt index b3885e1b..a5dd1f54 100644 --- a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/ManagedWalletAriesEventHandlerTest.kt +++ b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/ManagedWalletAriesEventHandlerTest.kt @@ -25,6 +25,7 @@ import io.ktor.server.testing.* import kotlinx.coroutines.runBlocking import org.eclipse.tractusx.managedidentitywallets.models.WalletExtendedData import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.Rfc23State +import org.eclipse.tractusx.managedidentitywallets.models.ssi.acapy.WalletAndAcaPyConfig import org.eclipse.tractusx.managedidentitywallets.persistence.repositories.ConnectionRepository import org.eclipse.tractusx.managedidentitywallets.persistence.repositories.CredentialRepository import org.eclipse.tractusx.managedidentitywallets.persistence.repositories.WalletRepository @@ -131,6 +132,7 @@ class ManagedWalletAriesEventHandlerTest { runBlocking { val connectionRecord = ConnectionRecord() connectionRecord.rfc23State = Rfc23State.RESPONSE_SENT.toString() + connectionRecord.theirDid = "shortDID123" whenever(acaPyServiceMocked.acceptConnectionRequest(any(), any())) .thenAnswer { connectionRecord } } @@ -192,6 +194,7 @@ class ManagedWalletAriesEventHandlerTest { // Mock acceptInvitationRequest call to Acapy Service val connectionRecord = ConnectionRecord() connectionRecord.rfc23State = Rfc23State.RESPONSE_SENT.toString() + connectionRecord.theirDid = "shortdid123" doAnswer { connectionRecord } .whenever(acaPyServiceMocked).acceptConnectionRequest(any(), anyOrNull()) val managedWalletAriesEventHandler = ManagedWalletsAriesEventHandler( @@ -205,7 +208,7 @@ class ManagedWalletAriesEventHandlerTest { managedWalletAriesEventHandler.handleEvent( holderWallet.walletId, "connections", - """{"rfc23_state":"request-received", "connection_id":"$connectionId"}""".trimIndent() + """{"rfc23_state":"request-received", "connection_id":"$connectionId", "their_did":"shortdid123"}""".trimIndent() ) } verify(walletServiceSpy, times(1)).acceptConnectionRequest(any(), any()) @@ -241,6 +244,84 @@ class ManagedWalletAriesEventHandlerTest { } } + @Test + fun testHandleAriesEventsConnectionsWithAllowList() { + withTestApplication({ + EnvironmentTestSetup.setupEnvironment(environment) + configurePersistence() + configureSerialization() + }) { + runBlocking { + // Setup + addWallets(walletRepo, walletServiceSpy, listOf(issuerWallet, holderWallet)) + whenever(acaPyServiceMocked.getWalletAndAcaPyConfig()).thenReturn( + WalletAndAcaPyConfig( + apiAdminUrl = "apiAdminUrl", + networkIdentifier = "networkIdentifier", + baseWalletBpn = EnvironmentTestSetup.DEFAULT_BPN, + baseWalletDID = EnvironmentTestSetup.DEFAULT_DID, + baseWalletVerkey = EnvironmentTestSetup.DEFAULT_VERKEY, + adminApiKey = "adminApiKey", + baseWalletAdminUrl = "baseWalletAdminUrl", + baseWalletAdminApiKey = "baseWalletAdminApiKey", + allowlistDids = listOf("did:sov:Did1","did:sov:Did2","did:sov:Did3") + ) + ) + // Mock acceptInvitationRequest call to Acapy Service + val connectionRecord = ConnectionRecord() + connectionRecord.rfc23State = Rfc23State.RESPONSE_SENT.toString() + doAnswer { connectionRecord } + .whenever(acaPyServiceMocked).acceptConnectionRequest(any(), anyOrNull()) + val managedWalletAriesEventHandler = ManagedWalletsAriesEventHandler( + walletService = walletServiceSpy, + revocationService = revocationServiceMocked, + webhookService = webhookServiceMocked, + utilsService = utilsService + ) + // Test `handleEvent` for connection with rfc23 state request-received + assertDoesNotThrow { + managedWalletAriesEventHandler.handleEvent( + holderWallet.walletId, + "connections", + """{"rfc23_state":"request-received", "their_public_did": "did:sov:test", "connection_id":"$connectionId"}""".trimIndent() + ) + } + verify(walletServiceSpy, times(0)).acceptConnectionRequest(any(), any()) + + whenever(acaPyServiceMocked.getWalletAndAcaPyConfig()).thenReturn( + WalletAndAcaPyConfig( + apiAdminUrl = "apiAdminUrl", + networkIdentifier = "networkIdentifier", + baseWalletBpn = EnvironmentTestSetup.DEFAULT_BPN, + baseWalletDID = EnvironmentTestSetup.DEFAULT_DID, + baseWalletVerkey = EnvironmentTestSetup.DEFAULT_VERKEY, + adminApiKey = "adminApiKey", + baseWalletAdminUrl = "baseWalletAdminUrl", + baseWalletAdminApiKey = "baseWalletAdminApiKey", + allowlistDids = listOf("shortDid1","did:sov:test","shortDid3") + ) + ) + + // Test `handleEvent` for connection with rfc23 state request-received + assertDoesNotThrow { + managedWalletAriesEventHandler.handleEvent( + holderWallet.walletId, + "connections", + """{"rfc23_state":"request-received", "their_public_did": "did:sov:test", "connection_id":"$connectionId"}""".trimIndent() + ) + } + verify(walletServiceSpy, times(1)).acceptConnectionRequest(any(), any()) + + // clean data + transaction { + walletRepo.deleteWallet(issuerWallet.bpn) + walletRepo.deleteWallet(holderWallet.bpn) + connectionRepository.deleteConnections(holderWallet.did) + } + } + } + } + @Test fun testHandleAriesEventsConnectionsFromEndorser() { withTestApplication({ diff --git a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/RevocationMockedService.kt b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/RevocationMockedService.kt index 5d86878f..f3c73319 100644 --- a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/RevocationMockedService.kt +++ b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/RevocationMockedService.kt @@ -25,7 +25,7 @@ import org.eclipse.tractusx.managedidentitywallets.models.ssi.VerifiableCredenti import org.eclipse.tractusx.managedidentitywallets.services.IRevocationService import java.util.* -class RevocationMockedService(private val networkIdentifier: String): IRevocationService { +class RevocationMockedService(): IRevocationService { override suspend fun registerList(profileName: String, issueCredential: Boolean) = UUID.randomUUID().toString() diff --git a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/WalletsTest.kt b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/WalletsTest.kt index f5efa631..97ed774d 100644 --- a/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/WalletsTest.kt +++ b/src/test/kotlin/org/eclipse/tractusx/managedidentitywallets/WalletsTest.kt @@ -389,6 +389,7 @@ class WalletsTest { "AdminApiKey", "", "", + listOf() ) ) whenever(acapyService.createSubWallet(any()))