diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 000000000..fa0eb296c --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,8 @@ +## Maintainers + +### Active Maintainers +| name | Github | Discord | +|--------------------|----------------------------------------------------|----------------| +| Philipp Etschel | [@etschelp](https://github.com/etschelp) | etschelp#7436 | +| Tim Schlagenhaufer | [@schlagtim](https://github.com/schlagtim) | schlagtim#1600 | +| Driton Goxhufi | [@DritonGoxhufi](https://github.com/DritonGoxhufi) | Driton#1353 | \ No newline at end of file diff --git a/README.md b/README.md index e8a248aed..39289cee5 100644 --- a/README.md +++ b/README.md @@ -30,55 +30,56 @@ The Business Partner Agent is built on top of the Hyperledger Self-Sovereign Ide ## Features in Detail -| Role/Feature | Flow | Protocol Version | -|------------------|----------------------------------------------------------------------------------------------------------------|-----------------------------------| -| Issuer | | | -| | auto: issue credential | indy: v1, v2
w3c: v2 | -| | manual: send credential offer to holder | indy: v1, v2
w3c: v2 | -| | manual: receive credential proposal from holder | indy: v1, v2
w3c: v2 | -| | manual: decline credential proposal from holder and provide reason | indy: v1, v2
w3c: v2 | -| | send credential offer as invitation attachment | indy: v2 | -| | revoke issued credential (requires tails server) | indy: v1, v2
w3c: n/a | -| | send revocation notification | indy: v1, v2
w3c: n/a | -| Holder | | | -| | auto: receive credential | indy: v1, v2
w3c: v2 | -| | manual: send credential proposal to issuer (based on document) | indy: v1, v2
w3c: v2 | -| | manual: receive credential offer from issuer | indy: v1, v2
w3c: v2 | -| | manual: decline credential offer from issuer | indy: v1, v2
w3c: v2 | -| | scheduled revocation check on all received credentials | indy: v1, v2
w3c: n/a | -| | receive revocation notification | indy: v1, v2
w3c: n/a | -| Prover | | | -| | auto: send presentation to verifier | indy: v1, v2
w3c: v2 | -| | auto: answer presentation request | indy: v1, v2
w3c: v2 | -| | manual: accept/decline presentation request and provide reason | indy: v1, v2
w3c: v2 | -| Verifier | | | -| | auto: request presentation from prover based on proof template | indy: v1, v2
w3c: v2 | -| | auto: receive and verify presentation from prover | indy: v1, v2
w3c: v2 | -| Proof-Template | | | -| | prepared presentation request templates for indy and w3c presentation exchanges | | -| | query by: schema attributes. restrict by: attribute value, predicates (<, >, <=, >=), schema, and issuer did | indy | -| | query by: schema attributes. restrict by: attribute value, schema, and issuer did | w3c | -| Connection | | | -| | connect by did:sov, did:web (if endpoint is aca-py) | did-exchange | -| | receive invitation by URL | connection-protocol, OOB | -| | create invitation (barcode or URL) | connection-protocol, OOB | -| | auto: accept incoming connection | did-exchange, connection-protocol | -| | manual: accept incoming connection | did-exchange, connection-protocol | -| | optional: scheduled trust ping to check connection status | n/a | -| | tag a connection, e.g. as trusted issuer | n/a | -| Ledger | | | -| | send schema to the ledger (requires endorser role) | n/a | -| | create a credential definition on the ledger (requires endorser role) | n/a | -| Basic Message | | | -| | send and receive basic messages via chat window | n/a | -| Tasks/Activities | | | -| | list of tasks that need attention, and list of past activities | n/a | -| TAA | | | -| | if ledger is configured with a TAA, show it and give option to accept | n/a | -| Read Only Ledger | | | -| | if mode is set to web only | n/a | -| Public Profile | | | -| | web accessible (self signed) imprint based on (indy/w3c) credentials or documents | n/a | +| Role/Feature | Flow | Protocol Version | +|------------------|--------------------------------------------------------------------------------------------------------------|-----------------------------------| +| Issuer | | | +| | auto: issue credential | indy: v1, v2
w3c: v2 | +| | manual: send credential offer to holder | indy: v1, v2
w3c: v2 | +| | manual: receive credential proposal from holder | indy: v1, v2
w3c: v2 | +| | manual: decline credential proposal from holder and provide reason | indy: v1, v2
w3c: v2 | +| | send credential offer as invitation attachment | indy: v1, v2 | +| | revoke issued credential (requires tails server) | indy: v1, v2
w3c: n/a | +| | send revocation notification | indy: v1, v2
w3c: n/a | +| Holder | | | +| | auto: receive credential | indy: v1, v2
w3c: v2 | +| | manual: send credential proposal to issuer (based on document) | indy: v1, v2
w3c: v2 | +| | manual: receive credential offer from issuer | indy: v1, v2
w3c: v2 | +| | manual: decline credential offer from issuer | indy: v1, v2
w3c: v2 | +| | scheduled revocation check on all received credentials | indy: v1, v2
w3c: n/a | +| | receive revocation notification | indy: v1, v2
w3c: n/a | +| Prover | | | +| | auto: send presentation to verifier | indy: v1, v2
w3c: v2 | +| | auto: answer presentation request | indy: v1, v2
w3c: v2 | +| | manual: accept/decline presentation request and provide reason | indy: v1, v2
w3c: v2 | +| Verifier | | | +| | auto: request presentation from prover based on proof template | indy: v1, v2
w3c: v2 | +| | auto: receive and verify presentation from prover | indy: v1, v2
w3c: v2 | +| | send presentation request as invitation attachment (backend only) | indy: v1, v2
w3c: v2 | +| Proof-Template | | | +| | prepared presentation request templates for indy and w3c presentation exchanges | | +| | query by: schema attributes. restrict by: attribute value, predicates (<, >, <=, >=), schema, and issuer did | indy | +| | query by: schema attributes. restrict by: attribute value, schema, and issuer did | w3c | +| Connection | | | +| | connect by did:sov, did:web (if endpoint is aca-py) | did-exchange | +| | receive invitation by URL | connection-protocol, OOB | +| | create invitation (barcode or URL) | connection-protocol, OOB | +| | auto: accept incoming connection | did-exchange, connection-protocol | +| | manual: accept incoming connection | did-exchange, connection-protocol | +| | optional: scheduled trust ping to check connection status | n/a | +| | tag a connection, e.g. as trusted issuer | n/a | +| Ledger | | | +| | send schema to the ledger (requires endorser role) | n/a | +| | create a credential definition on the ledger (requires endorser role) | n/a | +| Basic Message | | | +| | send and receive basic messages via chat window | n/a | +| Tasks/Activities | | | +| | list of tasks that need attention, and list of past activities | n/a | +| TAA | | | +| | if ledger is configured with a TAA, show it and give option to accept | n/a | +| Read Only Ledger | | | +| | if mode is set to web only | n/a | +| Public Profile | | | +| | web accessible (self signed) imprint based on (indy/w3c) credentials or documents | n/a | ## Upcoming Features diff --git a/backend/business-partner-agent/pom.xml b/backend/business-partner-agent/pom.xml index d22de3ecf..d52b86ebe 100644 --- a/backend/business-partner-agent/pom.xml +++ b/backend/business-partner-agent/pom.xml @@ -112,7 +112,7 @@ org.apache.tomcat tomcat-annotations-api - 10.1.0-M16 + 10.1.0-M17 @@ -133,7 +133,7 @@ network.idu.acapy aries-client-python - 0.7.26 + 0.7.27 org.hyperledger.business-partner-agent @@ -452,7 +452,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.2.0 + 3.3.0 copy-frontend-resources diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/AdminController.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/AdminController.java index 1857721a0..d4cf3e1d1 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/AdminController.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/AdminController.java @@ -287,6 +287,8 @@ public HttpResponse registerEndpoints(@Body TAADigestRequest tAADigest) { } /** + * Returns true if a TAA needs to be accepted before writing to the ledger + * * @return true if endpoint registration is required */ @Get("/endpoints/registrationRequired") diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/InvitationController.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/InvitationController.java index 9a6df5416..a4564d381 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/InvitationController.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/InvitationController.java @@ -18,15 +18,16 @@ package org.hyperledger.bpa.controller; import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; import io.micronaut.http.MutableHttpResponse; -import io.micronaut.http.annotation.Body; -import io.micronaut.http.annotation.Controller; -import io.micronaut.http.annotation.Post; +import io.micronaut.http.annotation.*; import io.micronaut.scheduling.TaskExecutors; import io.micronaut.scheduling.annotation.ExecuteOn; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import io.micronaut.validation.Validated; +import io.swagger.v3.oas.annotations.Hidden; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; import lombok.extern.slf4j.Slf4j; @@ -35,18 +36,26 @@ import org.hyperledger.bpa.controller.api.invitation.CheckInvitationRequest; import org.hyperledger.bpa.controller.api.partner.CreatePartnerInvitationRequest; import org.hyperledger.bpa.impl.aries.connection.ConnectionManager; +import org.hyperledger.bpa.impl.oob.OOBCredentialOffer; + +import java.util.UUID; @Slf4j -@Controller("/api/invitations") +@Controller(InvitationController.INVITATION_CONTROLLER_BASE_URL) @Tag(name = "Invitation Management") @Validated @Secured(SecurityRule.IS_AUTHENTICATED) @ExecuteOn(TaskExecutors.IO) public class InvitationController { + public static final String INVITATION_CONTROLLER_BASE_URL = "/api/invitations"; + @Inject ConnectionManager cm; + @Inject + OOBCredentialOffer offerManager; + /** * Check invitation (receive) * @@ -82,4 +91,21 @@ public HttpResponse requestConnectionInvitation( return HttpResponse.ok(cm.createConnectionInvitation(req)); } + /** + * Handle OOB credential/presentation exchange with attachment step 2 - redirect + * with encoded attachment + * + * @param id {@link UUID} + * @return Redirect with encoded credential-offer/presentation-request + * attachment in the location header + */ + @Secured(SecurityRule.IS_ANONYMOUS) + @Hidden + @ApiResponse(responseCode = "301", description = "Redirect with encoded credential offer in the location header") + @Get("/oob-attachment/{id}") + public HttpResponse handleConnectionLess(@PathVariable UUID id) { + return HttpResponse.status(HttpStatus.MOVED_PERMANENTLY).header("location", + offerManager.handleConnectionLess(id)); + } + } diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/IssuerController.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/IssuerController.java index 5c23fa0ab..cca396806 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/IssuerController.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/IssuerController.java @@ -21,16 +21,13 @@ import io.micronaut.data.model.Page; import io.micronaut.data.model.Pageable; import io.micronaut.http.HttpResponse; -import io.micronaut.http.HttpStatus; import io.micronaut.http.annotation.*; import io.micronaut.scheduling.TaskExecutors; import io.micronaut.scheduling.annotation.ExecuteOn; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import io.micronaut.validation.Validated; -import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; import org.hyperledger.aries.api.issue_credential_v1.CredentialExchangeRole; @@ -40,16 +37,14 @@ import org.hyperledger.bpa.controller.api.issuer.*; import org.hyperledger.bpa.impl.aries.creddef.CredDefManager; import org.hyperledger.bpa.impl.aries.credential.IssuerManager; -import org.hyperledger.bpa.impl.aries.credential.OOBCredentialOffer; import org.hyperledger.bpa.impl.aries.schema.SchemaService; +import org.hyperledger.bpa.impl.oob.OOBCredentialOffer; import javax.validation.Valid; import java.util.List; import java.util.UUID; -import static org.hyperledger.bpa.controller.IssuerController.ISSUER_CONTROLLER_BASE_URL; - -@Controller(ISSUER_CONTROLLER_BASE_URL) +@Controller(IssuerController.ISSUER_CONTROLLER_BASE_URL) @Tag(name = "Credential Issuance Management") @Validated @Secured(SecurityRule.IS_AUTHENTICATED) @@ -104,7 +99,7 @@ public HttpResponse createCredDef(@Body CreateCredDefRequest req) { } /** - * Delete a indy credential definition (will not delete it from the ledger) + * Delete an indy credential definition (will not delete it from the ledger) * * @param id {@link UUID} the cred def id * @return {@link HttpResponse} @@ -142,21 +137,6 @@ public HttpResponse issueCredentialConnectionLess( return HttpResponse.ok(connectionLess.issueConnectionLess(req)); } - /** - * Issue OOB credential step 2 - redirect with encoded offer - * - * @param id {@link UUID} - * @return Redirect with encoded credential offer in the location header - */ - @Secured(SecurityRule.IS_ANONYMOUS) - @Hidden - @ApiResponse(responseCode = "301", description = "Redirect with encoded credential offer in the location header") - @Get("/issue-credential/oob-attachment/{id}") - public HttpResponse handleConnectionLess(@PathVariable UUID id) { - return HttpResponse.status(HttpStatus.MOVED_PERMANENTLY).header("location", - connectionLess.handleConnectionLess(id)); - } - /** * List issued or received credentials * diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/ProofExchangeController.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/ProofExchangeController.java index 2a344aa56..8b33a06d1 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/ProofExchangeController.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/ProofExchangeController.java @@ -33,12 +33,15 @@ import org.hyperledger.bpa.api.aries.AriesProofExchange; import org.hyperledger.bpa.api.exception.WrongApiUsageException; import org.hyperledger.bpa.config.BPAMessageSource; +import org.hyperledger.bpa.controller.api.invitation.APICreateInvitationResponse; import org.hyperledger.bpa.controller.api.issuer.DeclineExchangeRequest; import org.hyperledger.bpa.controller.api.partner.ApproveProofRequest; import org.hyperledger.bpa.controller.api.partner.RequestProofRequest; import org.hyperledger.bpa.controller.api.partner.SendProofRequest; import org.hyperledger.bpa.controller.api.proof.PresentationRequestCredentialsIndy; +import org.hyperledger.bpa.controller.api.proof.RequestOOBPresentationRequest; import org.hyperledger.bpa.impl.aries.proof.ProofManager; +import org.hyperledger.bpa.impl.oob.OOBPresentationRequest; import javax.validation.Valid; import java.util.List; @@ -55,12 +58,15 @@ public class ProofExchangeController { @Inject ProofManager proofM; + @Inject + OOBPresentationRequest oob; + @Inject BPAMessageSource.DefaultMessageSource msg; /** - * Manual proof exchange flow. Get matching wallet credentials before sending or - * declining the proof request. + * Manual proof exchange flow: Get matching indy credentials before sending or + * declining the proof request * * @param id {@link UUID} the presentationExchangeId * @return list of {@link PresentationRequestCredentialsIndy} @@ -70,13 +76,20 @@ public HttpResponse> getMatchingCredent return HttpResponse.ok(proofM.getMatchingIndyCredentials(id)); } + /** + * Manual proof exchange flow: Get matching w3c credentials before sending or + * declining the proof request + * + * @param id {@link UUID} the presentationExchangeId + * @return list of {@link PresentationRequestCredentialsIndy} + */ @Get("/{id}/matching-credentials-ld") public HttpResponse> getMatchingLDCredentials(@PathVariable UUID id) { return HttpResponse.ok(proofM.getMatchingLDCredentials(id)); } /** - * Manual proof exchange flow. Answer ProofRequest with matching attributes + * Manual proof exchange flow: Answer ProofRequest with matching attributes * * @param id {@link UUID} the presentationExchangeId * @param req {@link ApproveProofRequest} @@ -89,7 +102,7 @@ public HttpResponse responseToProofRequest(@PathVariable UUID id, @Body @N } /** - * Manual proof exchange flow. Reject ProofRequest received from a partner + * Manual proof exchange flow: Reject ProofRequest received from a partner * * @param id {@link UUID} the presentationExchangeId * @param req {@link DeclineExchangeRequest} @@ -122,6 +135,19 @@ public HttpResponse requestProof( return HttpResponse.ok(); } + /** + * OOB presentation request step 1 - prepares presentation request and returns + * URL for use within the barcode + * + * @param req {@link RequestOOBPresentationRequest} + * @return {@link APICreateInvitationResponse} + */ + @Post("/proof-request/oob-attachment") + public HttpResponse connectionLessPresentationRequest( + @Valid @Body RequestOOBPresentationRequest req) { + return HttpResponse.ok(oob.requestConnectionLess(req)); + } + /** * Send proof to partner * diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/ProofTemplateController.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/ProofTemplateController.java index 029f47df2..3913e099f 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/ProofTemplateController.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/ProofTemplateController.java @@ -35,7 +35,6 @@ import javax.validation.Valid; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -71,12 +70,7 @@ public HttpResponse> listProofTemplates() { */ @Get("/{id}") public HttpResponse getProofTemplateForId(@PathVariable UUID id) { - Optional proofTemplate = proofTemplateManager.getProofTemplate(id); - if (proofTemplate.isPresent()) { - return HttpResponse.ok(proofTemplate.get().toRepresentation()); - } else { - return HttpResponse.notFound(); - } + return HttpResponse.ok(proofTemplateManager.findProofTemplate(id).toRepresentation()); } /** diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/WalletController.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/WalletController.java index 9def28056..fb53c6c95 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/WalletController.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/WalletController.java @@ -27,7 +27,6 @@ import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; import io.micronaut.validation.Validated; -import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; import org.hyperledger.bpa.api.CredentialType; @@ -68,7 +67,6 @@ public class WalletController { * @return list of {@link MyDocumentAPI} */ @Get("/document{?pc*}") - @Schema(implementation = Page.class) public HttpResponse> getDocuments(@Valid @Nullable PaginationCommand pc) { return HttpResponse.ok(docMgmt.getMyDocuments( pc != null ? pc.toPageable() : Pageable.unpaged(), diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/ExchangeVersionTranslator.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/ExchangeVersionTranslator.java new file mode 100644 index 000000000..ed120d87b --- /dev/null +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/ExchangeVersionTranslator.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020-2022 - for information on the respective copyright owner + * see the NOTICE file and/or the repository at + * https://github.com/hyperledger-labs/business-partner-agent + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + */ +package org.hyperledger.bpa.controller.api; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hyperledger.aries.api.ExchangeVersion; + +public interface ExchangeVersionTranslator { + + ExchangeVersion getExchangeVersion(); + + @JsonIgnore + default boolean exchangeIsV1() { + return getExchangeVersion() == null || ExchangeVersion.V1.equals(getExchangeVersion()); + } + + @JsonIgnore + default boolean exchangeIsV2() { + return ExchangeVersion.V2.equals(getExchangeVersion()); + } +} diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/WebSocketMessageBody.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/WebSocketMessageBody.java index 8eedef316..8ef025c8c 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/WebSocketMessageBody.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/WebSocketMessageBody.java @@ -20,6 +20,7 @@ import io.micronaut.core.annotation.Nullable; import lombok.*; import org.hyperledger.bpa.api.PartnerAPI; +import org.hyperledger.bpa.controller.api.partner.PartnerMessage; import org.hyperledger.bpa.persistence.model.ChatMessage; /** diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/CredEx.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/CredEx.java index b837c61fc..f41f29da2 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/CredEx.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/CredEx.java @@ -26,6 +26,7 @@ import org.hyperledger.bpa.api.CredentialType; import org.hyperledger.bpa.api.PartnerAPI; import org.hyperledger.bpa.api.aries.SchemaAPI; +import org.hyperledger.bpa.controller.api.ExchangeVersionTranslator; import org.hyperledger.bpa.persistence.model.BPACredentialExchange; import java.util.Map; @@ -35,7 +36,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class CredEx { +public class CredEx implements ExchangeVersionTranslator { private UUID id; private Long createdAt; diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/IssueCredentialRequest.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/IssueCredentialRequest.java index d71189235..1ae5133fb 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/IssueCredentialRequest.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/IssueCredentialRequest.java @@ -27,6 +27,7 @@ import lombok.experimental.SuperBuilder; import org.hyperledger.aries.api.ExchangeVersion; import org.hyperledger.bpa.api.CredentialType; +import org.hyperledger.bpa.controller.api.ExchangeVersionTranslator; import javax.validation.constraints.NotBlank; import java.util.UUID; @@ -68,17 +69,14 @@ public abstract class IssueCredentialRequest { @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) - public static final class IssueIndyCredentialRequest extends IssueCredentialRequest { + public static final class IssueIndyCredentialRequest extends IssueCredentialRequest + implements ExchangeVersionTranslator { @NotBlank private UUID credDefId; /** credential exchange api version */ private ExchangeVersion exchangeVersion; - - public boolean exchangeIsV1() { - return exchangeVersion == null || ExchangeVersion.V1.equals(exchangeVersion); - } } @Introspected diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/IssueOOBCredentialRequest.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/IssueOOBCredentialRequest.java index 942161345..ba89c3b6d 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/IssueOOBCredentialRequest.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/issuer/IssueOOBCredentialRequest.java @@ -23,27 +23,32 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.NoArgsConstructor; +import org.hyperledger.aries.api.ExchangeVersion; +import org.hyperledger.bpa.controller.api.ExchangeVersionTranslator; import org.hyperledger.bpa.persistence.model.Tag; -import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; import java.util.List; import java.util.UUID; @Introspected @Data @NoArgsConstructor -public class IssueOOBCredentialRequest { +public class IssueOOBCredentialRequest implements ExchangeVersionTranslator { + // connection private String alias; private List tag; private Boolean trustPing; // bpa internal id - @NotBlank + @NotNull private UUID credDefId; /** credential body key value pairs */ @JsonRawValue @Schema(example = "{}") private JsonNode document; + + private ExchangeVersion exchangeVersion; } diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/PartnerMessage.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/PartnerMessage.java similarity index 89% rename from backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/PartnerMessage.java rename to backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/PartnerMessage.java index e93969f50..3905bd5f1 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/PartnerMessage.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/PartnerMessage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 - for information on the respective copyright owner + * Copyright (c) 2020-2022 - for information on the respective copyright owner * see the NOTICE file and/or the repository at * https://github.com/hyperledger-labs/business-partner-agent * @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.hyperledger.bpa.controller.api; +package org.hyperledger.bpa.controller.api.partner; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/RequestCredentialRequest.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/RequestCredentialRequest.java index 1d40ca9fe..cb1bdd8e9 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/RequestCredentialRequest.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/RequestCredentialRequest.java @@ -20,12 +20,13 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.hyperledger.aries.api.ExchangeVersion; +import org.hyperledger.bpa.controller.api.ExchangeVersionTranslator; import java.util.UUID; @Data @NoArgsConstructor -public class RequestCredentialRequest { +public class RequestCredentialRequest implements ExchangeVersionTranslator { public UUID documentId; public ExchangeVersion exchangeVersion; } diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/SendProofRequest.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/SendProofRequest.java index 8d562a329..6db30a0f7 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/SendProofRequest.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/partner/SendProofRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 - for information on the respective copyright owner + * Copyright (c) 2020-2022 - for information on the respective copyright owner * see the NOTICE file and/or the repository at * https://github.com/hyperledger-labs/business-partner-agent * @@ -20,12 +20,13 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.hyperledger.aries.api.ExchangeVersion; +import org.hyperledger.bpa.controller.api.ExchangeVersionTranslator; import java.util.UUID; @Data @NoArgsConstructor -public class SendProofRequest { +public class SendProofRequest implements ExchangeVersionTranslator { private UUID partnerId; private UUID myCredentialId; private ExchangeVersion exchangeVersion; diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/proof/PresentationRequestVersion.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/proof/PresentationRequestVersion.java index fd14fa318..88791689f 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/proof/PresentationRequestVersion.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/proof/PresentationRequestVersion.java @@ -20,10 +20,11 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.hyperledger.aries.api.ExchangeVersion; +import org.hyperledger.bpa.controller.api.ExchangeVersionTranslator; @Data @NoArgsConstructor -public class PresentationRequestVersion { +public class PresentationRequestVersion implements ExchangeVersionTranslator { /** presentation exchange api version */ private ExchangeVersion exchangeVersion; } diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/proof/RequestOOBPresentationRequest.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/proof/RequestOOBPresentationRequest.java new file mode 100644 index 000000000..2a343f7d1 --- /dev/null +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/controller/api/proof/RequestOOBPresentationRequest.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020-2022 - for information on the respective copyright owner + * see the NOTICE file and/or the repository at + * https://github.com/hyperledger-labs/business-partner-agent + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + */ +package org.hyperledger.bpa.controller.api.proof; + +import io.micronaut.core.annotation.Introspected; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hyperledger.aries.api.ExchangeVersion; +import org.hyperledger.bpa.controller.api.ExchangeVersionTranslator; +import org.hyperledger.bpa.persistence.model.Tag; + +import javax.validation.constraints.NotNull; +import java.util.List; +import java.util.UUID; + +@Introspected +@Data +@NoArgsConstructor +public class RequestOOBPresentationRequest implements ExchangeVersionTranslator { + + // connection + private String alias; + private List tag; + private Boolean trustPing; + + // bpa internal id + @NotNull + private UUID templateId; + + private ExchangeVersion exchangeVersion; +} diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/connection/ConnectionManager.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/connection/ConnectionManager.java index f046de069..3f8c25c03 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/connection/ConnectionManager.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/connection/ConnectionManager.java @@ -41,6 +41,7 @@ import org.hyperledger.aries.api.out_of_band.InvitationCreateRequest; import org.hyperledger.aries.api.out_of_band.ReceiveInvitationFilter; import org.hyperledger.aries.api.present_proof.PresentProofRecordsFilter; +import org.hyperledger.aries.api.present_proof_v2.V2PresentProofRecordsFilter; import org.hyperledger.bpa.api.exception.EntityNotFoundException; import org.hyperledger.bpa.api.exception.InvitationException; import org.hyperledger.bpa.api.exception.NetworkException; @@ -367,18 +368,17 @@ public void removeConnection(@NonNull Partner partner) { record.getPresentationExchangeId(), e); } })); - // TODO needed, but needs new client release -// ac.presentProofV2Records(V2PresentProofRecordsFilter -// .builder() -// .connectionId(connectionId) -// .build()).ifPresent(records -> records.forEach(record -> { -// try { -// ac.presentProofV2RecordsRemove(record.getPresentationExchangeId()); -// } catch (IOException | AriesException e) { -// log.error("Could not delete v2 presentation exchange record: {}", -// record.getPresentationExchangeId(), e); -// } -// })); + ac.presentProofV2Records(V2PresentProofRecordsFilter + .builder() + .connectionId(connectionId) + .build()).ifPresent(records -> records.forEach(record -> { + try { + ac.presentProofV2RecordsRemove(record.getPresentationExchangeId()); + } catch (IOException | AriesException e) { + log.error("Could not delete v2 presentation exchange record: {}", + record.getPresentationExchangeId(), e); + } + })); ac.issueCredentialRecords(IssueCredentialRecordsFilter .builder() .connectionId(connectionId) diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/proof/ProofManager.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/proof/ProofManager.java index 85606121b..d31074824 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/proof/ProofManager.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/proof/ProofManager.java @@ -146,6 +146,16 @@ public void sendPresentProofRequestIndy(@NonNull UUID partnerId, @NonNull @Valid } } + /** + * Renders the proof-request, not bound to any connection + * + * @param proofTemplate {@link BPAProofTemplate} + * @return {@link PresentProofRequest} + */ + public PresentProofRequest renderIndyProofRequest(@NonNull @Valid BPAProofTemplate proofTemplate) { + return proofTemplateConversion.templateToProofRequest(proofTemplate).build(); + } + public void sendPresentProofRequestJsonLD(@NonNull UUID partnerId, @NonNull @Valid BPAProofTemplate proofTemplate) { Partner p = partnerRepo.findById(partnerId).orElseThrow(EntityNotFoundException::new); try { @@ -153,7 +163,7 @@ public void sendPresentProofRequestJsonLD(@NonNull UUID partnerId, @NonNull @Val .builder() .connectionId(p.getConnectionId()) .presentationRequest(V20PresSendRequestRequest.V20PresRequestByFormat.builder() - .dif(ldVerifier.prepareRequest(proofTemplate)) + .dif(renderLDProofRequest(proofTemplate)) .build()) .build()) .ifPresent(persistProof(PersistProofCmd.builder() @@ -164,6 +174,10 @@ public void sendPresentProofRequestJsonLD(@NonNull UUID partnerId, @NonNull @Val } + public V2DIFProofRequest renderLDProofRequest(@NonNull @Valid BPAProofTemplate proofTemplate) { + return ldVerifier.prepareRequest(proofTemplate); + } + // request proof from partner - currently not used by the frontend public void sendPresentProofRequestIndy(@NonNull UUID partnerId, @NonNull RequestProofRequest req) { try { diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/prooftemplates/ProofTemplateConversion.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/prooftemplates/ProofTemplateConversion.java index f28ba3bab..0ce1739d8 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/prooftemplates/ProofTemplateConversion.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/prooftemplates/ProofTemplateConversion.java @@ -66,6 +66,20 @@ public PresentProofRequest proofRequestViaVisitorFrom(@NonNull UUID partnerId, throw new PartnerException(ms.getMessage("api.partner.no.connection")); } + return templateToProofRequest(proofTemplate) + .connectionId(partner.getConnectionId()) + .build(); + } + + /** + * Renders a proof request builder not bound to any partner yet. + * + * @param proofTemplate {@link BPAProofTemplate} + * @return {@link PresentProofRequest.PresentProofRequestBuilder} + */ + @NonNull + public PresentProofRequest.PresentProofRequestBuilder templateToProofRequest( + @NonNull @Valid BPAProofTemplate proofTemplate) { ProofTemplateElementVisitor proofTemplateElementVisitor = new ProofTemplateElementVisitor( this::resolveLedgerSchemaId, new RevocationTimeStampProvider(clock)); @@ -78,9 +92,7 @@ public PresentProofRequest proofRequestViaVisitorFrom(@NonNull UUID partnerId, .forEach(proofTemplateElementVisitor::visit); return PresentProofRequest.builder() - .proofRequest(proofTemplateElementVisitor.getResult()) - .connectionId(partner.getConnectionId()) - .build(); + .proofRequest(proofTemplateElementVisitor.getResult()); } private Optional resolveLedgerSchemaId(UUID databaseSchemaId) { diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/prooftemplates/ProofTemplateManager.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/prooftemplates/ProofTemplateManager.java index 982ced6b7..2a70177f6 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/prooftemplates/ProofTemplateManager.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/prooftemplates/ProofTemplateManager.java @@ -24,6 +24,8 @@ import lombok.AllArgsConstructor; import lombok.NonNull; import org.hyperledger.aries.api.ExchangeVersion; +import org.hyperledger.aries.api.present_proof.PresentProofRequest; +import org.hyperledger.aries.api.present_proof_v2.V2DIFProofRequest; import org.hyperledger.bpa.api.CredentialType; import org.hyperledger.bpa.api.exception.DataPersistenceException; import org.hyperledger.bpa.api.exception.EntityNotFoundException; @@ -35,9 +37,13 @@ import org.hyperledger.bpa.persistence.model.prooftemplate.BPAAttributeGroup; import org.hyperledger.bpa.persistence.model.prooftemplate.ValueOperators; import org.hyperledger.bpa.persistence.repository.BPAProofTemplateRepository; +import org.jetbrains.annotations.NotNull; import javax.validation.Valid; -import java.util.*; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -64,9 +70,7 @@ public void invokeProofRequestByTemplate(@NonNull UUID id, @NonNull UUID partner public void invokeProofRequestByTemplate(@NonNull UUID id, @NonNull UUID partnerId, @Nullable ExchangeVersion version) { - BPAProofTemplate proofTemplate = repo.findById(id) - .orElseThrow(() -> new EntityNotFoundException( - ms.getMessage("api.proof.template.not.found", Map.of("id", id)))); + BPAProofTemplate proofTemplate = findProofTemplate(id); if (proofTemplate.typeIsIndy()) { version = version != null ? version : ExchangeVersion.V1; proofManager.sendPresentProofRequestIndy(partnerId, proofTemplate, version); @@ -75,8 +79,18 @@ public void invokeProofRequestByTemplate(@NonNull UUID id, @NonNull UUID partner } } - public Optional getProofTemplate(@NonNull UUID id) { - return repo.findById(id); + public RenderedTemplate renderTemplate(@NonNull UUID id) { + BPAProofTemplate proofTemplate = findProofTemplate(id); + if (proofTemplate.typeIsIndy()) { + return new RenderedTemplate(CredentialType.INDY, proofManager.renderIndyProofRequest(proofTemplate), null); + } + return new RenderedTemplate(CredentialType.JSON_LD, null, proofManager.renderLDProofRequest(proofTemplate)); + } + + public BPAProofTemplate findProofTemplate(@NotNull UUID id) { + return repo.findById(id) + .orElseThrow(() -> new EntityNotFoundException( + ms.getMessage("api.proof.template.not.found", Map.of("id", id)))); } public BPAProofTemplate addProofTemplate(@NonNull @Valid BPAProofTemplate template) { @@ -109,4 +123,7 @@ public Set getKnownConditionOperators(@Nullable CredentialType type) { } return Arrays.stream(ValueOperators.values()).map(ValueOperators::getValue).collect(Collectors.toSet()); } + + public record RenderedTemplate(CredentialType type, PresentProofRequest indy, V2DIFProofRequest dif) { + } } diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/oob/OOBBase.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/oob/OOBBase.java new file mode 100644 index 000000000..640d66234 --- /dev/null +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/oob/OOBBase.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2020-2022 - for information on the respective copyright owner + * see the NOTICE file and/or the repository at + * https://github.com/hyperledger-labs/business-partner-agent + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + */ +package org.hyperledger.bpa.impl.oob; + +import io.micronaut.context.annotation.Value; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.hyperledger.acy_py.generated.model.InvitationRecord; +import org.hyperledger.aries.api.connection.ConnectionState; +import org.hyperledger.aries.config.GsonConfig; +import org.hyperledger.bpa.api.exception.EntityNotFoundException; +import org.hyperledger.bpa.config.BPAMessageSource; +import org.hyperledger.bpa.controller.InvitationController; +import org.hyperledger.bpa.controller.api.invitation.APICreateInvitationResponse; +import org.hyperledger.bpa.impl.aries.connection.ConnectionManager; +import org.hyperledger.bpa.persistence.model.Partner; +import org.hyperledger.bpa.persistence.model.Tag; +import org.hyperledger.bpa.persistence.repository.PartnerRepository; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.*; + +@Slf4j +@Singleton +public abstract class OOBBase { + + @Value("${bpa.did.prefix}") + String didPrefix; + + @Value("${bpa.scheme}") + String scheme; + + @Value("${bpa.host}") + String host; + + @Inject + PartnerRepository partnerRepo; + + @Inject + BPAMessageSource.DefaultMessageSource ms; + + /** + * Step 2: Return the base64 encoded invitation plus attachment + * + * @param invMessageId invitation message id + * @return base64 encoded invitation URL + */ + public String handleConnectionLess(@NonNull UUID invMessageId) { + log.debug("Handling connectionless credential request: {}", invMessageId); + Partner ex = partnerRepo.findByInvitationMsgId(invMessageId.toString()) + .orElseThrow(EntityNotFoundException::new); + if (ex.getInvitationRecord() == null) { + throw new EntityNotFoundException(ms.getMessage("api.issuer.connectionless.invitation.not.found", + Map.of("id", invMessageId))); + } + // getInvitationUrl() has an encoding issue + byte[] envelopeBase64 = Base64.getEncoder().encode( + GsonConfig.defaultNoEscaping().toJson( + ex.getInvitationRecord().getInvitation()).getBytes(StandardCharsets.UTF_8)); + return "didcomm://" + host + "?oob=" + new String(envelopeBase64, StandardCharsets.UTF_8); + } + + Partner persistPartner(InvitationRecord r, String alias, Boolean trustPing, List tag) { + return partnerRepo.save(Partner + .builder() + .ariesSupport(Boolean.TRUE) + .invitationMsgId(r.getInviMsgId()) + .did(didPrefix + ConnectionManager.UNKNOWN_DID) + .state(ConnectionState.INVITATION) + .pushStateChange(ConnectionState.INVITATION, Instant.now()) + .invitationRecord(r) + .incoming(Boolean.TRUE) + .alias(StringUtils.trimToNull(alias)) + .tags(tag != null ? new HashSet<>(tag) : null) + .trustPing(trustPing != null ? trustPing : Boolean.FALSE) + .build()); + } + + URI createURI(String path) { + return URI.create(scheme + "://" + host + path); + } + + APICreateInvitationResponse buildResponse(@NonNull String invitationMsgId) { + return APICreateInvitationResponse.builder() + .invitationUrl( + createURI(InvitationController.INVITATION_CONTROLLER_BASE_URL + + "/oob-attachment/" + + invitationMsgId) + .toString()) + .invitationId(invitationMsgId) + .build(); + } +} diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/credential/OOBCredentialOffer.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/oob/OOBCredentialOffer.java similarity index 52% rename from backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/credential/OOBCredentialOffer.java rename to backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/oob/OOBCredentialOffer.java index 59b5dc1d8..044a7dae7 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/aries/credential/OOBCredentialOffer.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/oob/OOBCredentialOffer.java @@ -15,44 +15,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.hyperledger.bpa.impl.aries.credential; +package org.hyperledger.bpa.impl.oob; -import io.micronaut.context.annotation.Value; import jakarta.inject.Inject; import jakarta.inject.Singleton; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.hyperledger.acy_py.generated.model.InvitationRecord; import org.hyperledger.aries.AriesClient; -import org.hyperledger.aries.api.connection.ConnectionState; import org.hyperledger.aries.api.credentials.Credential; import org.hyperledger.aries.api.issue_credential_v1.CredentialExchangeRole; import org.hyperledger.aries.api.issue_credential_v1.CredentialExchangeState; -import org.hyperledger.aries.api.issue_credential_v1.V1CredentialFreeOfferHelper; +import org.hyperledger.aries.api.issue_credential_v1.CredentialFreeOfferHelper; import org.hyperledger.aries.config.GsonConfig; -import org.hyperledger.bpa.api.exception.EntityNotFoundException; import org.hyperledger.bpa.api.exception.WrongApiUsageException; import org.hyperledger.bpa.config.BPAMessageSource; -import org.hyperledger.bpa.controller.IssuerController; import org.hyperledger.bpa.controller.api.invitation.APICreateInvitationResponse; import org.hyperledger.bpa.controller.api.issuer.IssueOOBCredentialRequest; import org.hyperledger.bpa.impl.activity.DocumentValidator; -import org.hyperledger.bpa.impl.aries.connection.ConnectionManager; import org.hyperledger.bpa.impl.util.Converter; import org.hyperledger.bpa.persistence.model.BPACredentialDefinition; import org.hyperledger.bpa.persistence.model.BPACredentialExchange; import org.hyperledger.bpa.persistence.model.Partner; -import org.hyperledger.bpa.persistence.model.Tag; import org.hyperledger.bpa.persistence.model.converter.ExchangePayload; import org.hyperledger.bpa.persistence.repository.BPACredentialDefinitionRepository; import org.hyperledger.bpa.persistence.repository.IssuerCredExRepository; -import org.hyperledger.bpa.persistence.repository.PartnerRepository; -import java.net.URI; -import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.util.*; +import java.util.Map; /** * First try on attaching a credential offer in the OOB invitation. As with the @@ -61,16 +50,7 @@ */ @Slf4j @Singleton -public class OOBCredentialOffer { - - @Value("${bpa.did.prefix}") - String didPrefix; - - @Value("${bpa.scheme}") - String scheme; - - @Value("${bpa.host}") - String host; +public class OOBCredentialOffer extends OOBBase { @Inject Converter conv; @@ -81,20 +61,17 @@ public class OOBCredentialOffer { @Inject IssuerCredExRepository credExRepo; - @Inject - PartnerRepository partnerRepo; - @Inject DocumentValidator validator; @Inject BPAMessageSource.DefaultMessageSource ms; - private final V1CredentialFreeOfferHelper h; + private final CredentialFreeOfferHelper h; @Inject public OOBCredentialOffer(AriesClient ac) { - this.h = new V1CredentialFreeOfferHelper(ac); + this.h = new CredentialFreeOfferHelper(ac); } /** @@ -112,46 +89,23 @@ public APICreateInvitationResponse issueConnectionLess(@NonNull IssueOOBCredenti Map document = conv.toStringMap(req.getDocument()); - V1CredentialFreeOfferHelper.CredentialFreeOffer freeOffer = h - .buildFreeOffer(dbCredDef.getCredentialDefinitionId(), document); + CredentialFreeOfferHelper.CredentialFreeOffer freeOffer; + if (req.exchangeIsV1()) { + freeOffer = h.buildV1Indy(dbCredDef.getCredentialDefinitionId(), document); + } else { + freeOffer = h.buildV2Indy(dbCredDef.getCredentialDefinitionId(), document); + } log.debug("{}", GsonConfig.defaultNoEscaping().toJson(freeOffer)); Partner p = persistPartner(freeOffer.getInvitationRecord(), req.getAlias(), req.getTrustPing(), req.getTag()); persistCredentialExchange(freeOffer, document, dbCredDef, p); - return APICreateInvitationResponse.builder() - .invitationUrl( - createURI(IssuerController.ISSUER_CONTROLLER_BASE_URL - + "/issue-credential/oob-attachment/" - + freeOffer.getInvitationRecord().getInviMsgId()).toString()) - .invitationId(freeOffer.getInvitationRecord().getInviMsgId()) - .build(); - } - - /** - * Step 2: Return the base64 encoded invitation plus attachment - * - * @param invMessageId invitation message id - * @return base64 encoded invitation URL - */ - public String handleConnectionLess(@NonNull UUID invMessageId) { - log.debug("Handling connectionless credential request: {}", invMessageId); - Partner ex = partnerRepo.findByInvitationMsgId(invMessageId.toString()) - .orElseThrow(EntityNotFoundException::new); - if (ex.getInvitationRecord() == null) { - throw new EntityNotFoundException(ms.getMessage("api.issuer.connectionless.invitation.not.found", - Map.of("id", invMessageId))); - } - // getInvitationUrl() has an encoding issue - byte[] envelopeBase64 = Base64.getEncoder().encode( - GsonConfig.defaultNoEscaping().toJson( - ex.getInvitationRecord().getInvitation()).getBytes(StandardCharsets.UTF_8)); - return "didcomm://" + host + "?oob=" + new String(envelopeBase64, StandardCharsets.UTF_8); + return buildResponse(freeOffer.getInvitationRecord().getInviMsgId()); } private void persistCredentialExchange( - @NonNull V1CredentialFreeOfferHelper.CredentialFreeOffer r, + @NonNull CredentialFreeOfferHelper.CredentialFreeOffer r, @NonNull Map document, @NonNull BPACredentialDefinition dbCredDef, @NonNull Partner p) { @@ -163,35 +117,12 @@ private void persistCredentialExchange( .role(CredentialExchangeRole.ISSUER) .state(CredentialExchangeState.OFFER_SENT) .pushStateChange(CredentialExchangeState.OFFER_SENT, Instant.now()) - .credentialExchangeId(r.getCredentialExchangeId()) - .threadId(r.getThreadId()) - .credentialOffer(r.getCredentialProposalDict() != null - ? ExchangePayload - .indy(r.getCredentialProposalDict().getCredentialProposal()) - : null) + .credentialExchangeId(r.getCredentialExchangeRecord().getCredentialExchangeId()) + .threadId(r.getCredentialExchangeRecord().getThreadId()) + .credentialOffer(ExchangePayload.buildForCredentialOffer(r.getCredentialExchangeRecord())) .indyCredential(Credential.builder() .attrs(document) .build()); credExRepo.save(b.build()); } - - private Partner persistPartner(InvitationRecord r, String alias, Boolean trustPing, List tag) { - return partnerRepo.save(Partner - .builder() - .ariesSupport(Boolean.TRUE) - .invitationMsgId(r.getInviMsgId()) - .did(didPrefix + ConnectionManager.UNKNOWN_DID) - .state(ConnectionState.INVITATION) - .pushStateChange(ConnectionState.INVITATION, Instant.now()) - .invitationRecord(r) - .incoming(Boolean.TRUE) - .alias(StringUtils.trimToNull(alias)) - .tags(tag != null ? new HashSet<>(tag) : null) - .trustPing(trustPing != null ? trustPing : Boolean.FALSE) - .build()); - } - - private URI createURI(String path) { - return URI.create(scheme + "://" + host + path); - } } diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/oob/OOBPresentationRequest.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/oob/OOBPresentationRequest.java new file mode 100644 index 000000000..f659bc967 --- /dev/null +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/impl/oob/OOBPresentationRequest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2020-2022 - for information on the respective copyright owner + * see the NOTICE file and/or the repository at + * https://github.com/hyperledger-labs/business-partner-agent + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + */ +package org.hyperledger.bpa.impl.oob; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.hyperledger.aries.AriesClient; +import org.hyperledger.aries.api.present_proof.BasePresExRecord; +import org.hyperledger.aries.api.present_proof.PresentationFreeOfferHelper; +import org.hyperledger.aries.config.GsonConfig; +import org.hyperledger.bpa.api.CredentialType; +import org.hyperledger.bpa.controller.api.invitation.APICreateInvitationResponse; +import org.hyperledger.bpa.controller.api.proof.RequestOOBPresentationRequest; +import org.hyperledger.bpa.impl.aries.prooftemplates.ProofTemplateManager; +import org.hyperledger.bpa.persistence.model.BPAProofTemplate; +import org.hyperledger.bpa.persistence.model.Partner; +import org.hyperledger.bpa.persistence.model.PartnerProof; +import org.hyperledger.bpa.persistence.model.converter.ExchangePayload; +import org.hyperledger.bpa.persistence.repository.PartnerProofRepository; + +import java.time.Instant; +import java.util.UUID; + +@Slf4j +@Singleton +public class OOBPresentationRequest extends OOBBase { + + private final ProofTemplateManager templateManager; + + private final PresentationFreeOfferHelper h; + + private final PartnerProofRepository partnerProofRepo; + + @Inject + public OOBPresentationRequest( + AriesClient ac, + ProofTemplateManager templateManager, + PartnerProofRepository partnerProofRepo) { + this.h = PresentationFreeOfferHelper.builder() + .acaPy(ac) + .build(); + this.templateManager = templateManager; + this.partnerProofRepo = partnerProofRepo; + } + + public APICreateInvitationResponse requestConnectionLess(@NonNull RequestOOBPresentationRequest req) { + ProofTemplateManager.RenderedTemplate rendered = templateManager.renderTemplate(req.getTemplateId()); + PresentationFreeOfferHelper.PresentationFreeOffer freeOffer; + if (CredentialType.INDY.equals(rendered.type())) { + if (req.exchangeIsV1()) { + freeOffer = h.buildV1Indy(rendered.indy().getProofRequest()); + } else { + freeOffer = h.buildV2Indy(rendered.indy().getProofRequest()); + } + } else { + freeOffer = h.buildDif(rendered.dif()); + } + + log.debug("{}", GsonConfig.defaultNoEscaping().toJson(freeOffer)); + + Partner p = persistPartner(freeOffer.getInvitationRecord(), req.getAlias(), req.getTrustPing(), req.getTag()); + persistProof(req.getTemplateId(), rendered.type(), freeOffer.getPresentationExchangeRecord(), p); + + return buildResponse(freeOffer.getInvitationRecord().getInviMsgId()); + } + + private void persistProof( + @NonNull UUID templateId, + @NonNull CredentialType type, + @NonNull BasePresExRecord ex, + @NonNull Partner p) { + final PartnerProof pp = PartnerProof + .builder() + .state(ex.getState()) + .type(type) + .presentationExchangeId(ex.getPresentationExchangeId()) + .role(ex.getRole()) + .threadId(ex.getThreadId()) + .proofRequest(ExchangePayload.buildForProofRequest(ex)) + .proofTemplate(BPAProofTemplate.builder().id(templateId).build()) + .exchangeVersion(ex.getVersion()) + .pushStateChange(ex.getState(), Instant.now()) + .partner(p) + .credentialExchange(null) + .build(); + partnerProofRepo.save(pp); + } +} diff --git a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/persistence/model/converter/ExchangePayload.java b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/persistence/model/converter/ExchangePayload.java index b486e273a..d90d08640 100644 --- a/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/persistence/model/converter/ExchangePayload.java +++ b/backend/business-partner-agent/src/main/java/org/hyperledger/bpa/persistence/model/converter/ExchangePayload.java @@ -18,6 +18,10 @@ package org.hyperledger.bpa.persistence.model.converter; import lombok.*; +import org.hyperledger.aries.api.issue_credential_v1.BaseCredExRecord; +import org.hyperledger.aries.api.issue_credential_v1.V1CredentialExchange; +import org.hyperledger.aries.api.issue_credential_v2.V20CredExRecord; +import org.hyperledger.aries.api.issue_credential_v2.V20CredExRecordByFormat; import org.hyperledger.aries.api.present_proof.BasePresExRecord; import org.hyperledger.aries.api.present_proof.PresentProofRequest; import org.hyperledger.aries.api.present_proof.PresentationExchangeRecord; @@ -55,4 +59,14 @@ public static ExchangePayload buildForCredentialOffer( + @NonNull BaseCredExRecord credEx) { + if (credEx instanceof V1CredentialExchange v1) { + return ExchangePayload.indy(v1.getCredentialProposalDict().getCredentialProposal()); + } else if (credEx instanceof V20CredExRecord v2) { + return ExchangePayload.jsonLD(v2.resolveLDCredOffer()); + } + return null; + } } diff --git a/backend/business-partner-agent/src/test/java/org/hyperledger/bpa/impl/PartnerManagerTest.java b/backend/business-partner-agent/src/test/java/org/hyperledger/bpa/impl/PartnerManagerTest.java index af1f785c7..cae648712 100644 --- a/backend/business-partner-agent/src/test/java/org/hyperledger/bpa/impl/PartnerManagerTest.java +++ b/backend/business-partner-agent/src/test/java/org/hyperledger/bpa/impl/PartnerManagerTest.java @@ -19,6 +19,7 @@ import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import jakarta.inject.Inject; +import org.hyperledger.aries.api.issue_credential_v1.CredentialExchangeRole; import org.hyperledger.aries.api.issue_credential_v1.CredentialExchangeState; import org.hyperledger.bpa.api.CredentialType; import org.hyperledger.bpa.controller.api.partner.UpdatePartnerRequest; @@ -116,21 +117,42 @@ void testRemovePartner() { BPASchema schema = schemaRepo.save(BPASchema.builder() .schemaId("mySchema") .schemaAttributeName("test") - .type(CredentialType.JSON_LD) + .type(CredentialType.INDY) .build()); - BPACredentialExchange credEx = holderRepo.save(BPACredentialExchange + BPACredentialExchange asIssuerSuccess = holderRepo.save(BPACredentialExchange .builder() .partner(p) .schema(schema) .credentialExchangeId("1") + .threadId("1") + .state(CredentialExchangeState.CREDENTIAL_ACKED) + .build()); + BPACredentialExchange asHolderSuccess = holderRepo.save(BPACredentialExchange + .builder() + .partner(p) + .schema(schema) + .credentialExchangeId("2") .threadId("2") + .role(CredentialExchangeRole.HOLDER) .state(CredentialExchangeState.CREDENTIAL_ACKED) .build()); + BPACredentialExchange asHolderFailure = holderRepo.save(BPACredentialExchange + .builder() + .partner(p) + .schema(schema) + .credentialExchangeId("3") + .threadId("3") + .role(CredentialExchangeRole.HOLDER) + .state(CredentialExchangeState.ABANDONED) + .build()); + partnerManager.removePartnerById(p.getId()); Assertions.assertTrue(partnerRepo.findById(p.getId()).isEmpty()); - Assertions.assertTrue(holderRepo.findById(credEx.getId()).isEmpty()); - Assertions.assertEquals(0, holderRepo.count()); + Assertions.assertTrue(holderRepo.findById(asIssuerSuccess.getId()).isEmpty()); + Assertions.assertTrue(holderRepo.findById(asHolderFailure.getId()).isEmpty()); + Assertions.assertNull(holderRepo.findById(asHolderSuccess.getId()).orElseThrow().getPartner()); + Assertions.assertEquals(1, holderRepo.count()); } private void checkTagOnPartner(UUID partnerId, String... tagName) { diff --git a/backend/pom.xml b/backend/pom.xml index 01bada762..c49627097 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -57,14 +57,14 @@ 2.18.0 1.18.24 1.18.20.0 - 3.5.3 + 3.5.4 3.4.3 - 4.4.2 - 3.6.2 + 4.4.3 + 3.6.3 4.6.1 4.10.0 1.4.2.Final - 6.47.0 + 6.48.0 4.7.1 1.17.3 @@ -137,7 +137,7 @@ org.flywaydb flyway-core - 8.5.13 + 9.0.4 @@ -290,7 +290,7 @@ net.revelc.code.formatter formatter-maven-plugin - 2.19.0 + 2.20.0 @@ -342,7 +342,7 @@ io.micronaut.build micronaut-maven-plugin - 3.3.1 + 3.4.0 org.apache.maven.plugins diff --git a/frontend/backend-types.ts b/frontend/backend-types.ts index b6a98b549..c63453447 100644 --- a/frontend/backend-types.ts +++ b/frontend/backend-types.ts @@ -23,11 +23,7 @@ export interface paths { get: operations["getRuntimeConfig"]; }; "/api/admin/endpoints/register": { - /** - * Trigger the backend to write configured endpoints to the ledger. TAA digest - * has to be passed to explicitly confirm prior TTA acceptance by the user for - * this ledger interaction / session. - */ + /** Trigger the backend to write configured endpoints to the ledger. TAA digest has to be passed to explicitly confirm prior TTA acceptance by the user for this ledger interaction / session. */ post: operations["registerEndpoints"]; }; "/api/admin/endpoints/registrationRequired": { @@ -44,10 +40,7 @@ export interface paths { get: operations["getSchema"]; /** Update a schema configuration */ put: operations["updateSchema"]; - /** - * Removes a schema configuration. Doing so means the BPA will not process - * requests containing this schema id anymore. - */ + /** Removes a schema configuration. Doing so means the BPA will not process requests containing this schema id anymore. */ delete: operations["removeSchema"]; }; "/api/admin/schema/{id}/trustedIssuer": { @@ -97,7 +90,7 @@ export interface paths { post: operations["createCredDef"]; }; "/api/issuer/creddef/{id}": { - /** Delete a indy credential definition (will not delete it from the ledger) */ + /** Delete an indy credential definition (will not delete it from the ledger) */ delete: operations["deleteCredDef"]; }; "/api/issuer/exchanges": { @@ -109,17 +102,11 @@ export interface paths { get: operations["getCredentialExchange"]; }; "/api/issuer/exchanges/{id}/decline-proposal": { - /** - * Manual credential exchange: Issuer declines credential proposal received from - * the holder - */ + /** Manual credential exchange: Issuer declines credential proposal received from the holder */ put: operations["declineCredentialExchange"]; }; "/api/issuer/exchanges/{id}/re-issue": { - /** - * Send holder a new credential offer based on an existing (revoked) exchange - * record - */ + /** Send holder a new credential offer based on an existing (revoked) exchange record */ post: operations["reIssueCredential"]; }; "/api/issuer/exchanges/{id}/revoke": { @@ -127,17 +114,11 @@ export interface paths { put: operations["revokeCredential"]; }; "/api/issuer/exchanges/{id}/send-offer": { - /** - * Manual credential exchange step two: Issuer sends credential counter offer to - * holder (in reference to a proposal) - */ + /** Manual credential exchange step two: Issuer sends credential counter offer to holder (in reference to a proposal) */ put: operations["sendCredentialOffer"]; }; "/api/issuer/issue-credential/oob-attachment": { - /** - * Issue OOB credential step 1 - prepares credential offer and returns URL for - * use within the barcode - */ + /** Issue OOB credential step 1 - prepares credential offer and returns URL for use within the barcode */ post: operations["issueCredentialConnectionLess"]; }; "/api/issuer/issue-credential/send": { @@ -229,11 +210,7 @@ export interface paths { get: operations["getPartnerProofs"]; }; "/api/partners/{id}/proof-request": { - /** - * Request proof from partner - * - * ProofExchangeController#requestProof - */ + /** Request proof from partner */ post: operations["requestProof"]; }; "/api/partners/{id}/proof-request/{templateId}": { @@ -241,11 +218,7 @@ export interface paths { put: operations["invokeProofRequestByTemplate"]; }; "/api/partners/{id}/proof-send": { - /** - * Send proof to partner - * - * ProofExchangeController#sendProof - */ + /** Send proof to partner */ post: operations["sendProof"]; }; "/api/partners/{id}/refresh": { @@ -256,6 +229,10 @@ export interface paths { /** Request proof from partner */ post: operations["requestProof_1"]; }; + "/api/proof-exchanges/proof-request/oob-attachment": { + /** OOB presentation request step 1 - prepares presentation request and returns URL for use within the barcode */ + post: operations["connectionLessPresentationRequest"]; + }; "/api/proof-exchanges/proof-send": { /** Send proof to partner */ post: operations["sendProof_1"]; @@ -271,10 +248,7 @@ export interface paths { post: operations["declinePresentProofRequest"]; }; "/api/proof-exchanges/{id}/matching-credentials": { - /** - * Manual proof exchange flow. Get matching wallet credentials before sending or - * declining the proof request. - */ + /** Manual proof exchange flow. Get matching wallet credentials before sending or declining the proof request. */ get: operations["getMatchingCredentials"]; }; "/api/proof-exchanges/{id}/matching-credentials-ld": { @@ -317,10 +291,7 @@ export interface paths { delete: operations["deleteCredential"]; }; "/api/wallet/credential/{id}/accept-offer": { - /** - * Manual credential exchange step four: Holder accepts credential offer from - * issuer - */ + /** Manual credential exchange step four: Holder accepts credential offer from issuer */ put: operations["acceptCredentialOffer"]; }; "/api/wallet/credential/{id}/decline-offer": { @@ -699,6 +670,7 @@ export interface components { invitation: components["schemas"]["InvitationMessage"]; invitationId: string; invitationUrl: string; + oobId: string; state: string; trace: boolean; updatedAt: string; @@ -741,6 +713,29 @@ export interface components { * @example {} */ document?: { [key: string]: unknown }[]; + exchangeVersion?: components["schemas"]["ExchangeVersion"]; + }; + JsonArray: components["schemas"]["JsonElement"] & { + empty?: boolean; + asNumber?: components["schemas"]["Number"]; + asString?: string; + /** Format: double */ + asDouble?: number; + asBigDecimal?: number; + asBigInteger?: number; + /** Format: float */ + asFloat?: number; + /** Format: int64 */ + asLong?: number; + /** Format: int32 */ + asInt?: number; + /** Format: byte */ + asByte?: string; + /** @deprecated */ + asCharacter?: string; + /** Format: int32 */ + asShort?: number; + asBoolean?: boolean; }; JsonElement: { jsonArray?: boolean; @@ -748,7 +743,7 @@ export interface components { jsonPrimitive?: boolean; jsonNull?: boolean; asJsonObject?: components["schemas"]["JsonObject"]; - asJsonArray?: { [key: string]: unknown }[]; + asJsonArray?: components["schemas"]["JsonArray"]; asJsonPrimitive?: components["schemas"]["JsonPrimitive"]; asJsonNull?: components["schemas"]["JsonNull"]; asBoolean?: boolean; @@ -879,6 +874,41 @@ export interface components { }; Number: { [key: string]: unknown }; Object: { [key: string]: unknown }; + Page_AriesCredential_: components["schemas"]["Slice_AriesCredential_"] & { + /** Format: int64 */ + totalSize: number; + /** Format: int32 */ + totalPages?: number; + }; + Page_AriesProofExchange_: components["schemas"]["Slice_AriesProofExchange_"] & { + /** Format: int64 */ + totalSize: number; + /** Format: int32 */ + totalPages?: number; + }; + Page_CredEx_: components["schemas"]["Slice_CredEx_"] & { + /** Format: int64 */ + totalSize: number; + /** Format: int32 */ + totalPages?: number; + }; + Page_MyDocumentAPI_: components["schemas"]["Slice_MyDocumentAPI_"] & { + /** Format: int64 */ + totalSize: number; + /** Format: int32 */ + totalPages?: number; + }; + Pageable: components["schemas"]["Sort"] & { + /** Format: int32 */ + number?: number; + /** Format: int32 */ + size: number; + /** Format: int64 */ + offset?: number; + sort: components["schemas"]["Sort"]; + unpaged?: boolean; + sorted?: boolean; + }; PaginationCommand: { /** Format: int32 */ size?: number; @@ -902,10 +932,7 @@ export interface components { lastSeen?: string | null; /** @description The fully qualified did like did:sov:123 */ did?: string; - /** - * @description If the partner supports aries, set if the partner has an endpoint that - * supports did communication - */ + /** @description If the partner supports aries, set if the partner has an endpoint that supports did communication */ ariesSupport?: boolean; /** @description aries connection id */ connectionId?: string | null; @@ -913,10 +940,7 @@ export interface components { state?: components["schemas"]["ConnectionState"] | null; /** @description history of aries connection states - excluding ping */ stateToTimestamp?: unknown; - /** - * @description aries connection label, if incoming connection set by the partner via the - * --label flag, or through rest overwrite - */ + /** @description aries connection label, if incoming connection set by the partner via the --label flag, or through rest overwrite */ label?: string | null; /** @description The partners alias or name, always set by a user in the UI */ alias?: string | null; @@ -928,31 +952,18 @@ export interface components { trustPing?: boolean | null; /** @description Aries OOB invitation message id */ invitationMsgId?: string | null; - /** - * @description The Partners Public Profile VerifiablePresentation to be used in the - * PartnerAPI - */ + /** @description The Partners Public Profile VerifiablePresentation to be used in the PartnerAPI */ verifiablePresentation?: | components["schemas"]["VerifiablePresentation_VerifiableCredential.VerifiableIndyCredential_"] | null; /** @description credential offer or proof request when using OOB invitations with attachments */ invitationRecord?: components["schemas"]["InvitationRecord"] | null; - /** - * @description Serialized PartnerCredentialType to allow filtering partners by - * supported credentials - */ + /** @description Serialized PartnerCredentialType to allow filtering partners by supported credentials */ supportedCredentials?: { [key: string]: unknown } | null; tags?: components["schemas"]["Tag"][]; }; PartnerAPI: { - /** - * @description Virtual partner name field that is calculated from the state of the Partner - * in the following order: 1. Alias set by a user (set when creating the - * connection, or editable when clicking on the pencil in the partners details) - * 2. Legal Name from public profile if set 3. aca-py label, --label flag or - * overwritten when creating the connection with the label option 4. did, public - * or peer - */ + /** @description Virtual partner name field that is calculated from the state of the Partner in the following order: 1. Alias set by a user (set when creating the connection, or editable when clicking on the pencil in the partners details) 2. Legal Name from public profile if set 3. aca-py label, --label flag or overwritten when creating the connection with the label option 4. did, public or peer */ name?: string; id?: string; /** Format: int64 */ @@ -1076,9 +1087,18 @@ export interface components { /** @enum {string} */ ProofType: "Ed25519Signature2018" | "BbsBlsSignature2020"; RequestCredentialRequest: { + /** Format: uuid */ documentId?: string; exchangeVersion?: components["schemas"]["ExchangeVersion"]; }; + RequestOOBPresentationRequest: { + alias?: string; + tag?: components["schemas"]["Tag"][]; + trustPing?: boolean; + /** Format: uuid */ + templateId: string; + exchangeVersion?: components["schemas"]["ExchangeVersion"]; + }; RequestProofRequest: { requestBySchema?: components["schemas"]["RequestProofRequest.RequestBySchema"]; /** Format: uuid */ @@ -1178,6 +1198,70 @@ export interface components { myCredentialId?: string; exchangeVersion?: components["schemas"]["ExchangeVersion"]; }; + Slice_AriesCredential_: { + content: components["schemas"]["AriesCredential"][]; + pageable: components["schemas"]["Pageable"]; + /** Format: int32 */ + pageNumber?: number; + /** Format: int64 */ + offset?: number; + /** Format: int32 */ + size?: number; + empty?: boolean; + /** Format: int32 */ + numberOfElements?: number; + }; + Slice_AriesProofExchange_: { + content: components["schemas"]["AriesProofExchange"][]; + pageable: components["schemas"]["Pageable"]; + /** Format: int32 */ + pageNumber?: number; + /** Format: int64 */ + offset?: number; + /** Format: int32 */ + size?: number; + empty?: boolean; + /** Format: int32 */ + numberOfElements?: number; + }; + Slice_CredEx_: { + content: components["schemas"]["CredEx"][]; + pageable: components["schemas"]["Pageable"]; + /** Format: int32 */ + pageNumber?: number; + /** Format: int64 */ + offset?: number; + /** Format: int32 */ + size?: number; + empty?: boolean; + /** Format: int32 */ + numberOfElements?: number; + }; + Slice_MyDocumentAPI_: { + content: components["schemas"]["MyDocumentAPI"][]; + pageable: components["schemas"]["Pageable"]; + /** Format: int32 */ + pageNumber?: number; + /** Format: int64 */ + offset?: number; + /** Format: int32 */ + size?: number; + empty?: boolean; + /** Format: int32 */ + numberOfElements?: number; + }; + Sort: { + sorted?: boolean; + orderBy: components["schemas"]["Sort.Order"][]; + }; + "Sort.Order": { + ignoreCase: boolean; + direction: components["schemas"]["Sort.Order.Direction"]; + property: string; + ascending?: boolean; + }; + /** @enum {string} */ + "Sort.Order.Direction": "ASC" | "DESC"; "StateChangeDecorator.StateToTimestamp_ConnectionState_": { stateToTimestamp?: { [key: string]: string }; }; @@ -1299,7 +1383,6 @@ export interface components { export interface operations { getDid: { - parameters: {}; responses: { /** getDid 200 response */ 200: { @@ -1329,7 +1412,6 @@ export interface operations { }; /** Get runtime configuration */ getRuntimeConfig: { - parameters: {}; responses: { /** RuntimeConfig */ 200: { @@ -1339,13 +1421,8 @@ export interface operations { }; }; }; - /** - * Trigger the backend to write configured endpoints to the ledger. TAA digest - * has to be passed to explicitly confirm prior TTA acceptance by the user for - * this ledger interaction / session. - */ + /** Trigger the backend to write configured endpoints to the ledger. TAA digest has to be passed to explicitly confirm prior TTA acceptance by the user for this ledger interaction / session. */ registerEndpoints: { - parameters: {}; responses: { /** HttpResponse */ 200: unknown; @@ -1358,7 +1435,6 @@ export interface operations { }; }; isEndpointsWriteRequired: { - parameters: {}; responses: { /** true if endpoint registration is required */ 200: { @@ -1370,7 +1446,6 @@ export interface operations { }; /** List configured schemas */ listSchemas: { - parameters: {}; responses: { /** list of SchemaAPI */ 200: { @@ -1382,7 +1457,6 @@ export interface operations { }; /** Import an existing indy or json-ld schema as schema configuration */ addSchema: { - parameters: {}; responses: { /** HttpResponse */ 200: { @@ -1438,10 +1512,7 @@ export interface operations { }; }; }; - /** - * Removes a schema configuration. Doing so means the BPA will not process - * requests containing this schema id anymore. - */ + /** Removes a schema configuration. Doing so means the BPA will not process requests containing this schema id anymore. */ removeSchema: { parameters: { path: { @@ -1523,7 +1594,6 @@ export interface operations { }; /** Get TAA record (digest, text, version) */ getTAARecord: { - parameters: {}; responses: { /** TAARecord */ 200: { @@ -1535,7 +1605,6 @@ export interface operations { }; /** List configured tags */ listTags: { - parameters: {}; responses: { /** list of TagAPI */ 200: { @@ -1547,7 +1616,6 @@ export interface operations { }; /** Add a tag */ addTag: { - parameters: {}; responses: { /** HttpResponse */ 200: { @@ -1623,7 +1691,6 @@ export interface operations { }; /** Create a connection-invitation */ requestConnectionInvitation: { - parameters: {}; responses: { /** APICreateInvitationResponse */ 200: { @@ -1641,7 +1708,6 @@ export interface operations { }; /** Receive / accept invitation */ acceptInvitation: { - parameters: {}; responses: { /** MutableHttpResponse */ 200: { @@ -1659,7 +1725,6 @@ export interface operations { }; /** Check invitation (receive) */ checkInvitation: { - parameters: {}; responses: { /** MutableHttpResponse */ 200: { @@ -1677,7 +1742,6 @@ export interface operations { }; /** List credential definitions, items that I can issue */ listCredDefs: { - parameters: {}; responses: { /** list of SchemaAPI */ 200: { @@ -1689,7 +1753,6 @@ export interface operations { }; /** Create a new indy credential definition, and send it to the ledger */ createCredDef: { - parameters: {}; responses: { /** CredDef */ 200: { @@ -1705,7 +1768,7 @@ export interface operations { }; }; }; - /** Delete a indy credential definition (will not delete it from the ledger) */ + /** Delete an indy credential definition (will not delete it from the ledger) */ deleteCredDef: { parameters: { path: { @@ -1734,7 +1797,7 @@ export interface operations { /** list of CredEx */ 200: { content: { - "application/json": components["schemas"]["CredEx"][]; + "application/json": components["schemas"]["Page_CredEx_"]; }; }; }; @@ -1755,10 +1818,7 @@ export interface operations { }; }; }; - /** - * Manual credential exchange: Issuer declines credential proposal received from - * the holder - */ + /** Manual credential exchange: Issuer declines credential proposal received from the holder */ declineCredentialExchange: { parameters: { path: { @@ -1777,10 +1837,7 @@ export interface operations { }; }; }; - /** - * Send holder a new credential offer based on an existing (revoked) exchange - * record - */ + /** Send holder a new credential offer based on an existing (revoked) exchange record */ reIssueCredential: { parameters: { path: { @@ -1810,10 +1867,7 @@ export interface operations { }; }; }; - /** - * Manual credential exchange step two: Issuer sends credential counter offer to - * holder (in reference to a proposal) - */ + /** Manual credential exchange step two: Issuer sends credential counter offer to holder (in reference to a proposal) */ sendCredentialOffer: { parameters: { path: { @@ -1836,12 +1890,8 @@ export interface operations { }; }; }; - /** - * Issue OOB credential step 1 - prepares credential offer and returns URL for - * use within the barcode - */ + /** Issue OOB credential step 1 - prepares credential offer and returns URL for use within the barcode */ issueCredentialConnectionLess: { - parameters: {}; responses: { /** APICreateInvitationResponse */ 200: { @@ -1859,7 +1909,6 @@ export interface operations { }; /** Auto credential exchange: Issuer sends credential to holder */ issueCredential: { - parameters: {}; responses: { /** HttpResponse */ 200: { @@ -1877,7 +1926,6 @@ export interface operations { }; /** Create a new schema on the indy ledger and import it */ createSchema: { - parameters: {}; responses: { /** SchemaAPI */ 200: { @@ -1895,7 +1943,6 @@ export interface operations { }; /** Manual trigger to send an invitation url by email */ sendInvitationMail: { - parameters: {}; responses: { /** HTTP status */ 200: unknown; @@ -1909,7 +1956,6 @@ export interface operations { }; /** List message templates */ listMessageTemplates: { - parameters: {}; responses: { /** list of MessageTemplateCmd.ApiMessageTemplate */ 200: { @@ -1921,7 +1967,6 @@ export interface operations { }; /** Add new message template */ addMessageTemplate: { - parameters: {}; responses: { /** MessageTemplateCmd.ApiMessageTemplate */ 200: { @@ -1971,7 +2016,6 @@ export interface operations { }; /** List message trigger configuration */ listMessageTrigger: { - parameters: {}; responses: { /** list of MessageTriggerConfigCmd.ApiTriggerConfig */ 200: { @@ -1983,7 +2027,6 @@ export interface operations { }; /** ASdd new message trigger configuration */ addMessageTrigger: { - parameters: {}; responses: { /** MessageTriggerConfigCmd.TriggerConfigRequest */ 200: { @@ -2033,7 +2076,6 @@ export interface operations { }; /** List user info */ listUserInfo: { - parameters: {}; responses: { /** list of MessageUserInfoCmd.ApiUserInfo */ 200: { @@ -2045,7 +2087,6 @@ export interface operations { }; /** Add new user info configuration */ addUserInfo: { - parameters: {}; responses: { /** MessageUserInfoCmd.ApiUserInfo */ 200: { @@ -2112,7 +2153,6 @@ export interface operations { }; /** Add a new partner */ addPartner: { - parameters: {}; responses: { /** PartnerAPI */ 200: { @@ -2292,8 +2332,12 @@ export interface operations { /** List proof exchange records */ getPartnerProofs: { parameters: { + query: { + /** PaginationCommand */ + pc?: components["schemas"]["PaginationCommand"] | null; + }; path: { - /** UUID the partner id */ + /** partner id */ id: string; }; }; @@ -2301,16 +2345,12 @@ export interface operations { /** HTTP status */ 200: { content: { - "application/json": components["schemas"]["AriesProofExchange"][]; + "application/json": components["schemas"]["Page_AriesProofExchange_"]; }; }; }; }; - /** - * Request proof from partner - * - * ProofExchangeController#requestProof - */ + /** Request proof from partner */ requestProof: { parameters: { path: { @@ -2350,11 +2390,7 @@ export interface operations { }; }; }; - /** - * Send proof to partner - * - * ProofExchangeController#sendProof - */ + /** Send proof to partner */ sendProof: { parameters: { path: { @@ -2392,7 +2428,6 @@ export interface operations { }; /** Request proof from partner */ requestProof_1: { - parameters: {}; responses: { /** HTTP status */ 200: unknown; @@ -2404,9 +2439,25 @@ export interface operations { }; }; }; + /** OOB presentation request step 1 - prepares presentation request and returns URL for use within the barcode */ + connectionLessPresentationRequest: { + responses: { + /** APICreateInvitationResponse */ + 200: { + content: { + "application/json": components["schemas"]["APICreateInvitationResponse"]; + }; + }; + }; + /** RequestOOBPresentationRequest */ + requestBody: { + content: { + "application/json": components["schemas"]["RequestOOBPresentationRequest"]; + }; + }; + }; /** Send proof to partner */ sendProof_1: { - parameters: {}; responses: { /** HTTP status */ 200: unknown; @@ -2467,10 +2518,7 @@ export interface operations { }; }; }; - /** - * Manual proof exchange flow. Get matching wallet credentials before sending or - * declining the proof request. - */ + /** Manual proof exchange flow. Get matching wallet credentials before sending or declining the proof request. */ getMatchingCredentials: { parameters: { path: { @@ -2523,7 +2571,6 @@ export interface operations { }; /** List configured templates */ listProofTemplates: { - parameters: {}; responses: { /** list of ProofTemplate */ 200: { @@ -2535,7 +2582,6 @@ export interface operations { }; /** Add a new proof template */ addProofTemplate: { - parameters: {}; responses: { /** ProofTemplate */ 200: { @@ -2553,7 +2599,12 @@ export interface operations { }; /** List configured proof condition operators */ listKnownConditionOperators: { - parameters: {}; + parameters: { + query: { + /** CredentialType filter operators by type */ + type?: components["schemas"]["CredentialType"] | null; + }; + }; responses: { /** list of ValueOperators */ 200: { @@ -2595,7 +2646,6 @@ export interface operations { }; /** Get simple BPA information and usage statistics */ getStats: { - parameters: {}; responses: { /** BPAStats */ 200: { @@ -2617,7 +2667,7 @@ export interface operations { /** list of AriesCredential */ 200: { content: { - "application/json": components["schemas"]["AriesCredential"][]; + "application/json": components["schemas"]["Page_AriesCredential_"]; }; }; }; @@ -2671,10 +2721,7 @@ export interface operations { 200: unknown; }; }; - /** - * Manual credential exchange step four: Holder accepts credential offer from - * issuer - */ + /** Manual credential exchange step four: Holder accepts credential offer from issuer */ acceptCredentialOffer: { parameters: { path: { @@ -2731,14 +2778,13 @@ export interface operations { /** list of MyDocumentAPI */ 200: { content: { - "application/json": components["schemas"]["Object"][]; + "application/json": components["schemas"]["Page_MyDocumentAPI_"]; }; }; }; }; /** Add a document to the wallet */ addDocument: { - parameters: {}; responses: { /** HttpResponse */ 200: { @@ -2808,7 +2854,6 @@ export interface operations { }; }; getMasterdata: { - parameters: {}; responses: { /** getMasterdata 200 response */ 200: { diff --git a/frontend/src/components/issue/IssueCredentialIndyOob.vue b/frontend/src/components/issue/IssueCredentialIndyOob.vue index 9e8777976..3df796862 100644 --- a/frontend/src/components/issue/IssueCredentialIndyOob.vue +++ b/frontend/src/components/issue/IssueCredentialIndyOob.vue @@ -203,6 +203,19 @@ + + + + {{ $t("button.useV2") }} + + + + + + @@ -273,6 +286,7 @@ import VBpaButton from "@/components/BpaButton"; import * as textUtils from "@/utils/textUtils"; import * as CSV from "csv-string"; import QrcodeVue from "qrcode.vue"; +import { ExchangeVersion } from "@/constants"; export default { name: "IssueCredentialIndyOob", @@ -296,6 +310,7 @@ export default { selectedTags: new Array(), // Disable trust ping for invitation to mobile wallets by default trustPing: false, + useV2Exchange: false, invitationUrl: "", createDisabled: true, expertLoad: { @@ -378,6 +393,9 @@ export default { alias: this.alias, tag: tags, trustPing: this.trustPing, + exchangeVersion: this.useV2Exchange + ? ExchangeVersion.V2 + : ExchangeVersion.V1, credDefId: this.credDef.id, document: document, };