Skip to content

Commit

Permalink
Merge branch 'main' into update/main/dal
Browse files Browse the repository at this point in the history
  • Loading branch information
yshyn-iohk authored Jan 20, 2025
2 parents 49b449a + d436bd1 commit ffc3458
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,11 @@ class IssueControllerImpl(
for {
issuingDID <- getIssuingDIDFromAnonCredsProperties(request)
credentialDefinitionGUID <- ZIO
.fromOption(request.credentialDefinitionId)
.fromOption(
request.anoncredsVcPropertiesV1
.map(_.credentialDefinitionId)
.orElse(request.credentialDefinitionId)
)
.mapError(_ =>
ErrorResponse.badRequest(detail = Some("Missing request parameter: credentialDefinitionId"))
)
Expand Down Expand Up @@ -219,6 +223,9 @@ class IssueControllerImpl(
claims <- ZIO
.fromOption(request.anoncredsVcPropertiesV1.map(_.claims).orElse(request.claims))
.orElseFail(ErrorResponse.badRequest(detail = Some("Missing request parameter: claims")))
validityPeriod = request.anoncredsVcPropertiesV1
.flatMap(_.validityPeriod)
.orElse(request.validityPeriod)
record <- credentialService
.createAnonCredsIssueCredentialRecord(
pairwiseIssuerDID = offerContext.pairwiseIssuerDID,
Expand All @@ -227,7 +234,7 @@ class IssueControllerImpl(
credentialDefinitionGUID = credentialDefinitionGUID,
credentialDefinitionId = credentialDefinitionId,
claims = claims,
validityPeriod = request.validityPeriod,
validityPeriod = validityPeriod,
automaticIssuance = request.automaticIssuance.orElse(Some(true)),
goalCode = offerContext.goalCode,
goal = offerContext.goal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,15 @@ case class AnonCredsVCPropertiesV1(
@description(annotations.issuingDID.description)
@encodedExample(annotations.issuingDID.example)
issuingDID: String,
@description(AnonCredsVCPropertiesV1.annotations.schemaId.description)
@encodedExample(AnonCredsVCPropertiesV1.annotations.schemaId.example)
schemaId: String,
@description(AnonCredsVCPropertiesV1.annotations.credentialDefinitionId.description)
@encodedExample(AnonCredsVCPropertiesV1.annotations.credentialDefinitionId.example)
credentialDefinitionId: String,
credentialDefinitionId: UUID,
@description(AnonCredsVCPropertiesV1.annotations.claims.description)
@encodedExample(AnonCredsVCPropertiesV1.annotations.claims.example)
claims: zio.json.ast.Json
claims: zio.json.ast.Json,
@description(annotations.validityPeriod.description)
@encodedExample(annotations.validityPeriod.example)
validityPeriod: Option[Double]
)

object AnonCredsVCPropertiesV1 {
Expand All @@ -209,15 +209,6 @@ object AnonCredsVCPropertiesV1 {
given decoder: JsonDecoder[AnonCredsVCPropertiesV1] = DeriveJsonDecoder.gen

object annotations {
object schemaId
extends Annotation[String](
description = """
|The URL or DIDURL pointing to the AnonCreds schema that will be used for this offer.
|When dereferenced, the returned content should be a JSON schema compliant with the '[AnonCreds v1.0 schema](https://hyperledger.github.io/anoncreds-spec/#term:schema)' version of the specification.
|""".stripMargin,
example =
"https://agent-host.com/cloud-agent/schema-registry/schemas/d9569cec-c81e-4779-aa86-0d5994d82676/schema"
)
object credentialDefinitionId
extends Annotation[UUID](
description = """
Expand Down
2 changes: 1 addition & 1 deletion infrastructure/local/.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
AGENT_VERSION=1.40.0
PRISM_NODE_VERSION=2.3.0
PRISM_NODE_VERSION=2.5.0
VAULT_DEV_ROOT_TOKEN_ID=root
2 changes: 1 addition & 1 deletion infrastructure/single-tenant-testing-stack/.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
AGENT_VERSION=1.25.0
PRISM_NODE_VERSION=2.3.0
PRISM_NODE_VERSION=2.5.0
VAULT_DEV_ROOT_TOKEN_ID=root
4 changes: 3 additions & 1 deletion release.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ export default {
{ name: 'beta', prerelease: true }
],
plugins: [
'@semantic-release/commit-analyzer',
['@semantic-release/commit-analyzer', {
"preset": "conventionalcommits"
}],
["@semantic-release/exec", {
"prepareCmd": "echo ${nextRelease.version} > .release-version"
}],
Expand Down
8 changes: 4 additions & 4 deletions tests/integration-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ The following variables must be set before running the tests:
* `AGENT_VERSION`: version of the ICA docker image to use.

```shell
TESTS_CONFIG=/configs/basic.conf PRISM_NODE_VERSION=2.3.0 AGENT_VERSION=1.36.1 ./gradlew test
TESTS_CONFIG=/configs/basic.conf PRISM_NODE_VERSION=2.5.0 AGENT_VERSION=1.36.1 ./gradlew test
```

> Please note: there is no need to pass environment variables if you're using already running agents.
Expand All @@ -414,13 +414,13 @@ To simplify the execution, each configuration file creates a new `gradle` task.
It's possible to execute the configuration file as

```shell
PRISM_NODE_VERSION=2.3.0 AGENT_VERSION=1.36.1 ./gradlew test_basic
PRISM_NODE_VERSION=2.5.0 AGENT_VERSION=1.36.1 ./gradlew test_basic
```

Also, it's possible to execute the integration tests to all configurations files. The task is named `regression`, it should take a long time to execute.

```shell
PRISM_NODE_VERSION=2.3.0 AGENT_VERSION=1.36.1 ./gradlew regression
PRISM_NODE_VERSION=2.5.0 AGENT_VERSION=1.36.1 ./gradlew regression
```

#### Regression report
Expand All @@ -432,7 +432,7 @@ To run all scenarios, even if there's a failure, it's required to add `--continu

Example
```bash
AGENT_VERSION=v1.36.1 PRISM_NODE_VERSION=v2.3.0 ./gradlew regression --continue
AGENT_VERSION=v1.36.1 PRISM_NODE_VERSION=v2.5.0 ./gradlew regression --continue
```

Each `context` is based on the configuration used for the current execution and will be displayed in the
Expand Down
2 changes: 1 addition & 1 deletion tests/integration-tests/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ dependencies {
testImplementation("io.ktor:ktor-server-netty:2.3.0")
testImplementation("io.ktor:ktor-client-apache:2.3.0")
// RestAPI client
testImplementation("org.hyperledger.identus:cloud-agent-client-kotlin:1.40.1-8363d1e")
testImplementation("org.hyperledger.identus:cloud-agent-client-kotlin:1.40.1-1766228")
// Test helpers library
testImplementation("io.iohk.atala:atala-automation:0.4.0")
// Hoplite for configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import java.util.UUID

enum class CreateCredentialOfferAPIVersion {
V0 {
override fun buildCredentialOfferRequest(
override fun buildJWTCredentialOfferRequest(
credentialType: CredentialType,
did: String,
assertionKey: String,
Expand All @@ -23,10 +23,46 @@ enum class CreateCredentialOfferAPIVersion {
credentialFormat = credentialType.format,
automaticIssuance = false,
)

override fun buildSDJWTCredentialOfferRequest(
credentialType: CredentialType,
did: String,
assertionKey: String,
schemaUrl: String?,
claims: Map<String, Any>,
connectionId: UUID,
validityPeriod: Double?,
): CreateIssueCredentialRecordRequest = CreateIssueCredentialRecordRequest(
schemaId = schemaUrl,
claims = claims,
issuingDID = did,
issuingKid = assertionKey,
connectionId = connectionId,
validityPeriod = validityPeriod ?: 3600.0,
credentialFormat = credentialType.format,
automaticIssuance = false,
)

override fun buildAnonCredsCredentialOfferRequest(
credentialType: CredentialType,
did: String,
credentialDefinitionId: UUID,
claims: Map<String, Any>,
connectionId: UUID,
validityPeriod: Double?,
) = CreateIssueCredentialRecordRequest(
credentialDefinitionId = credentialDefinitionId,
claims = claims,
issuingDID = did,
connectionId = connectionId,
validityPeriod = validityPeriod ?: 3600.0,
credentialFormat = credentialType.format,
automaticIssuance = false,
)
},

V1 {
override fun buildCredentialOfferRequest(
override fun buildJWTCredentialOfferRequest(
credentialType: CredentialType,
did: String,
assertionKey: String,
Expand All @@ -53,8 +89,25 @@ enum class CreateCredentialOfferAPIVersion {
validityPeriod = validityPeriod ?: 3600.0,
)
} else {
null
throw IllegalArgumentException("Unsupported credential type: $credentialType")
},
)

override fun buildSDJWTCredentialOfferRequest(
credentialType: CredentialType,
did: String,
assertionKey: String,
schemaUrl: String?,
claims: Map<String, Any>,
connectionId: UUID,
validityPeriod: Double?,
): CreateIssueCredentialRecordRequest = CreateIssueCredentialRecordRequest(
issuingKid = assertionKey,
connectionId = connectionId,
credentialFormat = credentialType.format,
automaticIssuance = false,
claims = null,
issuingDID = null,
sdJwtVcPropertiesV1 = if (credentialType == CredentialType.SD_JWT_VCDM_1_1) {
SDJWTVCPropertiesV1(
credentialSchema = CredentialSchemaRef(
Expand All @@ -67,13 +120,42 @@ enum class CreateCredentialOfferAPIVersion {
validityPeriod = validityPeriod ?: 3600.0,
)
} else {
null
throw IllegalArgumentException("Unsupported credential type: $credentialType")
},
)

override fun buildAnonCredsCredentialOfferRequest(
credentialType: CredentialType,
did: String,
credentialDefinitionId: UUID,
claims: Map<String, Any>,
connectionId: UUID,
validityPeriod: Double?,
) = CreateIssueCredentialRecordRequest(
connectionId = connectionId,
credentialFormat = credentialType.format,
automaticIssuance = false,
anoncredsVcPropertiesV1 = AnonCredsVCPropertiesV1(
issuingDID = did,
credentialDefinitionId = credentialDefinitionId,
claims = claims,
validityPeriod = validityPeriod ?: 3600.0,
),
)
},
;

abstract fun buildCredentialOfferRequest(
abstract fun buildJWTCredentialOfferRequest(
credentialType: CredentialType,
did: String,
assertionKey: String,
schemaUrl: String?,
claims: Map<String, Any>,
connectionId: UUID,
validityPeriod: Double? = null,
): CreateIssueCredentialRecordRequest

abstract fun buildSDJWTCredentialOfferRequest(
credentialType: CredentialType,
did: String,
assertionKey: String,
Expand All @@ -82,4 +164,13 @@ enum class CreateCredentialOfferAPIVersion {
connectionId: UUID,
validityPeriod: Double? = null,
): CreateIssueCredentialRecordRequest

abstract fun buildAnonCredsCredentialOfferRequest(
credentialType: CredentialType,
did: String,
credentialDefinitionId: UUID,
claims: Map<String, Any>,
connectionId: UUID,
validityPeriod: Double? = null,
): CreateIssueCredentialRecordRequest
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ enum class CredentialClaims {
"lastName" to "Doe",
)
},
ANONCREDS_STUDENT_CLAIMS {
override val claims: Map<String, Any> = linkedMapOf(
"name" to "Bob",
"age" to "21",
"sex" to "M",
)
},
;

abstract val claims: Map<String, Any>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package steps.credentials

import common.CreateCredentialOfferAPIVersion
import common.CredentialType
import interactions.Post
import interactions.body
import io.cucumber.java.en.When
Expand Down Expand Up @@ -47,4 +49,39 @@ class AnoncredSteps {
issuer.remember("thid", credentialRecord.thid)
holder.remember("thid", credentialRecord.thid)
}

@When("{actor} sends the prepared anoncreds credential offer to {actor}")
fun issuerSendsThePreparedAnonCredsOfferToHolder(issuer: Actor, holder: Actor) {
val api = issuer.recall<CreateCredentialOfferAPIVersion>("currentAPI")
val credentialType = issuer.recall<CredentialType>("currentCredentialType")
val did = issuer.recall<String>("currentDID")
val claims = issuer.recall<Map<String, Any>>("currentClaims")
val connectionId = issuer.recall<Connection>("connection-with-${holder.name}").connectionId
val credentialDefinitionId = issuer.recall<CredentialDefinitionResponse>("anoncredsCredentialDefinition").guid

val request = api.buildAnonCredsCredentialOfferRequest(credentialType, did, credentialDefinitionId, claims, connectionId)

val credentialOfferRequest = CreateIssueCredentialRecordRequest(
credentialDefinitionId = issuer.recall<CredentialDefinitionResponse>("anoncredsCredentialDefinition").guid,
claims = linkedMapOf(
"name" to "Bob",
"age" to "21",
"sex" to "M",
),
issuingDID = issuer.recall("shortFormDid"),
connectionId = issuer.recall<Connection>("connection-with-${holder.name}").connectionId,
validityPeriod = 3600.0,
credentialFormat = "AnonCreds",
automaticIssuance = false,
)

issuer.attemptsTo(
Post.to("/issue-credentials/credential-offers").body(credentialOfferRequest),
Ensure.thatTheLastResponse().statusCode().isEqualTo(SC_CREATED),
)

val credentialRecord = SerenityRest.lastResponse().get<IssueCredentialRecord>()
issuer.remember("thid", credentialRecord.thid)
holder.remember("thid", credentialRecord.thid)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class CredentialSteps {
val api = issuer.recall<CreateCredentialOfferAPIVersion>("currentAPI")
val credentialType = issuer.recall<CredentialType>("currentCredentialType")
val did = issuer.recall<String>("currentDID")

val assertionKey = issuer.recall<String>("currentAssertionKey")
val schema = issuer.recall<CredentialSchema>("currentSchema")
val schemaGuid = issuer.recall<String>(schema.name)
Expand All @@ -89,7 +90,11 @@ class CredentialSteps {
"${issuer.recall<String>("baseUrl")}/schema-registry/schemas/$it"
}

val credentialOfferRequest = api.buildCredentialOfferRequest(credentialType, did, assertionKey, schemaUrl!!, claims, connectionId)
val credentialOfferRequest = when (credentialType) {
CredentialType.JWT_VCDM_1_1 -> api.buildJWTCredentialOfferRequest(credentialType, did, assertionKey, schemaUrl!!, claims, connectionId)
CredentialType.SD_JWT_VCDM_1_1 -> api.buildSDJWTCredentialOfferRequest(credentialType, did, assertionKey, schemaUrl!!, claims, connectionId)
else -> throw IllegalArgumentException("Unsupported credential type: $credentialType")
}

issuer.attemptsTo(
Post.to("/issue-credentials/credential-offers").body(credentialOfferRequest),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@anoncred @issuance @refactoring
Feature: Issue Anoncreds credential using different REST API request versions

Background:
Given Issuer and Holder have an existing connection
And Issuer has a published DID for 'ANONCRED'
And Issuer has an anoncred schema definition
And Holder has an unpublished DID for 'ANONCRED'

Scenario Outline: Issuing jwt credential using different API version
When Issuer prepares the credential in 'ANONCREDS_V1' format using the '<createCredentialOfferApiVersion>' API
And Issuer prepares to use a 'short' form of DID with key id 'assertion-1'
And Issuer prepares the claims '<claims>' for the credential
And Issuer sends the prepared anoncreds credential offer to Holder
And Holder receives the credential offer
And Holder accepts anoncred credential offer
And Issuer issues the credential
Then Holder receives the issued credential
Examples:
| createCredentialOfferApiVersion | claims |
| V0 | ANONCREDS_STUDENT_CLAIMS |
| V1 | ANONCREDS_STUDENT_CLAIMS |

0 comments on commit ffc3458

Please sign in to comment.