From 6a848b9d22f4ec9ff954864d9f2cc0540035a075 Mon Sep 17 00:00:00 2001 From: Kevin Burgmann Date: Wed, 15 Mar 2023 20:10:37 +0100 Subject: [PATCH 1/5] Performance improvements --- build.gradle.kts | 1 + gradle/wrapper/gradle-wrapper.properties | 2 +- src/main/kotlin/id/walt/DidCheqdTest.kt | 12 ----- .../w3c/templates/VcTemplateManager.kt | 43 +++++++++++++----- .../kotlin/id/walt/services/did/DidService.kt | 19 ++++++-- .../id/walt/services/jwt/WaltIdJwtService.kt | 8 ++-- .../kotlin/id/walt/signatory/Signatory.kt | 10 +++-- .../signatory/rest/SignatoryController.kt | 2 +- src/test/kotlin/id/walt/Performance.kt | 45 ++++--------------- 9 files changed, 70 insertions(+), 72 deletions(-) delete mode 100644 src/main/kotlin/id/walt/DidCheqdTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index ce6c3de7..09430b4a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -68,6 +68,7 @@ dependencies { // Misc implementation("commons-io:commons-io:2.11.0") implementation("io.minio:minio:8.4.6") + implementation("io.github.reactivecircus.cache4k:cache4k:0.9.0") // HTTP implementation("io.ktor:ktor-client-jackson-jvm:2.2.4") diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f72df95a..e1bef7e8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/kotlin/id/walt/DidCheqdTest.kt b/src/main/kotlin/id/walt/DidCheqdTest.kt deleted file mode 100644 index ab0ae478..00000000 --- a/src/main/kotlin/id/walt/DidCheqdTest.kt +++ /dev/null @@ -1,12 +0,0 @@ -package id.walt - -import id.walt.servicematrix.ServiceMatrix -import id.walt.services.did.DidService -import java.io.File - -fun main() { - ServiceMatrix("service-matrix.properties") - val did = DidService.importDidFromFile(File("did-cheqd.json")) - - println("DID imported: $did") -} diff --git a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt index c0b7be7d..615f49f7 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt @@ -5,12 +5,15 @@ import id.walt.credentials.w3c.W3CIssuer import id.walt.credentials.w3c.toVerifiableCredential import id.walt.services.context.ContextManager import id.walt.services.hkvstore.HKVKey +import io.github.reactivecircus.cache4k.Cache +import kotlinx.coroutines.runBlocking import mu.KotlinLogging import java.io.File import java.nio.file.FileSystems import java.nio.file.Files import kotlin.io.path.isRegularFile import kotlin.io.path.nameWithoutExtension +import kotlin.time.Duration.Companion.hours object VcTemplateManager { private val log = KotlinLogging.logger {} @@ -25,20 +28,40 @@ object VcTemplateManager { return VcTemplate(name, template, true) } + val templateCache = Cache.Builder() + .maximumCacheSize(1000) + .expireAfterWrite(1.hours) + .build() + + private fun String?.toVcTemplate(name: String, loadTemplate: Boolean, isMutable: Boolean) = + this?.let { VcTemplate(name, if (loadTemplate) it.toVerifiableCredential() else null, isMutable) } + + fun loadTemplateFromHkvStore(name: String, loadTemplate: Boolean) = + ContextManager.hkvStore.getAsString(HKVKey(SAVED_VC_TEMPLATES_KEY, name)) + .toVcTemplate(name, loadTemplate, true) + + fun loadTemplateFromResources(name: String, loadTemplate: Boolean) = + object {}.javaClass.getResource("/vc-templates/$name.json")?.readText() + .toVcTemplate(name, loadTemplate, false) + + fun loadTemplateFromFile(name: String, loadTemplate: Boolean, runtimeTemplateFolder: String) = + File("$runtimeTemplateFolder/$name.json").let { + if (it.exists()) it.readText() else null + }.toVcTemplate(name, loadTemplate, false) + fun getTemplate( name: String, loadTemplate: Boolean = true, runtimeTemplateFolder: String = "/vc-templates-runtime" - ): VcTemplate { - return ContextManager.hkvStore.getAsString(HKVKey(SAVED_VC_TEMPLATES_KEY, name)) - ?.let { VcTemplate(name, if (loadTemplate) it.toVerifiableCredential() else null, true) } - ?: object {}.javaClass.getResource("/vc-templates/$name.json")?.readText() - ?.let { VcTemplate(name, if (loadTemplate) it.toVerifiableCredential() else null, false) } - ?: File("$runtimeTemplateFolder/$name.json").let { - if (it.exists()) it.readText() else null - }?.let { VcTemplate(name, if (loadTemplate) it.toVerifiableCredential() else null, false) } - ?: throw IllegalArgumentException("No template found, with name $name") - } + ): VcTemplate = + runBlocking { + templateCache.get(name) { + loadTemplateFromHkvStore(name, true)//.also { println("At $name HKV: $it") } + ?: loadTemplateFromResources(name, true)//.also { println("At $name resources: $it") } + ?: loadTemplateFromFile(name, true, runtimeTemplateFolder)//.also { println("At $name file: $it") } + ?: throw IllegalArgumentException("No template found with name: $name") + } + } private val resourceWalk = lazy { diff --git a/src/main/kotlin/id/walt/services/did/DidService.kt b/src/main/kotlin/id/walt/services/did/DidService.kt index c9a69b59..9ff51af6 100644 --- a/src/main/kotlin/id/walt/services/did/DidService.kt +++ b/src/main/kotlin/id/walt/services/did/DidService.kt @@ -21,6 +21,7 @@ import id.walt.services.key.KeyService import id.walt.services.keystore.KeyType import id.walt.services.vc.JsonLdCredentialService import id.walt.signatory.ProofConfig +import io.github.reactivecircus.cache4k.Cache import io.ipfs.multibase.Multibase import io.ktor.client.plugins.* import io.ktor.client.request.* @@ -40,6 +41,7 @@ import java.security.KeyFactory import java.security.KeyPair import java.security.spec.X509EncodedKeySpec import java.util.* +import kotlin.time.Duration.Companion.minutes private val log = KotlinLogging.logger {} @@ -109,10 +111,19 @@ object DidService { } } - fun load(did: String): Did = load(DidUrl.from(did)) - fun load(didUrl: DidUrl): Did = Did.decode( - loadDid(didUrl.did) ?: throw IllegalArgumentException("DID $didUrl not found.") - ) ?: throw IllegalArgumentException("DID $didUrl not found.") + val didCache = Cache.Builder() + .maximumCacheSize(1000) + .expireAfterWrite(15.minutes) + .build() + + + fun load(did: String): Did = runBlocking { load(DidUrl.from(did)) } + suspend fun load(didUrl: DidUrl): Did = + didCache.get(didUrl) { + Did.decode( + loadDid(didUrl.did) ?: throw IllegalArgumentException("DID $didUrl not found.") + ) ?: throw IllegalArgumentException("DID $didUrl not found.") + } fun resolveDidEbsiRaw(did: String): String = runBlocking { log.debug { "Resolving DID $did" } diff --git a/src/main/kotlin/id/walt/services/jwt/WaltIdJwtService.kt b/src/main/kotlin/id/walt/services/jwt/WaltIdJwtService.kt index f77b061f..cdec53df 100644 --- a/src/main/kotlin/id/walt/services/jwt/WaltIdJwtService.kt +++ b/src/main/kotlin/id/walt/services/jwt/WaltIdJwtService.kt @@ -67,17 +67,17 @@ open class WaltIdJwtService : JwtService() { private fun createSignedJwt(jwsAlgorithm: JWSAlgorithm, keyAlias: String, claimsSet: JWTClaimsSet, includeJwk: JWK?) = SignedJWT( - /* header = */ JWSHeader + JWSHeader .Builder(jwsAlgorithm) .keyID(keyAlias) .type(JOSEObjectType.JWT) .apply { includeJwk?.let { jwk(it) } }.build(), - /* claimsSet = */ claimsSet - ).also { + claimsSet + )/*.also { // log.debug { "Created signable JWT object: $it." } - } + }*/ override fun sign( diff --git a/src/main/kotlin/id/walt/signatory/Signatory.kt b/src/main/kotlin/id/walt/signatory/Signatory.kt index 364f6cfa..28c729f0 100644 --- a/src/main/kotlin/id/walt/signatory/Signatory.kt +++ b/src/main/kotlin/id/walt/signatory/Signatory.kt @@ -42,7 +42,7 @@ data class ProofConfig( @Json(serializeNull = false) val subjectDid: String? = null, @Json(serializeNull = false) val verifierDid: String? = null, @Json(serializeNull = false) val issuerVerificationMethod: String? = null, // DID URL that defines key ID; if null the issuers' default key is used - val proofType: ProofType = ProofType.LD_PROOF, + val proofType: ProofType = ProofType.JWT, @Json(serializeNull = false) val domain: String? = null, @Json(serializeNull = false) val nonce: String? = null, @Json(serializeNull = false) val proofPurpose: String? = null, @@ -90,6 +90,7 @@ abstract class Signatory : WaltIdService() { open fun importTemplate(templateId: String, template: String): Unit = implementation.importTemplate(templateId, template) open fun removeTemplate(templateId: String): Unit = implementation.removeTemplate(templateId) + open fun hasTemplateId(templateId: String): Boolean = implementation.hasTemplateId(templateId) } class WaltIdSignatory(configurationPath: String) : Signatory() { @@ -149,7 +150,8 @@ class WaltIdSignatory(configurationPath: String) : Signatory() { val credentialBuilder = when (Files.exists(Path.of(templateIdOrFilename))) { true -> Files.readString(Path.of(templateIdOrFilename)).toVerifiableCredential() else -> VcTemplateManager.getTemplate(templateIdOrFilename, true, configuration.templatesFolder).template - }?.let { W3CCredentialBuilder.fromPartial(it) } ?: throw Exception("Template not found") + }?.let { /*println("Issuing with $it");*/ W3CCredentialBuilder.fromPartial(it) } + ?: throw Exception("Template could not be loaded") return issue(dataProvider?.populate(credentialBuilder, config) ?: credentialBuilder, config, issuer, storeCredential) } @@ -173,7 +175,7 @@ class WaltIdSignatory(configurationPath: String) : Signatory() { fullProofConfig.expirationDate?.let { setExpirationDate(it) } }.build() - log.info { "Signing credential with proof using ${fullProofConfig.proofType.name}..." } + log.debug { "Signing credential with proof using ${fullProofConfig.proofType.name}..." } log.debug { "Signing credential with proof using ${fullProofConfig.proofType.name}, credential is: $vcRequest" } val signedVc = when (fullProofConfig.proofType) { ProofType.LD_PROOF -> JsonLdCredentialService.getService().sign(vcRequest.toJson(), fullProofConfig) @@ -188,6 +190,8 @@ class WaltIdSignatory(configurationPath: String) : Signatory() { return signedVc } + override fun hasTemplateId(templateId: String) = runCatching { VcTemplateManager.getTemplate(templateId, true) }.getOrNull() != null + override fun listTemplates(): List = VcTemplateManager.listTemplates(configuration.templatesFolder) override fun listTemplateIds() = VcTemplateManager.listTemplates(configuration.templatesFolder).map { it.name } override fun loadTemplate(templateId: String): VerifiableCredential = diff --git a/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt b/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt index f2f4d0c2..fa735f4a 100644 --- a/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt +++ b/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt @@ -68,7 +68,7 @@ object SignatoryController { fun issueCredential(ctx: Context) { val req = ctx.bodyAsClass() - if (req.templateId != null && !signatory.listTemplateIds().contains(req.templateId)) { + if (req.templateId != null && !signatory.hasTemplateId(req.templateId)) { throw BadRequestResponse("Template with supplied id does not exist.") } if (req.templateId == null && req.credentialData == null) { diff --git a/src/test/kotlin/id/walt/Performance.kt b/src/test/kotlin/id/walt/Performance.kt index 406e2cb0..70021b1d 100644 --- a/src/test/kotlin/id/walt/Performance.kt +++ b/src/test/kotlin/id/walt/Performance.kt @@ -4,9 +4,16 @@ import id.walt.credentials.w3c.W3CIssuer import id.walt.model.DidMethod import id.walt.servicematrix.ServiceMatrix import id.walt.services.did.DidService +import id.walt.signatory.ProofConfig +import id.walt.signatory.ProofType import id.walt.signatory.Signatory import id.walt.test.getTemplate import io.kotest.core.spec.style.StringSpec +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlin.system.measureTimeMillis class Performance : StringSpec({ @@ -20,12 +27,9 @@ class Performance : StringSpec({ template.issuer = W3CIssuer(issuerWebDid) template.credentialSubject!!.id = issuerWebDid // self signed - val credOffer: String = template.toJson() - - /* "Issue 1" { val jobs = ArrayList() - repeat(10000) { + repeat(100000) { jobs.add(GlobalScope.launch { println("" + measureTimeMillis { signatory.issue("VerifiableId", ProofConfig(issuerDid = issuerWebDid, proofType = ProofType.JWT)) @@ -36,37 +40,4 @@ class Performance : StringSpec({ jobs.forEach { it.join() } } - "Issue 2" { - val jobs = ArrayList() - - measureTimeMillis { - - repeat(50000) { - launch(Dispatchers.Default) { - signatory.issue("VerifiableId", ProofConfig(issuerDid = issuerWebDid, proofType = ProofType.JWT)) - }.let { jobs.add(it) } - } - - jobs.forEach { it.join() } - }.let { println("=> Full issuance: $it ms") } - } - - "Issue 3" { - val offers = ArrayList().apply { - repeat(10000) { - add(credOffer) - } - } - - measureTimeMillis { - offers - .map { /*launch(Dispatchers.Default) {*/ - measureTimeMillis { - signatory.issue("VerifiableId", ProofConfig(issuerDid = issuerWebDid, proofType = ProofType.JWT)) - }.let { println("Single issuance: $it ms") } - /*}*/} - //.forEach { it.join() } - }.let { println("=> Full issuance: $it ms") } - } - */ }) From 40e6cee55ecbe7bc98ac0aebbaa6d9a6c76ed79c Mon Sep 17 00:00:00 2001 From: Kevin Burgmann Date: Wed, 15 Mar 2023 23:25:22 +0100 Subject: [PATCH 2/5] Fix some issues arising from context system when using caches --- README.md | 6 +- build.gradle.kts | 18 +-- src/main/kotlin/id/walt/PerformanceRun.kt | 43 +++++ .../id/walt/credentials/w3c/JsonConverter.kt | 29 ++-- .../credentials/w3c/VerifiableCredential.kt | 5 +- .../credentials/w3c/W3CCredentialSubject.kt | 1 + .../w3c/templates/VcTemplateManager.kt | 41 +++-- .../kotlin/id/walt/services/did/DidService.kt | 22 +-- .../kotlin/id/walt/signatory/Signatory.kt | 10 +- src/test/kotlin/id/walt/Performance.kt | 4 +- .../kotlin/id/walt/services/ReadmeTest.kt | 2 +- .../id/walt/services/oidc/OIDC4VCTest.kt | 4 +- .../id/walt/signatory/SignatoryApiTest.kt | 12 +- .../id/walt/signatory/SignatoryServiceTest.kt | 29 ++-- .../vc-templates/AmletCredential.json | 44 +++++ .../vc-templates/DataConsortium.json | 18 +++ .../vc-templates/DataSelfDescription.json | 19 +++ .../vc-templates/DataServiceOffering.json | 37 +++++ .../EbsiAccreditedVerifiableAttestation.json | 31 ++++ .../EbsiDiplomaVerifiableAccreditation.json | 51 ++++++ .../resources/vc-templates/EbsiEuropass.json | 0 ...EbsiVerifiableAccreditationToAccredit.json | 40 +++++ .../EbsiVerifiableAttestationGeneric.json | 26 +++ .../EbsiVerifiableAttestationLegal.json | 49 ++++++ .../EbsiVerifiableAttestationPerson.json | 26 +++ src/test/resources/vc-templates/Email.json | 11 ++ src/test/resources/vc-templates/Europass.json | 153 ++++++++++++++++++ .../vc-templates/EuropeanBankIdentity.json | 23 +++ .../vc-templates/GaiaxCredential.json | 40 +++++ .../vc-templates/Iso27001Certificate.json | 30 ++++ .../resources/vc-templates/KybCredential.json | 33 ++++ .../vc-templates/KybMonoCredential.json | 11 ++ .../resources/vc-templates/KycCredential.json | 20 +++ .../resources/vc-templates/LegalPerson.json | 29 ++++ .../vc-templates/OpenBadgeCredential.json | 40 +++++ .../vc-templates/ParticipantCredential.json | 28 ++++ .../resources/vc-templates/PeerReview.json | 32 ++++ .../vc-templates/PermanentResidentCard.json | 11 ++ .../vc-templates/ProofOfResidence.json | 43 +++++ .../ServiceOfferingCredential.json | 27 ++++ .../vc-templates/UniversityDegree.json | 16 ++ .../vc-templates/VerifiableAttestation.json | 29 ++++ .../vc-templates/VerifiableAuthorization.json | 13 ++ .../vc-templates/VerifiableDiploma.json | 64 ++++++++ .../resources/vc-templates/VerifiableId.json | 31 ++++ .../vc-templates/VerifiableMandate.json | 28 ++++ .../vc-templates/VerifiablePresentation.json | 43 +++++ .../VerifiableVaccinationCertificate.json | 1 + 48 files changed, 1239 insertions(+), 84 deletions(-) create mode 100644 src/main/kotlin/id/walt/PerformanceRun.kt create mode 100644 src/test/resources/vc-templates/AmletCredential.json create mode 100644 src/test/resources/vc-templates/DataConsortium.json create mode 100644 src/test/resources/vc-templates/DataSelfDescription.json create mode 100644 src/test/resources/vc-templates/DataServiceOffering.json create mode 100644 src/test/resources/vc-templates/EbsiAccreditedVerifiableAttestation.json create mode 100644 src/test/resources/vc-templates/EbsiDiplomaVerifiableAccreditation.json create mode 100644 src/test/resources/vc-templates/EbsiEuropass.json create mode 100644 src/test/resources/vc-templates/EbsiVerifiableAccreditationToAccredit.json create mode 100644 src/test/resources/vc-templates/EbsiVerifiableAttestationGeneric.json create mode 100644 src/test/resources/vc-templates/EbsiVerifiableAttestationLegal.json create mode 100644 src/test/resources/vc-templates/EbsiVerifiableAttestationPerson.json create mode 100644 src/test/resources/vc-templates/Email.json create mode 100644 src/test/resources/vc-templates/Europass.json create mode 100644 src/test/resources/vc-templates/EuropeanBankIdentity.json create mode 100644 src/test/resources/vc-templates/GaiaxCredential.json create mode 100644 src/test/resources/vc-templates/Iso27001Certificate.json create mode 100644 src/test/resources/vc-templates/KybCredential.json create mode 100644 src/test/resources/vc-templates/KybMonoCredential.json create mode 100644 src/test/resources/vc-templates/KycCredential.json create mode 100644 src/test/resources/vc-templates/LegalPerson.json create mode 100644 src/test/resources/vc-templates/OpenBadgeCredential.json create mode 100644 src/test/resources/vc-templates/ParticipantCredential.json create mode 100644 src/test/resources/vc-templates/PeerReview.json create mode 100644 src/test/resources/vc-templates/PermanentResidentCard.json create mode 100644 src/test/resources/vc-templates/ProofOfResidence.json create mode 100644 src/test/resources/vc-templates/ServiceOfferingCredential.json create mode 100644 src/test/resources/vc-templates/UniversityDegree.json create mode 100644 src/test/resources/vc-templates/VerifiableAttestation.json create mode 100644 src/test/resources/vc-templates/VerifiableAuthorization.json create mode 100644 src/test/resources/vc-templates/VerifiableDiploma.json create mode 100644 src/test/resources/vc-templates/VerifiableId.json create mode 100644 src/test/resources/vc-templates/VerifiableMandate.json create mode 100644 src/test/resources/vc-templates/VerifiablePresentation.json create mode 100644 src/test/resources/vc-templates/VerifiableVaccinationCertificate.json diff --git a/README.md b/README.md index 7eeb99d3..4f2bff7f 100644 --- a/README.md +++ b/README.md @@ -63,13 +63,13 @@ fun main() { val issuerDid = DidService.create(DidMethod.ebsi) val holderDid = DidService.create(DidMethod.key) - // Issue VC in JSON-LD and JWT format (for show-casing both formats) + // Issue VC with LD_PROOF and JWT format (for show-casing both formats) val vcJson = Signatory.getService().issue( - templateId = "VerifiableId", + templateIdOrFilename = "VerifiableId", config = ProofConfig(issuerDid = issuerDid, subjectDid = holderDid, proofType = ProofType.LD_PROOF) ) val vcJwt = Signatory.getService().issue( - templateId = "Europass", + templateIdOrFilename = "Europass", config = ProofConfig(issuerDid = issuerDid, subjectDid = holderDid, proofType = ProofType.JWT) ) diff --git a/build.gradle.kts b/build.gradle.kts index 09430b4a..a3b619b7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,8 +35,8 @@ dependencies { implementation("com.github.multiformats:java-multibase:v1.1.0") implementation("com.microsoft.azure:azure-keyvault:1.2.6") implementation("com.microsoft.azure:azure-client-authentication:1.7.14") - implementation("com.nimbusds:nimbus-jose-jwt:9.30.1") - implementation("com.nimbusds:oauth2-oidc-sdk:10.5.1") + implementation("com.nimbusds:nimbus-jose-jwt:9.30.2") + implementation("com.nimbusds:oauth2-oidc-sdk:10.7") implementation("org.bouncycastle:bcprov-jdk15to18:1.72") implementation("org.bouncycastle:bcpkix-jdk15to18:1.72") @@ -52,23 +52,23 @@ dependencies { implementation("com.beust:klaxon:5.6") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.0") implementation("com.jayway.jsonpath:json-path:2.7.0") - implementation("com.networknt:json-schema-validator:1.0.77") + implementation("com.networknt:json-schema-validator:1.0.78") implementation("net.pwall.json:json-kotlin-schema:0.39") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1") // DB - implementation("org.xerial:sqlite-jdbc:3.39.4.1") + implementation("org.xerial:sqlite-jdbc:3.40.1.0") implementation("com.zaxxer:HikariCP:5.0.1") // CLI - implementation("com.github.ajalt.clikt:clikt-jvm:3.5.0") + implementation("com.github.ajalt.clikt:clikt-jvm:3.5.2") implementation("com.github.ajalt.clikt:clikt:3.5.0") // Misc implementation("commons-io:commons-io:2.11.0") implementation("io.minio:minio:8.4.6") - implementation("io.github.reactivecircus.cache4k:cache4k:0.9.0") + implementation("com.github.ben-manes.caffeine:caffeine:3.1.5") // HTTP implementation("io.ktor:ktor-client-jackson-jvm:2.2.4") @@ -108,9 +108,9 @@ dependencies { //testImplementation(kotlin("test-junit")) testImplementation("io.mockk:mockk:1.13.2") - testImplementation("io.kotest:kotest-runner-junit5:5.5.4") - testImplementation("io.kotest:kotest-assertions-core:5.5.4") - testImplementation("io.kotest:kotest-assertions-json:5.5.4") + testImplementation("io.kotest:kotest-runner-junit5:5.5.5") + testImplementation("io.kotest:kotest-assertions-core:5.5.5") + testImplementation("io.kotest:kotest-assertions-json:5.5.5") } diff --git a/src/main/kotlin/id/walt/PerformanceRun.kt b/src/main/kotlin/id/walt/PerformanceRun.kt new file mode 100644 index 00000000..93f06fd8 --- /dev/null +++ b/src/main/kotlin/id/walt/PerformanceRun.kt @@ -0,0 +1,43 @@ +package id.walt + +import id.walt.credentials.w3c.VerifiableCredential +import id.walt.credentials.w3c.W3CIssuer +import id.walt.model.DidMethod +import id.walt.servicematrix.ServiceMatrix +import id.walt.services.did.DidService +import id.walt.signatory.ProofConfig +import id.walt.signatory.ProofType +import id.walt.signatory.Signatory +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import java.io.File +import kotlin.system.measureTimeMillis + +val RESOURCES_PATH: String = "src/test/resources" + +fun getTemplate(name: String): VerifiableCredential = + VerifiableCredential.fromString(File("$RESOURCES_PATH/verifiable-credentials/vc-template-default.json").readText(Charsets.UTF_8)) + +suspend fun main() { + ServiceMatrix("service-matrix.properties") + val issuerWebDid: String = DidService.create(DidMethod.web) + + //val credentialService: JwtCredentialService = JwtCredentialService.getService() + val signatory = Signatory.getService() + val template = getTemplate("ebsi-attestation") + + template.issuer = W3CIssuer(issuerWebDid) + template.credentialSubject!!.id = issuerWebDid // self signed + + println("" + measureTimeMillis { + val jobs = ArrayList() + repeat(500000) { + jobs.add(GlobalScope.launch { + signatory.issue("VerifiableId", ProofConfig(issuerDid = issuerWebDid, proofType = ProofType.JWT)) + }) + } + + jobs.forEach { it.join() } + } + " ms overall") +} diff --git a/src/main/kotlin/id/walt/credentials/w3c/JsonConverter.kt b/src/main/kotlin/id/walt/credentials/w3c/JsonConverter.kt index 1c64218d..a919b3c0 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/JsonConverter.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/JsonConverter.kt @@ -1,6 +1,7 @@ package id.walt.credentials.w3c import kotlinx.serialization.json.* +import kotlin.reflect.jvm.jvmName object JsonConverter { @@ -9,34 +10,30 @@ object JsonConverter { is Number -> JsonPrimitive(value) is String -> JsonPrimitive(value) is Boolean -> JsonPrimitive(value) - is List<*> -> buildJsonArray { - value.forEach { add(toJsonElement(it)) } - } + null -> JsonNull - is Map<*, *> -> buildJsonObject { - value.keys.forEach { key -> - put(key.toString(), toJsonElement(value[key])) - } - } + is List<*> -> buildJsonArray { value.forEach { add(toJsonElement(it)) } } + is Map<*, *> -> buildJsonObject { value.keys.forEach { put(it.toString(), toJsonElement(value[it])) } } is JsonElement -> value - null -> JsonNull - else -> throw Exception("Json values can only be Number, String, List or Map") + + //else -> JsonNull + else -> throw Exception("Json values can only be Number, String, Boolean, Null, List or Map, not \"${value::class.jvmName}\": toString = $value") } } fun fromJsonElement(element: JsonElement): Any? { return when (element) { - is JsonPrimitive -> if (element.isString) { - element.contentOrNull - } else { - element.booleanOrNull ?: element.longOrNull ?: element.doubleOrNull + is JsonPrimitive -> when { + element.isString -> element.contentOrNull + else -> element.booleanOrNull ?: element.longOrNull ?: element.doubleOrNull } is JsonArray -> element.map { fromJsonElement(it) }.toList() is JsonObject -> element.keys.associateWith { - fromJsonElement(element[it] ?: JsonNull) - } + fromJsonElement(element[it] ?: JsonNull) + } + else -> throw IllegalArgumentException("Invalid JSON element \"${element::class.jvmName}\": $element") } } diff --git a/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt b/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt index e1feb193..129f46ef 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt @@ -74,7 +74,10 @@ open class VerifiableCredential internal constructor( } } - fun toJson() = toJsonObject().toString() + fun toJson(): String { + println("To JSON Object: $type $id") + return toJsonObject().toString() + } fun toJsonElement() = jwt?.let { JsonPrimitive(it) } ?: toJsonObject() override fun toString(): String { diff --git a/src/main/kotlin/id/walt/credentials/w3c/W3CCredentialSubject.kt b/src/main/kotlin/id/walt/credentials/w3c/W3CCredentialSubject.kt index 3aec3d85..23f66e5b 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/W3CCredentialSubject.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/W3CCredentialSubject.kt @@ -9,6 +9,7 @@ open class W3CCredentialSubject(var id: String? = null, override val properties: id?.let { put("id", it) } properties.let { props -> props.keys.forEach { key -> + println("Properties key: $key => ${props[key]}") put(key, JsonConverter.toJsonElement(props[key])) } } diff --git a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt index 615f49f7..db7dc308 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt @@ -1,19 +1,18 @@ package id.walt.credentials.w3c.templates +import com.github.benmanes.caffeine.cache.Caffeine import id.walt.credentials.w3c.VerifiableCredential import id.walt.credentials.w3c.W3CIssuer import id.walt.credentials.w3c.toVerifiableCredential import id.walt.services.context.ContextManager import id.walt.services.hkvstore.HKVKey -import io.github.reactivecircus.cache4k.Cache -import kotlinx.coroutines.runBlocking import mu.KotlinLogging import java.io.File import java.nio.file.FileSystems import java.nio.file.Files +import java.time.Duration import kotlin.io.path.isRegularFile import kotlin.io.path.nameWithoutExtension -import kotlin.time.Duration.Companion.hours object VcTemplateManager { private val log = KotlinLogging.logger {} @@ -28,13 +27,13 @@ object VcTemplateManager { return VcTemplate(name, template, true) } - val templateCache = Cache.Builder() - .maximumCacheSize(1000) - .expireAfterWrite(1.hours) + val templateCache = Caffeine.newBuilder() + .maximumSize(1000) + .expireAfterWrite(Duration.ofMinutes(10)) .build() private fun String?.toVcTemplate(name: String, loadTemplate: Boolean, isMutable: Boolean) = - this?.let { VcTemplate(name, if (loadTemplate) it.toVerifiableCredential() else null, isMutable) } + this?.let { VcTemplate(name, if (loadTemplate && it.isNotBlank()) it.toVerifiableCredential() else null, isMutable) } fun loadTemplateFromHkvStore(name: String, loadTemplate: Boolean) = ContextManager.hkvStore.getAsString(HKVKey(SAVED_VC_TEMPLATES_KEY, name)) @@ -49,19 +48,29 @@ object VcTemplateManager { if (it.exists()) it.readText() else null }.toVcTemplate(name, loadTemplate, false) + fun retrieveOrLoadCachedTemplate( + name: String, + loadTemplate: Boolean = true, + runtimeTemplateFolder: String = "/vc-templates-runtime" + ) = templateCache.get(name) { + loadTemplateFromHkvStore(name, loadTemplate) + ?: loadTemplateFromResources(name, loadTemplate) + ?: loadTemplateFromFile(name, loadTemplate, runtimeTemplateFolder) + ?: throw IllegalArgumentException("No template found with name: $name") + } + fun getTemplate( name: String, loadTemplate: Boolean = true, runtimeTemplateFolder: String = "/vc-templates-runtime" - ): VcTemplate = - runBlocking { - templateCache.get(name) { - loadTemplateFromHkvStore(name, true)//.also { println("At $name HKV: $it") } - ?: loadTemplateFromResources(name, true)//.also { println("At $name resources: $it") } - ?: loadTemplateFromFile(name, true, runtimeTemplateFolder)//.also { println("At $name file: $it") } - ?: throw IllegalArgumentException("No template found with name: $name") - } - } + ): VcTemplate { + val cachedTemplate = retrieveOrLoadCachedTemplate(name, loadTemplate, runtimeTemplateFolder) + + return if (cachedTemplate.template == null && loadTemplate) { + templateCache.invalidate(name) + retrieveOrLoadCachedTemplate(name, true, runtimeTemplateFolder) + } else cachedTemplate + } private val resourceWalk = lazy { diff --git a/src/main/kotlin/id/walt/services/did/DidService.kt b/src/main/kotlin/id/walt/services/did/DidService.kt index 9ff51af6..c27a6b6d 100644 --- a/src/main/kotlin/id/walt/services/did/DidService.kt +++ b/src/main/kotlin/id/walt/services/did/DidService.kt @@ -1,6 +1,7 @@ package id.walt.services.did import com.beust.klaxon.Klaxon +import com.github.benmanes.caffeine.cache.Caffeine import com.nimbusds.jose.jwk.Curve import com.nimbusds.jose.jwk.JWK import com.nimbusds.jose.util.Base64URL @@ -21,7 +22,6 @@ import id.walt.services.key.KeyService import id.walt.services.keystore.KeyType import id.walt.services.vc.JsonLdCredentialService import id.walt.signatory.ProofConfig -import io.github.reactivecircus.cache4k.Cache import io.ipfs.multibase.Multibase import io.ktor.client.plugins.* import io.ktor.client.request.* @@ -40,8 +40,8 @@ import java.nio.charset.StandardCharsets import java.security.KeyFactory import java.security.KeyPair import java.security.spec.X509EncodedKeySpec +import java.time.Duration import java.util.* -import kotlin.time.Duration.Companion.minutes private val log = KotlinLogging.logger {} @@ -111,18 +111,17 @@ object DidService { } } - val didCache = Cache.Builder() - .maximumCacheSize(1000) - .expireAfterWrite(15.minutes) + private val didCache = Caffeine.newBuilder() + .maximumSize(1000) + .expireAfterWrite(Duration.ofMinutes(10)) .build() - - fun load(did: String): Did = runBlocking { load(DidUrl.from(did)) } - suspend fun load(didUrl: DidUrl): Did = + fun load(did: String): Did = load(DidUrl.from(did)) + fun load(didUrl: DidUrl): Did = didCache.get(didUrl) { Did.decode( - loadDid(didUrl.did) ?: throw IllegalArgumentException("DID $didUrl not found.") - ) ?: throw IllegalArgumentException("DID $didUrl not found.") + loadDid(didUrl.did) ?: throw IllegalArgumentException("DID $didUrl could not be loaded/found.") + ) ?: throw IllegalArgumentException("DID $didUrl could not be decoded.") } fun resolveDidEbsiRaw(did: String): String = runBlocking { @@ -196,7 +195,7 @@ object DidService { } fun loadDidEbsi(did: String): DidEbsi = loadDidEbsi(DidUrl.from(did)) - fun loadDidEbsi(didUrl: DidUrl): DidEbsi = Did.decode(loadDid(didUrl.did)!!)!! as DidEbsi + fun loadDidEbsi(didUrl: DidUrl): DidEbsi = load(didUrl.did) as DidEbsi fun updateDidEbsi(did: DidEbsi) = storeDid(did.id, did.encode()) @@ -652,6 +651,7 @@ object DidService { fun deleteDid(didUrl: String) { loadOrResolveAnyDid(didUrl)?.let { did -> + didCache.invalidate(DidUrl.from(didUrl)) ContextManager.hkvStore.delete(HKVKey("did", "created", didUrl), recursive = true) did.verificationMethod?.forEach { ContextManager.keyStore.delete(it.id) diff --git a/src/main/kotlin/id/walt/signatory/Signatory.kt b/src/main/kotlin/id/walt/signatory/Signatory.kt index 28c729f0..d3583703 100644 --- a/src/main/kotlin/id/walt/signatory/Signatory.kt +++ b/src/main/kotlin/id/walt/signatory/Signatory.kt @@ -150,8 +150,8 @@ class WaltIdSignatory(configurationPath: String) : Signatory() { val credentialBuilder = when (Files.exists(Path.of(templateIdOrFilename))) { true -> Files.readString(Path.of(templateIdOrFilename)).toVerifiableCredential() else -> VcTemplateManager.getTemplate(templateIdOrFilename, true, configuration.templatesFolder).template - }?.let { /*println("Issuing with $it");*/ W3CCredentialBuilder.fromPartial(it) } - ?: throw Exception("Template could not be loaded") + }?.let { W3CCredentialBuilder.fromPartial(it) } + ?: throw Exception("Template could not be loaded: $templateIdOrFilename") return issue(dataProvider?.populate(credentialBuilder, config) ?: credentialBuilder, config, issuer, storeCredential) } @@ -190,12 +190,14 @@ class WaltIdSignatory(configurationPath: String) : Signatory() { return signedVc } - override fun hasTemplateId(templateId: String) = runCatching { VcTemplateManager.getTemplate(templateId, true) }.getOrNull() != null + override fun hasTemplateId(templateId: String) = + runCatching { VcTemplateManager.getTemplate(templateId, false) }.getOrNull() != null override fun listTemplates(): List = VcTemplateManager.listTemplates(configuration.templatesFolder) override fun listTemplateIds() = VcTemplateManager.listTemplates(configuration.templatesFolder).map { it.name } override fun loadTemplate(templateId: String): VerifiableCredential = - VcTemplateManager.getTemplate(templateId, true, configuration.templatesFolder).template!! + VcTemplateManager.getTemplate(templateId, true, configuration.templatesFolder).template + ?: throw IllegalArgumentException("Could not load template \"$templateId\" into WaltSignatory") override fun importTemplate(templateId: String, template: String) { val vc = VerifiableCredential.fromJson(template) diff --git a/src/test/kotlin/id/walt/Performance.kt b/src/test/kotlin/id/walt/Performance.kt index 70021b1d..8f315bce 100644 --- a/src/test/kotlin/id/walt/Performance.kt +++ b/src/test/kotlin/id/walt/Performance.kt @@ -1,5 +1,5 @@ package id.walt - +/* import id.walt.credentials.w3c.W3CIssuer import id.walt.model.DidMethod import id.walt.servicematrix.ServiceMatrix @@ -16,6 +16,7 @@ import kotlinx.coroutines.launch import kotlin.system.measureTimeMillis + class Performance : StringSpec({ ServiceMatrix("service-matrix.properties") val issuerWebDid: String = DidService.create(DidMethod.web) @@ -41,3 +42,4 @@ class Performance : StringSpec({ } }) +*/ diff --git a/src/test/kotlin/id/walt/services/ReadmeTest.kt b/src/test/kotlin/id/walt/services/ReadmeTest.kt index 20c12831..f626aed9 100644 --- a/src/test/kotlin/id/walt/services/ReadmeTest.kt +++ b/src/test/kotlin/id/walt/services/ReadmeTest.kt @@ -28,7 +28,7 @@ class ReadmeTest : StringSpec({ val issuerDid = DidService.create(DidMethod.ebsi) val holderDid = DidService.create(DidMethod.key) - // Issue VC in JSON-LD and JWT format (for show-casing both formats) + // Issue VC with LD_PROOF and JWT format (for show-casing both formats) val vcJson = Signatory.getService().issue( templateIdOrFilename = "VerifiableId", config = ProofConfig(issuerDid = issuerDid, subjectDid = holderDid, proofType = ProofType.LD_PROOF) diff --git a/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt b/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt index 40ffb784..48785ee2 100644 --- a/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt +++ b/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt @@ -183,7 +183,7 @@ class OIDC4VCTest : AnnotationSpec() { @Test fun testVerifyPresentation() { - val credential = Signatory.getService().issue("VerifiableId", ProofConfig(OIDCTestProvider.ISSUER_DID, SUBJECT_DID)) + val credential = Signatory.getService().issue("VerifiableId", ProofConfig(OIDCTestProvider.ISSUER_DID, SUBJECT_DID, proofType = ProofType.LD_PROOF)) val presentation = Custodian.getService().createPresentation(listOf(credential), SUBJECT_DID) .toVerifiablePresentation() val req = OIDC4VPService.createOIDC4VPRequest( @@ -230,7 +230,7 @@ class OIDC4VCTest : AnnotationSpec() { @Test fun testVerifyMultiplePresentations() { - val credential = Signatory.getService().issue("VerifiableId", ProofConfig(OIDCTestProvider.ISSUER_DID, SUBJECT_DID)) + val credential = Signatory.getService().issue("VerifiableId", ProofConfig(OIDCTestProvider.ISSUER_DID, SUBJECT_DID, proofType = ProofType.LD_PROOF)) val presentation = Custodian.getService().createPresentation(listOf(credential), SUBJECT_DID) .toVerifiablePresentation() val req = OIDC4VPService.createOIDC4VPRequest( diff --git a/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt b/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt index 343447ae..615c7344 100644 --- a/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt +++ b/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt @@ -64,13 +64,17 @@ class SignatoryApiTest : AnnotationSpec() { fun testListVcTemplates() = runBlocking { val templates = client.get("$SIGNATORY_API_URL/v1/templates").bodyAsText() + .also { println("BODY: $it") } .let { KlaxonWithConverters().parseArray(it) }!! - .map { it.name } - VcTemplateManager.listTemplates().map { it.name }.forEach { templateName -> templates shouldContain templateName } + val templateNames = templates.map { it.name } - templates shouldContain "Europass" - templates shouldContain "VerifiablePresentation" + templateNames shouldContain "Europass" + templateNames shouldContain "VerifiablePresentation" + + VcTemplateManager.listTemplates().map { it.name }.forEach { templateName -> templateNames shouldContain templateName } + + println(templates) } diff --git a/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt b/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt index 26352a81..01a33e07 100644 --- a/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt +++ b/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt @@ -39,7 +39,8 @@ class SignatoryServiceTest : StringSpec({ subjectDid = did, issuerDid = did, issueDate = LocalDateTime.of(2020, 11, 3, 0, 0).toInstant(ZoneOffset.UTC), - issuerVerificationMethod = vm + issuerVerificationMethod = vm, + proofType = ProofType.LD_PROOF ) ) @@ -59,9 +60,7 @@ class SignatoryServiceTest : StringSpec({ println("ISSUING CREDENTIAL...") val jwtStr = signatory.issue( "VerifiableId", ProofConfig( - subjectDid = did, - issuerDid = did, - proofType = ProofType.JWT + subjectDid = did, issuerDid = did, proofType = ProofType.JWT ) ) @@ -89,7 +88,8 @@ class SignatoryServiceTest : StringSpec({ subjectDid = did, issuerDid = did, issueDate = LocalDateTime.of(2020, 11, 3, 0, 0).toInstant(ZoneOffset.UTC), - issuerVerificationMethod = vm + issuerVerificationMethod = vm, + proofType = ProofType.LD_PROOF ) ) @@ -108,7 +108,8 @@ class SignatoryServiceTest : StringSpec({ "Issue and verify: VerifiableDiploma (JWT-Proof)" { println("ISSUING CREDENTIAL...") val jwtStr = signatory.issue( - "VerifiableDiploma", ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.JWT) + "VerifiableDiploma", + ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.JWT) ) println("VC:") @@ -145,8 +146,7 @@ class SignatoryServiceTest : StringSpec({ val builder = W3CCredentialBuilder() val data = mapOf(Pair("credentialSubject", mapOf(Pair("firstName", "Yves")))) val populated = MergingDataProvider(data).populate( - builder, - ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.LD_PROOF) + builder, ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.LD_PROOF) ).build() populated.credentialSubject?.properties?.get("firstName") shouldBe "Yves" @@ -168,9 +168,7 @@ class SignatoryServiceTest : StringSpec({ """.trimIndent() val signedVC = Signatory.getService().issue( - W3CCredentialBuilder - .fromPartial(template) - .setFromJson(userData), + W3CCredentialBuilder.fromPartial(template).setFromJson(userData), ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.LD_PROOF) ) @@ -189,11 +187,10 @@ class SignatoryServiceTest : StringSpec({ "sign any credential with user data from subject builder" { val signedVC = Signatory.getService().issue( - W3CCredentialBuilder() - .buildSubject { - setProperty("firstName", "Inco") - setProperty("familyName", "GNITO") - }, + W3CCredentialBuilder().buildSubject { + setProperty("firstName", "Inco") + setProperty("familyName", "GNITO") + }, ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.LD_PROOF), W3CIssuer(did, mapOf("name" to "Test Issuer")) ) diff --git a/src/test/resources/vc-templates/AmletCredential.json b/src/test/resources/vc-templates/AmletCredential.json new file mode 100644 index 00000000..c7488d35 --- /dev/null +++ b/src/test/resources/vc-templates/AmletCredential.json @@ -0,0 +1,44 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/AmletCredential.json", + "type": "JsonSchemaValidator2018" + }, + "credentialSubject": { + "birthDate": "1995-05-03", + "birthPlace": "Milan", + "company": { + "address": "VLE. BACCHIGLIONE 20, MILANO", + "email": "info@mopso.it", + "foundingDate": "2021-01-01", + "hasInternationalActivity": true, + "isRecipientOfPublicFunds": true, + "isRiskSector": false, + "name": "Mopso", + "revenue": "10000", + "ultimateBeneficialOwners": [{ + "address": "Milan, Italy", + "birthDate": "1995-05-03", + "birthPlace": "Milan", + "citizenship": "Italian", + "familyName": "Doe", + "givenName": "Jane", + "isPep": false, + "taxID": "DOEJNA95M43Z330V" + }], + "vatId": "11717750969" + }, + "execRappr": "LEGAL REPRESENTATIVE", + "familyName": "Doe", + "givenName": "Jane", + "id": "did:ebsi:zukSNRQ7Umb2TzbRLNkTGX4#3bf09a67ac1348f4bfe35b7c7fc54fba", + "taxID": "DOEJNA95M43Z330V" + }, + "expirationDate": "2023-03-21T00:00:00Z", + "id": "", + "issued": "2022-03-21T00:00:00Z", + "issuer": "did:ebsi:zkttgPyjddU8JqrkQ9aV5MW#6462224517f04e9d84ba1ea09c82e01c", + "validFrom": "2022-03-21T00:00:00Z", + "issuanceDate": "2022-03-21T00:00:00Z", + "type": ["VerifiableCredential", "AmletCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/DataConsortium.json b/src/test/resources/vc-templates/DataConsortium.json new file mode 100644 index 00000000..1d20af23 --- /dev/null +++ b/src/test/resources/vc-templates/DataConsortium.json @@ -0,0 +1,18 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "id": "1234" + }, + "dataspace": [{ + "id": "Gaia-X AISBL", + "serviceEndpoint": "https://endpoint1.io" + }, { + "id": "Gaia-X XYZ", + "serviceEndpoint": "https://endpoint2.io" + }], + "expirationDate": "2022-01-06T20:38:38Z", + "id": "1234", + "issued": "2022-01-03T20:38:38Z", + "issuer": "did:web:vc.gaia-x.eu:issuer", + "type": ["VerifiableCredential", "DataConsortiumCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/DataSelfDescription.json b/src/test/resources/vc-templates/DataSelfDescription.json new file mode 100644 index 00000000..356056d2 --- /dev/null +++ b/src/test/resources/vc-templates/DataSelfDescription.json @@ -0,0 +1,19 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "dependsOn": ["Pilot004DataService"], + "description": "AIS demonstrates machine learning application use case.", + "hasCertifications": ["ISO_27001", "GDPR_Compliance"], + "hasMarketingImage": "https://www.data-infrastructure.eu/GAIAX/Redaktion/EN/Bilder/UseCases/ai-marketplace-for-product-development.jpg?__blob=normal", + "hasName": "AIS", + "hasVersion": "0.1.0", + "id": "Pilot004AIService", + "providedBy": "GAIA-X", + "type": "Service", + "utilizes": ["ExampleKubernetesService"] + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "type": ["VerifiableCredential", "DataSelfDescription"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/DataServiceOffering.json b/src/test/resources/vc-templates/DataServiceOffering.json new file mode 100644 index 00000000..fc60de79 --- /dev/null +++ b/src/test/resources/vc-templates/DataServiceOffering.json @@ -0,0 +1,37 @@ +{ + "billing": { + "paymentModel": { + "frequenceOfPayment": "daily", + "unit": "EUR", + "value": "0.0" + }, + "price": "0.0", + "unit": "EUR" + }, + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "dependsOn": ["Pilot004DataService"], + "description": "AIS demonstrates machine learning application use case.", + "hasCertifications": ["ISO_27001", "GDPR_Compliance"], + "hasMarketingImage": "https://www.data-infrastructure.eu/Data/Redaktion/EN/Bilder/UseCases/ai-marketplace-for-product-development.jpg?__blob=normal", + "hasName": "AIS", + "hasVersion": "0.1.0", + "id": "Pilot004AIService", + "providedBy": "GAIA-X", + "type": "Service", + "utilizes": ["ExampleKubernetesService"] + }, + "description": "he data contains the basic information to build a transaction graph for later analysis by the safeFBDC algorithm. Is is one of multiple data sources to be analyzed.", + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "keywords": ["Analytics", "AML", "safeFBDC", "finance", "dataset", "graph"], + "modified": "2020-12-02T23:00:00+01:00", + "providedBy": "did:key:z6Mktqg13w3KRBdcqhyjU99ZhHF8FR6tRJACuQzmVcXNiH25", + "provisionType": "E.g., Hybrid", + "serviceModel": "E.g., DaaS", + "serviceTitle": "safeFBDC - Dataset A for safeFBDC AML analysis", + "version": "0.1", + "webAddress": "https://safefbdc.portal.minimal-gaia-x.eu/asset/did:op:348e1a938dB25221031Ed6b465782efCAcBB214E", + "type": ["VerifiableCredential", "DataServiceOffering"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/EbsiAccreditedVerifiableAttestation.json b/src/test/resources/vc-templates/EbsiAccreditedVerifiableAttestation.json new file mode 100644 index 00000000..1ae4443d --- /dev/null +++ b/src/test/resources/vc-templates/EbsiAccreditedVerifiableAttestation.json @@ -0,0 +1,31 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "urn:uuid:58afcb57-81f9-4e8c-817b-01f0597d6801", + "type": [ + "VerifiableCredential", + "VerifiableAttestation", + "AccreditedVerifiableAttestation" + ], + "issuer": "did:ebsi:00001234", + "issuanceDate": "2021-11-01T00:00:00Z", + "validFrom": "2021-11-01T00:00:00Z", + "expirationDate": "2024-06-22T14:11:44Z", + "issued": "2020-06-22T14:11:44Z", + "termsOfUse": [ + { + "id": "https://essif.europa.eu/accreditation/45", + "type": "VerifiableAccreditation" + } + ], + "credentialSubject": { + "id": "did:ebsi:00001235" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/45", + "type": "CredentialsStatusList2020" + }, + "credentialSchema": { + "id": "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/0x4a131e78474b35ca4c93d4904cae9f2013cd53794ce521f6fcfac17ac460c6e5", + "type": "FullJsonSchemaValidator2021" + } +} diff --git a/src/test/resources/vc-templates/EbsiDiplomaVerifiableAccreditation.json b/src/test/resources/vc-templates/EbsiDiplomaVerifiableAccreditation.json new file mode 100644 index 00000000..beb8e83b --- /dev/null +++ b/src/test/resources/vc-templates/EbsiDiplomaVerifiableAccreditation.json @@ -0,0 +1,51 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://essif.europa.eu/schemas/vc/2020/v1" + ], + "id": "https://essif.europa.eu/tsr/53", + "type": [ + "VerifiableCredential", + "VerifiableAttestation", + "VerifiableAccreditation", + "DiplomaVerifiableAccreditation" + ], + "issuer": "did:ebsi:00001234", + "issuanceDate": "2020-06-22T14:11:44Z", + "issued": "2020-06-22T14:11:44Z", + "credentialSubject": { + "id": "did:ebsi:00001235", + "authorizationClaims": { + "accreditationType": "http://data.europa.eu/snb/accreditation/003293d2ce", + "decision": "fully compliant", + "report": ["https://www.example.com/URLtoreport.pdf"], + "limitQFLevel": [ + "http://data.europa.eu/snb/eqf/6", + "http://data.europa.eu/snb/eqf/7" + ], + "limitField": ["http://data.europa.eu/snb/isced-f/0419"], + "limitJurisdiction": [ + "http://publications.europa.eu/resource/authority/atu/NLD" + ], + "reviewDate": "2024-04-22T14:11:44Z" + } + }, + "expirationDate": "2024-06-22T14:11:44Z", + "validFrom": "2021-11-01T00:00:00Z", + "credentialStatus": { + "id": "https://essif.europa.eu/status/45", + "type": "CredentialsStatusList2020" + }, + "credentialSchema": { + "id": "https://essif.europa.eu/tsr-123/verifiableattestation.json", + "type": "FullJsonSchemaValidator2021" + }, + "proof": { + "type": "EidasSeal2019", + "created": "2019-06-22T14:11:44Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "http://uri-to-verification-method", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..TCYt5X", + "proofValue": "BD21J4fdlnBvBA+y6D...fnC8Y=" + } +} diff --git a/src/test/resources/vc-templates/EbsiEuropass.json b/src/test/resources/vc-templates/EbsiEuropass.json new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/vc-templates/EbsiVerifiableAccreditationToAccredit.json b/src/test/resources/vc-templates/EbsiVerifiableAccreditationToAccredit.json new file mode 100644 index 00000000..8b9c7450 --- /dev/null +++ b/src/test/resources/vc-templates/EbsiVerifiableAccreditationToAccredit.json @@ -0,0 +1,40 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "urn:uuid:8568b525-a24e-4bc0-9d97-6a8459ec0130", + "type": [ + "VerifiableCredential", + "VerifiableAttestation", + "VerifiableAccreditation", + "VerifiableAccreditationToAccredit" + ], + "issuer": "did:ebsi:00001234", + "issuanceDate": "2021-11-01T00:00:00Z", + "validFrom": "2021-11-01T00:00:00Z", + "expirationDate": "2024-06-22T14:11:44Z", + "issued": "2020-06-22T14:11:44Z", + "credentialSubject": { + "id": "did:ebsi:00001235", + "accreditedFor": [ + { + "schemaId": "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/0x010110", + "types": [ + "VerifiableCredential", + "VerifiableAttestation", + "DiplomaCredential" + ], + "limitJurisdiction": "https://publications.europa.eu/resource/authority/atu/FIN" + } + ] + }, + "termsOfUse": [ + { + "id": "https://api-test.ebsi.eu/trusted-issuers-registry/../..xyz", + "type": "IssuanceCertificate" + } + ], + "credentialSchema": + { + "id": "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/0x1d7146f8897aa6cd5c59321ea0756ec61277028e4a8b3c13ec1b310ec47e6495", + "type": "FullJsonSchemaValidator2021" + } +} diff --git a/src/test/resources/vc-templates/EbsiVerifiableAttestationGeneric.json b/src/test/resources/vc-templates/EbsiVerifiableAttestationGeneric.json new file mode 100644 index 00000000..dc37148b --- /dev/null +++ b/src/test/resources/vc-templates/EbsiVerifiableAttestationGeneric.json @@ -0,0 +1,26 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "urn:uuid:003a1dd8-a5d2-42ef-8182-e921c0a9f2cd", + "type": ["VerifiableCredential", "VerifiableAttestation"], + "issuer": "did:ebsi:00001234", + "issuanceDate": "2021-11-01T00:00:00Z", + "validFrom": "2021-11-01T00:00:00Z", + "validUntil": "2050-11-01T00:00:00Z", + "expirationDate": "2024-06-22T14:11:44Z", + "issued": "2020-06-22T14:11:44Z", + "credentialSubject": { + "id": "did:ebsi:00001235" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/45", + "type": "AnyExtension" + }, + "credentialSchema": { + "id": "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/0x23039e6356ea6b703ce672e7cfac0b42765b150f63df78e2bd18ae785787f6a2", + "type": "FullJsonSchemaValidator2021" + }, + "termsOfUse": { + "id": "https://essif.europa.eu/accreditation/45", + "type": "AnyExtension" + } +} diff --git a/src/test/resources/vc-templates/EbsiVerifiableAttestationLegal.json b/src/test/resources/vc-templates/EbsiVerifiableAttestationLegal.json new file mode 100644 index 00000000..cd6137ba --- /dev/null +++ b/src/test/resources/vc-templates/EbsiVerifiableAttestationLegal.json @@ -0,0 +1,49 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https:/.europa.eu/schemas/v-id/2020/v1", + "https:/.europa.eu/schemas/eidas/2020/v1" + ], + "id": "urn:did:123456", + "type": ["VerifiableCredential", "VerifiableAttestation"], + "issuer": "did:ebsi:2757945549477fc571663bee12042873fe555b674bd294a3", + "issuanceDate": "2019-06-22T14:11:44Z", + "validFrom": "2019-06-22T14:11:44Z", + "issued": "2019-06-22T14:11:44Z", + "credentialSubject": { + "id": "did:ebsi:2659b154a445434a39d91149ead3bd993cb99fd5e78281b7", + "identifier": [ + { + "schemeID": "SHACL", + "value": "SHACL ID 1", + "id": "http://student.id/41231232" + } + ], + "legalName": "Example Company" + }, + "credentialStatus": { + "id": "https:/europa.eu/status/identity#1dee355d-0432-4910-ac9c-70d89e8d674e", + "type": "CredentialStatusList2020" + }, + "credentialSchema": { + "id": "https:/.europa.eu/tsr-vid/verifiableid1.json", + "type": "FullJsonSchemaValidator2021" + }, + "evidence": [ + { + "id": "https:/.europa.eu/tsr-vid/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d4231", + "type": ["DocumentVerification"], + "verifier": "did:ebsi:2e81454f76775c687694ee6772a17796436768a30e289555", + "evidenceDocument": ["Passport"], + "subjectPresence": "Physical", + "documentPresence": ["Physical"] + } + ], + "proof": { + "type": "EidasSeal2021", + "created": "2019-06-22T14:11:44Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:ebsi:2757945549477fc571663bee12042873fe555b674bd294a3#2368332668", + "jws": "HG21J4fdlnBvBA+y6D...amP7O=" + } +} diff --git a/src/test/resources/vc-templates/EbsiVerifiableAttestationPerson.json b/src/test/resources/vc-templates/EbsiVerifiableAttestationPerson.json new file mode 100644 index 00000000..dc37148b --- /dev/null +++ b/src/test/resources/vc-templates/EbsiVerifiableAttestationPerson.json @@ -0,0 +1,26 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "urn:uuid:003a1dd8-a5d2-42ef-8182-e921c0a9f2cd", + "type": ["VerifiableCredential", "VerifiableAttestation"], + "issuer": "did:ebsi:00001234", + "issuanceDate": "2021-11-01T00:00:00Z", + "validFrom": "2021-11-01T00:00:00Z", + "validUntil": "2050-11-01T00:00:00Z", + "expirationDate": "2024-06-22T14:11:44Z", + "issued": "2020-06-22T14:11:44Z", + "credentialSubject": { + "id": "did:ebsi:00001235" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/45", + "type": "AnyExtension" + }, + "credentialSchema": { + "id": "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/0x23039e6356ea6b703ce672e7cfac0b42765b150f63df78e2bd18ae785787f6a2", + "type": "FullJsonSchemaValidator2021" + }, + "termsOfUse": { + "id": "https://essif.europa.eu/accreditation/45", + "type": "AnyExtension" + } +} diff --git a/src/test/resources/vc-templates/Email.json b/src/test/resources/vc-templates/Email.json new file mode 100644 index 00000000..14310c5f --- /dev/null +++ b/src/test/resources/vc-templates/Email.json @@ -0,0 +1,11 @@ +{ + "context": ["https://www.w3.org/2018/credentials/v1"], + "credentialStatus": { + "id": "https://credentialstatus.velocitycareerlabs.io", + "type": "VelocityRevocationRegistry" + }, + "credentialSubject": { + "email": "adam.smith@example.com" + }, + "type": ["VerifiableCredential", "Email"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/Europass.json b/src/test/resources/vc-templates/Europass.json new file mode 100644 index 00000000..5db9f2ed --- /dev/null +++ b/src/test/resources/vc-templates/Europass.json @@ -0,0 +1,153 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/Europass.json", + "type": "JsonSchemaValidator2018" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/education#higherEducation#51e42fda-cb0a-4333-b6a6-35cb147e1a88", + "type": "TrustedCredentialStatus2021" + }, + "credentialSubject": { + "achieved": [{ + "entitlesTo": { + "definition": "Competences the student acquires after the completion of Graduate university study are sufficient conditions for attending the programme of Postgraduate doctoral study at the Faculty of Civil Engineering, Architecture and Geodesy in Split, as well as for attending the same or similar programmes and Postgraduate specialist studies at other faculties of Civil Engineering in Croatia. The acquired learning outcomes enable the student to attend other postgraduate study programmes in the field of technical sciences. ", + "id": "urn:epass:entitlement:1", + "issuedDate": "2019-09-20", + "specifiedBy": { + "entitlementType": "http://data.europa.eu/snb/entitlement/64aad92881", + "id": "urn:epass:entitlementspec:1", + "limitJurisdiction": ["http://publications.europa.eu/resource/authority/country/HRV"], + "limitOrganisation": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "status": "http://data.europa.eu/snb/entitlement-status/5b8d6b34fb", + "title": "Postgraduate doctoral study" + }, + "title": "Postgraduate doctoral study" + }, + "hasPart": { + "learningAchievements": [{ + "id": "", + "identifier": [{ + "schemeID": "Achievement ID", + "value": "GAB701" + }], + "specifiedBy": [{ + "eCTSCreditPoints": 5, + "eqflLevel": "http://data.europa.eu/snb/eqf/4", + "id": "urn:epass:qualification:1", + "isPartialQualification": true, + "maximumDuration": "P6M", + "nqflLevel": "http://data.europa.eu/snb/qdr/c_49672c5a", + "title": "Applied mathematics", + "volumeOfLearning": "PT60H" + }], + "title": "", + "wasAwardedBy": { + "awardingBody": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "awardingDate": "2019-09-20T00:00:00+02:00", + "id": "urn:epass:awardingProcess:2" + }, + "wasDerivedFrom": [{ + "assessedBy": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "grade": "good (3)", + "id": "urn:epass:asssessmentspec:2", + "specifiedBy": { + "gradingScheme": { + "definition": "The Croatian national grading scheme consists of five grades with numerical equivalents: izvrstan \u2013 5 (outstanding); vrlo dobar \u2013 4 (very good); dobar \u2013 3 (good); dovoljan \u2013 2 (sufficient); nedovoljan \u2013 1 (insufficient - fail). The minimum passing grade is dovoljan \u2013 2.", + "id": "urn:epass:scoringschemespec:2", + "title": "General grading scheme in Croatia" + }, + "id": "urn:epass:asssessmentspec:2", + "title": "Applied mathematics" + }, + "title": "Applied mathematics" + }], + "wasInfluencedBy": [{ + "directedBy": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "endedAtTime": "2018-01-14T00:00:00+01:00", + "id": "urn:epass:activity:1", + "identifier": [{ + "schemeID": "Activity ID", + "value": "GAB701" + }], + "location": ["urn:epass:location:4"], + "specifiedBy": { + "id": "urn:epass:learningactivityspec:1", + "language": ["http://publications.europa.eu/resource/authority/language/HRV"], + "learningActivityType": ["http://data.europa.eu/snb/learning-activity/fd33e234ae"], + "title": "Applied mathematics", + "workload": "PT60H" + }, + "startedAtTime": "2017-09-04T00:00:00+02:00", + "title": "Applied mathematics", + "workload": "PT60H" + }] + }] + }, + "id": "urn:epass:learningAchievement:1", + "specifiedBy": [{ + "eCTSCreditPoints": 120, + "entryRequirementsNote": "The minimum educational requirement for enrolment into graduate university programmes is the completion of an undergraduate university programme. The university can allow students who have completed a professional programme to also enrol graduate university programmes, but they are allowed to set special requirements in these cases.\nThe minimum educational requirement for enrolment into specialist graduate professional programmes is the completion of an undergraduate university programme or a professional programme (first cycle).", + "eqflLevel": "http://data.europa.eu/snb/eqf/5", + "id": "urn:epass:qualification:20", + "isPartialQualification": false, + "maximumDuration": "P21M", + "nqflLevel": "http://data.europa.eu/snb/qdr/c_dcc9aca1", + "title": "Master of Science in Civil Engineering", + "volumeOfLearning": "PT1440H" + }], + "title": "Master of Science in Civil Engineering", + "wasAwardedBy": { + "awardingBody": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "awardingDate": "2019-09-20T00:00:00+02:00", + "id": "urn:epass:awardingProcess:1" + }, + "wasDerivedFrom": [{ + "assessedBy": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "grade": "excellent (5)", + "id": "urn:epass:assessment:1", + "specifiedBy": { + "gradingScheme": { + "definition": "The Croatian national grading scheme consists of five grades with numerical equivalents: izvrstan \u2013 5 (outstanding); vrlo dobar \u2013 4 (very good); dobar \u2013 3 (good); dovoljan \u2013 2 (sufficient); nedovoljan \u2013 1 (insufficient - fail). The minimum passing grade is dovoljan \u2013 2.", + "id": "urn:epass:scoringschemespec:1", + "title": "General grading scheme in Croatia" + }, + "id": "urn:epass:asssessmentspec:1", + "title": "Overall Diploma Assessment" + }, + "title": "Overall Diploma Assessment" + }], + "wasInfluencedBy": [{ + "directedBy": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "endedAtTime": "2018-01-14T00:00:00+01:00", + "id": "urn:epass:learningAchievement:1", + "identifier": [{ + "schemeID": "Activity ID", + "value": "GAB701" + }], + "location": ["urn:epass:location:4"], + "specifiedBy": { + "id": "urn:epass:learningactivityspec:1", + "language": ["http://publications.europa.eu/resource/authority/language/HRV"], + "learningActivityType": ["http://data.europa.eu/snb/learning-activity/fd33e234ae"], + "title": "Applied mathematics", + "workload": "PT60H" + }, + "startedAtTime": "2017-09-04T00:00:00+02:00", + "title": "Master of Science in Civil Engineering", + "workload": "PT60H" + }] + }], + "id": "did:epass:person:1", + "identifier": { + "schemeID": "Student identification number", + "value": "99009900" + } + }, + "id": "urn:credential:5a4d5412-27e3-4540-a5e5-f1aa4d55b20c", + "issued": "2020-07-20T13:58:53+02:00", + "issuer": "did:epass:org:1", + "validFrom": "2019-09-20T00:00:00+02:00", + "issuanceDate": "2019-09-20T00:00:00+02:00", + "type": ["VerifiableCredential", "VerifiableAttestation", "Europass"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/EuropeanBankIdentity.json b/src/test/resources/vc-templates/EuropeanBankIdentity.json new file mode 100644 index 00000000..44b19019 --- /dev/null +++ b/src/test/resources/vc-templates/EuropeanBankIdentity.json @@ -0,0 +1,23 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/EuropeanBankIdentity.json", + "type": "JsonSchemaValidator2018" + }, + "credentialSubject": { + "birthDate": "1958-08-17", + "familyName": "DOE", + "givenName": "JOHN", + "id": "identity#verifiableID", + "placeOfBirth": { + "country": "DE", + "locality": "Berlin" + } + }, + "id": "identity#EuropeanBankIdentity#3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:example:456", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "EuropeanBankIdentity"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/GaiaxCredential.json b/src/test/resources/vc-templates/GaiaxCredential.json new file mode 100644 index 00000000..c0be4a1e --- /dev/null +++ b/src/test/resources/vc-templates/GaiaxCredential.json @@ -0,0 +1,40 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "DNSpublicKey": "04:8B:CA:33:B1:A1:3A:69:E6:A2:1E:BE:CB:4E:DF:75:A9:70:8B:AA:51:83:AB:A1:B0:5A:35:20:3D:B4:29:09:AD:67:B4:12:19:3B:6A:B5:7C:12:3D:C4:CA:DD:A5:E0:DA:05:1E:5E:1A:4B:D1:F2:BA:8F:07:4D:C7:B6:AA:23:46", + "brandName": "deltaDAO", + "commercialRegister": { + "countryName": "Germany", + "locality": "Hamburg", + "organizationName": "Amtsgericht Hamburg (-Mitte)", + "organizationUnit": "Registergericht", + "postalCode": "20355", + "streetAddress": "Caffamacherreihe 20" + }, + "corporateEmailAddress": "contact@delta-dao.com", + "ethereumAddress": { + "id": "0x4C84a36fCDb7Bc750294A7f3B5ad5CA8F74C4A52" + }, + "id": "did:key:dummy", + "individualContactLegal": "legal@delta-dao.com", + "individualContactTechnical": "support@delta-dao.com", + "jurisdiction": "Germany", + "legalForm": "Stock Company", + "legalRegistrationNumber": "HRB 170364", + "legallyBindingAddress": { + "countryName": "Germany", + "locality": "Hamburg", + "postalCode": "22303", + "streetAddress": "Geibelstr. 46B" + }, + "legallyBindingName": "deltaDAO AG", + "trustState": "trusted", + "webAddress": { + "url": "https://www.delta-dao.com/" + } + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "type": ["VerifiableCredential", "GaiaxCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/Iso27001Certificate.json b/src/test/resources/vc-templates/Iso27001Certificate.json new file mode 100644 index 00000000..f8d1ea60 --- /dev/null +++ b/src/test/resources/vc-templates/Iso27001Certificate.json @@ -0,0 +1,30 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "brandName": "deltaDAO", + "corporateEmailAddress": "contact@delta-dao.com", + "id": "did:key:dummy", + "individualContactLegal": "legal@delta-dao.com", + "individualContactTechnical": "support@delta-dao.com", + "jurisdiction": "Germany", + "legalForm": "Stock Company", + "legalRegistrationNumber": "HRB 170364", + "legallyBindingAddress": { + "countryName": "Germany", + "locality": "Hamburg", + "postalCode": "22303", + "streetAddress": "Geibelstr. 46B" + }, + "legallyBindingName": "deltaDAO AG", + "webAddress": "https://www.delta-dao.com/" + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "iso2700Criteria": { + "certifcateName": "IOS/IEC 27001:2022", + "certificateNr": "123", + "scope": ["consulting service", "software development", "IT operations"] + }, + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "type": ["VerifiableCredential", "VerifiableAttestation", "Iso27001Certificate"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/KybCredential.json b/src/test/resources/vc-templates/KybCredential.json new file mode 100644 index 00000000..9cfc5919 --- /dev/null +++ b/src/test/resources/vc-templates/KybCredential.json @@ -0,0 +1,33 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "brandName": "deltaDAO", + "commercialRegister": { + "countryName": "Germany", + "locality": "Hamburg", + "organizationName": "Amtsgericht Hamburg (-Mitte)", + "organizationUnit": "Registergericht", + "postalCode": "20355", + "streetAddress": "Caffamacherreihe 20" + }, + "corporateEmailAddress": "contact@delta-dao.com", + "id": "did:key:dummy", + "individualContactLegal": "legal@delta-dao.com", + "individualContactTechnical": "support@delta-dao.com", + "jurisdiction": "Germany", + "legalForm": "Stock Company", + "legalRegistrationNumber": "HRB 170364", + "legallyBindingAddress": { + "countryName": "Germany", + "locality": "Hamburg", + "postalCode": "22303", + "streetAddress": "Geibelstr. 46B" + }, + "legallyBindingName": "deltaDAO AG", + "webAddress": "https://www.delta-dao.com/" + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "type": ["VerifiableCredential", "VerifiableAttestation", "KybCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/KybMonoCredential.json b/src/test/resources/vc-templates/KybMonoCredential.json new file mode 100644 index 00000000..c254eca9 --- /dev/null +++ b/src/test/resources/vc-templates/KybMonoCredential.json @@ -0,0 +1,11 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "id": "did:key:dummy", + "kybValidationPassed": true + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "type": ["VerifiableCredential", "VerifiableAttestation", "KybMonoCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/KycCredential.json b/src/test/resources/vc-templates/KycCredential.json new file mode 100644 index 00000000..f95cfee4 --- /dev/null +++ b/src/test/resources/vc-templates/KycCredential.json @@ -0,0 +1,20 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "currentAddress": "1 Boulevard de la Liberté, 59800 Lille", + "dateOfBirth": "1993-04-08", + "familyName": "DOE", + "firstName": "Jane", + "gender": "FEMALE", + "id": "did:ebsi:2AEMAqXWKYMu1JHPAgGcga4dxu7ThgfgN95VyJBJGZbSJUtp", + "nameAndFamilyNameAtBirth": "Jane DOE", + "personalIdentifier": "0904008084H", + "placeOfBirth": "LILLE, FRANCE" + }, + "id": "identity#KycCredential#3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "VerifiableAttestation", "KycCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/LegalPerson.json b/src/test/resources/vc-templates/LegalPerson.json new file mode 100644 index 00000000..0e34bfea --- /dev/null +++ b/src/test/resources/vc-templates/LegalPerson.json @@ -0,0 +1,29 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "gx-participant:blockchainAccountId": "0x4C84a36fCDb7Bc750294A7f3B5ad5CA8F74C4A52", + "gx-participant:headquarterAddress": { + "gx-participant:addressCode": "DE-HH", + "gx-participant:addressCountryCode": "DE", + "gx-participant:postal-code": "22303", + "gx-participant:street-address": "Geibelstraße 46b" + }, + "gx-participant:legalAddress": { + "gx-participant:addressCode": "DE-HH", + "gx-participant:addressCountryCode": "DE", + "gx-participant:postal-code": "22303", + "gx-participant:street-address": "Geibelstraße 46b" + }, + "gx-participant:legalName": "deltaDAO AG", + "gx-participant:registrationNumber": { + "gx-participant:registrationNumberNumber": "391200FJBNU0YW987L26", + "gx-participant:registrationNumberType": "leiCode" + }, + "gx-participant:termsAndConditions": "70c1d713215f95191a11d38fe2341faed27d19e083917bc8732ca4fea4976700", + "id": "did:web:delta-dao.com" + }, + "id": "https://delta-dao.com/.well-known/participant.json", + "issuanceDate": "2022-09-15T20:05:20.997Z", + "issuer": "did:web:delta-dao.com", + "type": ["VerifiableCredential", "LegalPerson"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/OpenBadgeCredential.json b/src/test/resources/vc-templates/OpenBadgeCredential.json new file mode 100644 index 00000000..eb4caf35 --- /dev/null +++ b/src/test/resources/vc-templates/OpenBadgeCredential.json @@ -0,0 +1,40 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1", "https://purl.imsglobal.org/spec/ob/v3p0/context.json"], + "credentialSchema": [{ + "id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/json/ob_v3p0_achievementcredential_schema.json", + "type": "https://json-schema.org/draft/2019-09/schema" + }], + "credentialSubject": { + "achievement": { + "criteria": { + "narrative": "The cohort of the JFF Plugfest 2 in August-November of 2022 collaborated to push interoperability of VCs in education forward.", + "type": "Criteria" + }, + "description": "This wallet can display this Open Badge 3.0", + "id": "0", + "image": { + "id": "https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png", + "type": "Image" + }, + "name": "Our Wallet Passed JFF Plugfest #2 2022", + "type": "Achievement" + }, + "id": "did:jwk:1235667890", + "type": "AchievementSubject" + }, + "id": "urn:uuid:01af1ece-4279-4c1e-b952-469bc5b5bab2", + "issuanceDate": "2020-03-10T04:24:12.164Z", + "issued": "2020-03-10T04:24:12.164Z", + "issuer": { + "id": "did:key:z6MkrHKzgsahxBLyNAbLQyB1pcWNYC9GmywiWPgkrvntAZcj", + "image": { + "id": "https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png", + "type": "Image" + }, + "name": "Jobs for the Future (JFF)", + "type": "Profile", + "url": "https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png" + }, + "name": "Achievement Credential", + "type": ["VerifiableCredential", "OpenBadgeCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/ParticipantCredential.json b/src/test/resources/vc-templates/ParticipantCredential.json new file mode 100644 index 00000000..55895f51 --- /dev/null +++ b/src/test/resources/vc-templates/ParticipantCredential.json @@ -0,0 +1,28 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1", "https://w3id.org/security/suites/ed25519-2020/v1", "https://w3id.org/security/suites/jws-2020/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/ParticipantCredential.json", + "type": "JsonSchemaValidator2018" + }, + "credentialStatus": { + "id": "https://gaiax.europa.eu/status/participant-credential#392ac7f6-399a-437b-a268-4691ead8f176", + "type": "CredentialStatusList2020" + }, + "credentialSubject": { + "ethereumAddress": "0x4C84a36fCDb7Bc750294A7f3B5ad5CA8F74C4A52", + "hasCountry": "GER", + "hasJurisdiction": "GER", + "hasLegallyBindingName": "deltaDAO AG", + "hasRegistrationNumber": "DEK1101R.HRB170364", + "hash": "9ecf754ffdad0c6de238f60728a90511780b2f7dbe2f0ea015115515f3f389cd", + "id": "did:web:delta-dao.com", + "leiCode": "391200FJBNU0YW987L26", + "parentOrganisation": "", + "subOrganisation": "" + }, + "expirationDate": "2022-01-06T20:38:38Z", + "id": "vc.gaia-x.eu/participant-credential#392ac7f6-399a-437b-a268-4691ead8f176", + "issued": "2022-01-03T20:38:38Z", + "issuer": "did:web:vc.gaia-x.eu:issuer", + "type": ["VerifiableCredential", "ParticipantCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/PeerReview.json b/src/test/resources/vc-templates/PeerReview.json new file mode 100644 index 00000000..88c0dabe --- /dev/null +++ b/src/test/resources/vc-templates/PeerReview.json @@ -0,0 +1,32 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/serialized/PeerReview.json", + "type": "JsonSchemaValidator2018" + }, + "credentialSubject": { + "completedDate": "2021-08-31T00:00:00Z", + "groupingKey": "c4235de3-e35b-4a53-ac46-f62c2b222441", + "id": "identity#verifiableID", + "measurements": [{ + "indicators": [{ + "category": "SKILL", + "name": "Coaching", + "type": "People Skills" + }], + "name": "Coaching", + "numberValue": 87, + "scale": "PERCENTAGE", + "type": "People Skills" + }], + "name": "WorkPi Peer Review", + "providerName": "WorkPi", + "type": "Assessment" + }, + "id": "identity#PeerReview#3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:example:456", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "PeerReview"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/PermanentResidentCard.json b/src/test/resources/vc-templates/PermanentResidentCard.json new file mode 100644 index 00000000..3de9b456 --- /dev/null +++ b/src/test/resources/vc-templates/PermanentResidentCard.json @@ -0,0 +1,11 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1", "https://w3id.org/citizenship/v1"], + "credentialSubject": { + "birthDate": "1958-08-17", + "givenName": "JOHN", + "id": "did:example:123", + "type": ["PermanentResident", "Person"] + }, + "issuer": "did:example:456", + "type": ["VerifiableCredential", "PermanentResidentCard"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/ProofOfResidence.json b/src/test/resources/vc-templates/ProofOfResidence.json new file mode 100644 index 00000000..ed98aa65 --- /dev/null +++ b/src/test/resources/vc-templates/ProofOfResidence.json @@ -0,0 +1,43 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/ProofOfResidence.json", + "type": "JsonSchemaValidator2018" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/identity#verifiableID#1dee355d-0432-4910-ac9c-70d89e8d674e", + "type": "CredentialStatusList2020" + }, + "credentialSubject": { + "address": { + "countryName": "LU", + "locality": "Steinfort", + "postalCode": "L-8410", + "streetAddress": "16 Route D' Arlon" + }, + "dateOfBirth": "1993-04-08", + "familyName": "Beron", + "familyStatus": "Single", + "firstNames": "Domink", + "gender": "Male", + "id": "id123", + "identificationNumber": "123456789", + "nationality": "AT" + }, + "evidence": [{ + "documentPresence": "Physical", + "evidenceDocument": "Passport", + "id": "https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678", + "subjectPresence": "Physical", + "type": ["DocumentVerification"], + "verifier": "did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a" + }], + "expirationDate": "2022-06-22T14:11:44Z", + "id": "residence#3fea53a4-0432-4910-ac9c-69ah8da3c37f", + "issued": "2019-06-22T14:11:44Z", + "issuer": "did:ebsi:2757945549477fc571663bee12042873fe555b674bd294a3", + "title": "Proof of Residence", + "validFrom": "2019-06-22T14:11:44Z", + "issuanceDate": "2019-06-22T14:11:44Z", + "type": ["VerifiableCredential", "VerifiableAttestation", "ProofOfResidence"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/ServiceOfferingCredential.json b/src/test/resources/vc-templates/ServiceOfferingCredential.json new file mode 100644 index 00000000..7ab35790 --- /dev/null +++ b/src/test/resources/vc-templates/ServiceOfferingCredential.json @@ -0,0 +1,27 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "gx-service-offering:dataExport": [{ + "gx-service-offering:accessType": "digital", + "gx-service-offering:formatType": "mime/png", + "gx-service-offering:requestType": "emails" + }], + "gx-service-offering:dataProtectionRegime": ["GDPR2016"], + "gx-service-offering:dependsOn": ["https://compliance.gaia-x.eu/.well-known/serviceManagedPostgreSQLOVH.json", "https://compliance.gaia-x.eu/.well-known/serviceManagedK8sOVH.json"], + "gx-service-offering:description": "The Compliance Service will validate the shape and content of Self Descriptions. Required fields and consistency rules are defined in the Gaia-X Trust Framework.", + "gx-service-offering:gdpr": [{ + "gx-service-offering:imprint": "https://gaia-x.eu/imprint/", + "gx-service-offering:privacyPolicy": "https://gaia-x.eu/privacy-policy/" + }], + "gx-service-offering:name": "Gaia-X Lab Compliance Service", + "gx-service-offering:providedBy": "https://compliance.gaia-x.eu/.well-known/participant.json", + "gx-service-offering:termsAndConditions": [{ + "gx-service-offering:hash": "myrandomhash", + "gx-service-offering:url": "https://compliance.gaia-x.eu/terms" + }], + "gx-service-offering:webAddress": "https://compliance.gaia-x.eu/", + "id": "https://compliance.gaia-x.eu/.well-known/serviceComplianceService.json" + }, + "id": "https://compliance.gaia-x.eu/.well-known/serviceComplianceService.json", + "type": ["VerifiableCredential", "ServiceOfferingExperimental"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/UniversityDegree.json b/src/test/resources/vc-templates/UniversityDegree.json new file mode 100644 index 00000000..0a3f5358 --- /dev/null +++ b/src/test/resources/vc-templates/UniversityDegree.json @@ -0,0 +1,16 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"], + "credentialSubject": { + "degree": { + "name": "Bachelor of Science and Arts", + "type": "BachelorDegree" + }, + "id": "did:example:123" + }, + "id": "http://example.gov/credentials/3732", + "issued": "2020-03-10T04:24:12.164Z", + "issuer": { + "id": "did:example:456" + }, + "type": ["VerifiableCredential", "UniversityDegreeCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/VerifiableAttestation.json b/src/test/resources/vc-templates/VerifiableAttestation.json new file mode 100644 index 00000000..0a8aefbb --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableAttestation.json @@ -0,0 +1,29 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/VerifiableAttestation.json", + "type": "JsonSchemaValidator2018" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/identity#verifiableID#1dee355d-0432-4910-ac9c-70d89e8d674e", + "type": "CredentialStatusList2020" + }, + "credentialSubject": { + "id": "id123" + }, + "evidence": [{ + "documentPresence": "Physical", + "evidenceDocument": "Passport", + "id": "https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678", + "subjectPresence": "Physical", + "type": ["DocumentVerification"], + "verifier": "did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a" + }], + "expirationDate": "2022-06-22T14:11:44Z", + "id": "education#higherEducation#3fea53a4-0432-4910-ac9c-69ah8da3c37f", + "issued": "2019-06-22T14:11:44Z", + "issuer": "did:ebsi:2757945549477fc571663bee12042873fe555b674bd294a3", + "validFrom": "2019-06-22T14:11:44Z", + "issuanceDate": "2019-06-22T14:11:44Z", + "type": ["VerifiableCredential", "VerifiableAttestation"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/VerifiableAuthorization.json b/src/test/resources/vc-templates/VerifiableAuthorization.json new file mode 100644 index 00000000..f0ac6370 --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableAuthorization.json @@ -0,0 +1,13 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "id": "did:ebsi:00000004321", + "naturalPerson": { + "did": "did:example:00001111" + } + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:ebsi:000001234", + "type": ["VerifiableCredential", "VerifiableAuthorization"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/VerifiableDiploma.json b/src/test/resources/vc-templates/VerifiableDiploma.json new file mode 100644 index 00000000..619ed77d --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableDiploma.json @@ -0,0 +1,64 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/VerifiableDiploma.json", + "type": "JsonSchemaValidator2018" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176", + "type": "CredentialStatusList2020" + }, + "credentialSubject": { + "awardingOpportunity": { + "awardingBody": { + "eidasLegalIdentifier": "Unknown", + "homepage": "https://leaston.bcdiploma.com/", + "id": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "preferredName": "Leaston University", + "registration": "0597065J" + }, + "endedAtTime": "2020-06-26T00:00:00Z", + "id": "https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity", + "identifier": "https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ", + "location": "FRANCE", + "startedAtTime": "2019-09-02T00:00:00Z" + }, + "dateOfBirth": "1993-04-08", + "familyName": "DOE", + "givenNames": "Jane", + "gradingScheme": { + "id": "https://leaston.bcdiploma.com/law-economics-management#GradingScheme", + "title": "Lower Second-Class Honours" + }, + "id": "did:ebsi:2AEMAqXWKYMu1JHPAgGcga4dxu7ThgfgN95VyJBJGZbSJUtp", + "identifier": "0904008084H", + "learningAchievement": { + "additionalNote": ["DISTRIBUTION MANAGEMENT"], + "description": "MARKETING AND SALES", + "id": "https://leaston.bcdiploma.com/law-economics-management#LearningAchievment", + "title": "MASTERS LAW, ECONOMICS AND MANAGEMENT" + }, + "learningSpecification": { + "ectsCreditPoints": 120, + "eqfLevel": 7, + "id": "https://leaston.bcdiploma.com/law-economics-management#LearningSpecification", + "iscedfCode": ["7"], + "nqfLevel": ["7"] + } + }, + "evidence": { + "documentPresence": ["Physical"], + "evidenceDocument": ["Passport"], + "id": "https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678", + "subjectPresence": "Physical", + "type": ["DocumentVerification"], + "verifier": "did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a" + }, + "expirationDate": "2022-08-31T00:00:00Z", + "id": "education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "VerifiableAttestation", "VerifiableDiploma"] +} diff --git a/src/test/resources/vc-templates/VerifiableId.json b/src/test/resources/vc-templates/VerifiableId.json new file mode 100644 index 00000000..b8b4aa04 --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableId.json @@ -0,0 +1,31 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/VerifiableId.json", + "type": "FullJsonSchemaValidator2021" + }, + "credentialSubject": { + "currentAddress": ["1 Boulevard de la Liberté, 59800 Lille"], + "dateOfBirth": "1993-04-08", + "familyName": "DOE", + "firstName": "Jane", + "gender": "FEMALE", + "id": "did:ebsi:2AEMAqXWKYMu1JHPAgGcga4dxu7ThgfgN95VyJBJGZbSJUtp", + "nameAndFamilyNameAtBirth": "Jane DOE", + "personalIdentifier": "0904008084H", + "placeOfBirth": "LILLE, FRANCE" + }, + "evidence": [{ + "documentPresence": ["Physical"], + "evidenceDocument": ["Passport"], + "subjectPresence": "Physical", + "type": ["DocumentVerification"], + "verifier": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN" + }], + "id": "urn:uuid:3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "VerifiableAttestation", "VerifiableId"] +} diff --git a/src/test/resources/vc-templates/VerifiableMandate.json b/src/test/resources/vc-templates/VerifiableMandate.json new file mode 100644 index 00000000..0bffcb4c --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableMandate.json @@ -0,0 +1,28 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xb77f8516a965631b4f197ad54c65a9e2f9936ebfb76bae4906d33744dbcc60ba", + "type": "FullJsonSchemaValidator2021" + }, + "credentialSubject": { + "holder": { + "constraints": {}, + "grant": "apply_to_masters", + "id": "", + "role": "family" + }, + "id": "", + "policySchemaURI": "https://raw.githubusercontent.com/walt-id/waltid-ssikit/master/src/test/resources/verifiable-mandates/test-policy.rego" + }, + "evidence": [{ + "evidenceValue": "", + "id": "https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678", + "type": ["VerifiableMandatePresentation"] + }], + "id": "urn:uuid:3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "VerifiableMandate"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/VerifiablePresentation.json b/src/test/resources/vc-templates/VerifiablePresentation.json new file mode 100644 index 00000000..db95674d --- /dev/null +++ b/src/test/resources/vc-templates/VerifiablePresentation.json @@ -0,0 +1,43 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "holder": "did:ebsi:00000004321", + "id": "id", + "type": ["VerifiablePresentation"], + "verifiableCredential": [{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "id": "did:ebsi:00000004321", + "naturalPerson": { + "did": "did:example:00001111" + } + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:ebsi:000001234", + "proof": { + "created": "assertionMethod", + "creator": "2020-08-24T14:13:44Z", + "domain": "did:ebsi-eth:000001234#key-1", + "proofPurpose": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19.", + "type": "EcdsaSecp256k1Signature2019" + }, + "type": ["VerifiableCredential", "VerifiableAuthorization"] + }, { + "@context": ["https://www.w3.org/2018/credentials/v1", "https://w3id.org/citizenship/v1"], + "credentialSubject": { + "birthDate": "1958-08-17", + "givenName": "JOHN", + "id": "did:example:123", + "type": ["PermanentResident", "Person"] + }, + "issuer": "did:example:456", + "proof": { + "created": "assertionMethod", + "creator": "2020-04-22T10:37:22Z", + "domain": "did:example:456#key-1", + "proofPurpose": "eyJjcml0IjpbImI2NCJdLCJiNjQiOmZhbHNlLCJhbGciOiJFZERTQSJ9..BhWew0x-txcroGjgdtK-yBCqoetg9DD9SgV4245TmXJi-PmqFzux6Cwaph0r-mbqzlE17yLebjfqbRT275U1AA", + "type": "Ed25519Signature2018" + }, + "type": ["VerifiableCredential", "PermanentResidentCard"] + }] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/VerifiableVaccinationCertificate.json b/src/test/resources/vc-templates/VerifiableVaccinationCertificate.json new file mode 100644 index 00000000..ddc88964 --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableVaccinationCertificate.json @@ -0,0 +1 @@ +{"@context" : ["https://www.w3.org/2018/credentials/v1"], "credentialSchema" : {"id" : "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/VerifiableVaccinationCertificate.json", "type" : "JsonSchemaValidator2018"}, "credentialStatus" : {"id" : "https://essif.europa.eu/status/covidvaccination#392ac7f6-399a-437b-a268-4691ead8f176", "type" : "CredentialStatusList2020"}, "credentialSubject" : {"dateOfBirth" : "1993-04-08", "familyName" : "DOE", "givenNames" : "Jane", "id" : "asdf", "personIdentifier" : "optional The type of identifier and identifier of the person, according to the policies applicable in each country. Examples are citizen ID and/or document number (ID- card/passport) or identifier within the health system/IIS/e-registry.", "personSex" : "optional", "uniqueCertificateIdentifier" : "UVCI0904008084H", "vaccinationProphylaxisInformation" : [{"administeringCentre" : "Name/code of administering centre or a health authority responsible for the vaccination event", "batchNumber" : "optional 1234", "countryOfVaccination" : "DE", "dateOfVaccination" : "2021-02-12", "diseaseOrAgentTargeted" : {"code" : "840539006", "system" : "2.16.840.1.113883. 6.96", "version" : "2021-01-31"}, "doseNumber" : "1", "marketingAuthorizationHolder" : "Example Vaccine Manufacturing Company", "nextVaccinationDate" : "optional - 2021-03-28", "totalSeriesOfDoses" : "2", "vaccineMedicinalProduct" : "VACCINE concentrate for dispersion for injection", "vaccineOrProphylaxis" : "1119349007 COVID-19 example vaccine"}]}, "evidence" : {"documentPresence" : ["Physical"], "evidenceDocument" : ["Passport"], "id" : "https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678", "subjectPresence" : "Physical", "type" : ["DocumentVerification"], "verifier" : "did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a"}, "expirationDate" : "2022-08-31T00:00:00Z", "id" : "covidvaccination#392ac7f6-399a-437b-a268-4691ead8f176", "issued" : "2021-08-31T00:00:00Z", "issuer" : "qwer", "validFrom" : "2021-08-31T00:00:00Z", "issuanceDate" : "2021-08-31T00:00:00Z", "type" : ["VerifiableCredential", "VerifiableAttestation", "VerifiableVaccinationCertificate"]} \ No newline at end of file From e6e231d3642b0d9e486aa5a08c4c0578fd0e491a Mon Sep 17 00:00:00 2001 From: severinstampler Date: Thu, 16 Mar 2023 11:48:27 +0100 Subject: [PATCH 3/5] fix load template api test fix loading templates from cache if loadTemplate = false --- .../credentials/w3c/templates/VcTemplate.kt | 3 +- .../w3c/templates/VcTemplateManager.kt | 43 ++++++++++++------- .../signatory/rest/SignatoryController.kt | 3 +- .../id/walt/signatory/SignatoryApiTest.kt | 6 ++- 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplate.kt b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplate.kt index 04508cce..b49b0492 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplate.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplate.kt @@ -1,9 +1,10 @@ package id.walt.credentials.w3c.templates +import id.walt.common.SingleVCObject import id.walt.credentials.w3c.VerifiableCredential data class VcTemplate( val name: String, - val template: VerifiableCredential? = null, + @SingleVCObject val template: VerifiableCredential? = null, val mutable: Boolean ) diff --git a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt index db7dc308..096040a9 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt @@ -32,41 +32,52 @@ object VcTemplateManager { .expireAfterWrite(Duration.ofMinutes(10)) .build() - private fun String?.toVcTemplate(name: String, loadTemplate: Boolean, isMutable: Boolean) = - this?.let { VcTemplate(name, if (loadTemplate && it.isNotBlank()) it.toVerifiableCredential() else null, isMutable) } + private fun String?.toVcTemplate(name: String, populateTemplate: Boolean, isMutable: Boolean) = + this?.let { VcTemplate(name, if (populateTemplate && it.isNotBlank()) it.toVerifiableCredential() else null, isMutable) } fun loadTemplateFromHkvStore(name: String, loadTemplate: Boolean) = ContextManager.hkvStore.getAsString(HKVKey(SAVED_VC_TEMPLATES_KEY, name)) .toVcTemplate(name, loadTemplate, true) - fun loadTemplateFromResources(name: String, loadTemplate: Boolean) = + fun loadTemplateFromResources(name: String, populateTemplate: Boolean) = object {}.javaClass.getResource("/vc-templates/$name.json")?.readText() - .toVcTemplate(name, loadTemplate, false) + .toVcTemplate(name, populateTemplate, false) - fun loadTemplateFromFile(name: String, loadTemplate: Boolean, runtimeTemplateFolder: String) = + fun loadTemplateFromFile(name: String, populateTemplate: Boolean, runtimeTemplateFolder: String) = File("$runtimeTemplateFolder/$name.json").let { if (it.exists()) it.readText() else null - }.toVcTemplate(name, loadTemplate, false) + }.toVcTemplate(name, populateTemplate, false) + + fun loadTemplate(name: String, populateTemplate: Boolean, runtimeTemplateFolder: String) = loadTemplateFromHkvStore(name, populateTemplate) + ?: loadTemplateFromResources(name, populateTemplate) + ?: loadTemplateFromFile(name, populateTemplate, runtimeTemplateFolder) + ?: throw IllegalArgumentException("No template found with name: $name") fun retrieveOrLoadCachedTemplate( name: String, - loadTemplate: Boolean = true, + populateTemplate: Boolean = true, runtimeTemplateFolder: String = "/vc-templates-runtime" - ) = templateCache.get(name) { - loadTemplateFromHkvStore(name, loadTemplate) - ?: loadTemplateFromResources(name, loadTemplate) - ?: loadTemplateFromFile(name, loadTemplate, runtimeTemplateFolder) - ?: throw IllegalArgumentException("No template found with name: $name") - } + ) = if(populateTemplate) { + // only add to cache, if template is populated + templateCache.get(name) { + loadTemplate(name, true, runtimeTemplateFolder) + } + } else { + // try to get from cache or load unpopulated template + (templateCache.getIfPresent(name) ?: loadTemplate(name, false, runtimeTemplateFolder)).let { + // reset populated template, in case it was loaded from cache + VcTemplate(it.name, null, it.mutable) + } + } fun getTemplate( name: String, - loadTemplate: Boolean = true, + populateTemplate: Boolean = true, runtimeTemplateFolder: String = "/vc-templates-runtime" ): VcTemplate { - val cachedTemplate = retrieveOrLoadCachedTemplate(name, loadTemplate, runtimeTemplateFolder) + val cachedTemplate = retrieveOrLoadCachedTemplate(name, populateTemplate, runtimeTemplateFolder) - return if (cachedTemplate.template == null && loadTemplate) { + return if (cachedTemplate.template == null && populateTemplate) { templateCache.invalidate(name) retrieveOrLoadCachedTemplate(name, true, runtimeTemplateFolder) } else cachedTemplate diff --git a/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt b/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt index fa735f4a..51924c8b 100644 --- a/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt +++ b/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt @@ -1,5 +1,6 @@ package id.walt.signatory.rest +import id.walt.common.KlaxonWithConverters import id.walt.credentials.w3c.JsonConverter import id.walt.credentials.w3c.VerifiableCredential import id.walt.credentials.w3c.builder.W3CCredentialBuilder @@ -24,7 +25,7 @@ object SignatoryController { val signatory = Signatory.getService() fun listTemplates(ctx: Context) { - ctx.json(signatory.listTemplates()) + ctx.contentType(ContentType.APPLICATION_JSON).result(KlaxonWithConverters().toJsonString(signatory.listTemplates())) } fun listTemplatesDocs() = document().operation { diff --git a/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt b/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt index 615c7344..b67f6c76 100644 --- a/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt +++ b/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt @@ -13,6 +13,7 @@ import id.walt.signatory.rest.IssueCredentialRequest import id.walt.signatory.rest.SignatoryRestAPI import id.walt.test.RESOURCES_PATH import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.inspectors.shouldForAll import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe @@ -66,7 +67,10 @@ class SignatoryApiTest : AnnotationSpec() { client.get("$SIGNATORY_API_URL/v1/templates").bodyAsText() .also { println("BODY: $it") } .let { KlaxonWithConverters().parseArray(it) }!! - + // make sure templates are not populated + templates.forEach { + it.template shouldBe null + } val templateNames = templates.map { it.name } templateNames shouldContain "Europass" From 0caeec4ef37681cf3b2730b4351bb067b4098103 Mon Sep 17 00:00:00 2001 From: Kevin Burgmann Date: Wed, 15 Mar 2023 23:25:22 +0100 Subject: [PATCH 4/5] Fix some issues arising from context system when using caches --- README.md | 6 +- build.gradle.kts | 18 +-- src/main/kotlin/id/walt/PerformanceRun.kt | 43 +++++ .../id/walt/credentials/w3c/JsonConverter.kt | 29 ++-- .../credentials/w3c/VerifiableCredential.kt | 5 +- .../credentials/w3c/W3CCredentialSubject.kt | 1 + .../w3c/templates/VcTemplateManager.kt | 41 +++-- .../kotlin/id/walt/services/did/DidService.kt | 22 +-- .../kotlin/id/walt/signatory/Signatory.kt | 10 +- src/test/kotlin/id/walt/Performance.kt | 4 +- .../kotlin/id/walt/services/ReadmeTest.kt | 2 +- .../id/walt/services/oidc/OIDC4VCTest.kt | 4 +- .../id/walt/signatory/SignatoryApiTest.kt | 12 +- .../id/walt/signatory/SignatoryServiceTest.kt | 29 ++-- .../vc-templates/AmletCredential.json | 44 +++++ .../vc-templates/DataConsortium.json | 18 +++ .../vc-templates/DataSelfDescription.json | 19 +++ .../vc-templates/DataServiceOffering.json | 37 +++++ .../EbsiAccreditedVerifiableAttestation.json | 31 ++++ .../EbsiDiplomaVerifiableAccreditation.json | 51 ++++++ .../resources/vc-templates/EbsiEuropass.json | 0 ...EbsiVerifiableAccreditationToAccredit.json | 40 +++++ .../EbsiVerifiableAttestationGeneric.json | 26 +++ .../EbsiVerifiableAttestationLegal.json | 49 ++++++ .../EbsiVerifiableAttestationPerson.json | 26 +++ src/test/resources/vc-templates/Email.json | 11 ++ src/test/resources/vc-templates/Europass.json | 153 ++++++++++++++++++ .../vc-templates/EuropeanBankIdentity.json | 23 +++ .../vc-templates/GaiaxCredential.json | 40 +++++ .../vc-templates/Iso27001Certificate.json | 30 ++++ .../resources/vc-templates/KybCredential.json | 33 ++++ .../vc-templates/KybMonoCredential.json | 11 ++ .../resources/vc-templates/KycCredential.json | 20 +++ .../resources/vc-templates/LegalPerson.json | 29 ++++ .../vc-templates/OpenBadgeCredential.json | 40 +++++ .../vc-templates/ParticipantCredential.json | 28 ++++ .../resources/vc-templates/PeerReview.json | 32 ++++ .../vc-templates/PermanentResidentCard.json | 11 ++ .../vc-templates/ProofOfResidence.json | 43 +++++ .../ServiceOfferingCredential.json | 27 ++++ .../vc-templates/UniversityDegree.json | 16 ++ .../vc-templates/VerifiableAttestation.json | 29 ++++ .../vc-templates/VerifiableAuthorization.json | 13 ++ .../vc-templates/VerifiableDiploma.json | 64 ++++++++ .../resources/vc-templates/VerifiableId.json | 31 ++++ .../vc-templates/VerifiableMandate.json | 28 ++++ .../vc-templates/VerifiablePresentation.json | 43 +++++ .../VerifiableVaccinationCertificate.json | 1 + 48 files changed, 1239 insertions(+), 84 deletions(-) create mode 100644 src/main/kotlin/id/walt/PerformanceRun.kt create mode 100644 src/test/resources/vc-templates/AmletCredential.json create mode 100644 src/test/resources/vc-templates/DataConsortium.json create mode 100644 src/test/resources/vc-templates/DataSelfDescription.json create mode 100644 src/test/resources/vc-templates/DataServiceOffering.json create mode 100644 src/test/resources/vc-templates/EbsiAccreditedVerifiableAttestation.json create mode 100644 src/test/resources/vc-templates/EbsiDiplomaVerifiableAccreditation.json create mode 100644 src/test/resources/vc-templates/EbsiEuropass.json create mode 100644 src/test/resources/vc-templates/EbsiVerifiableAccreditationToAccredit.json create mode 100644 src/test/resources/vc-templates/EbsiVerifiableAttestationGeneric.json create mode 100644 src/test/resources/vc-templates/EbsiVerifiableAttestationLegal.json create mode 100644 src/test/resources/vc-templates/EbsiVerifiableAttestationPerson.json create mode 100644 src/test/resources/vc-templates/Email.json create mode 100644 src/test/resources/vc-templates/Europass.json create mode 100644 src/test/resources/vc-templates/EuropeanBankIdentity.json create mode 100644 src/test/resources/vc-templates/GaiaxCredential.json create mode 100644 src/test/resources/vc-templates/Iso27001Certificate.json create mode 100644 src/test/resources/vc-templates/KybCredential.json create mode 100644 src/test/resources/vc-templates/KybMonoCredential.json create mode 100644 src/test/resources/vc-templates/KycCredential.json create mode 100644 src/test/resources/vc-templates/LegalPerson.json create mode 100644 src/test/resources/vc-templates/OpenBadgeCredential.json create mode 100644 src/test/resources/vc-templates/ParticipantCredential.json create mode 100644 src/test/resources/vc-templates/PeerReview.json create mode 100644 src/test/resources/vc-templates/PermanentResidentCard.json create mode 100644 src/test/resources/vc-templates/ProofOfResidence.json create mode 100644 src/test/resources/vc-templates/ServiceOfferingCredential.json create mode 100644 src/test/resources/vc-templates/UniversityDegree.json create mode 100644 src/test/resources/vc-templates/VerifiableAttestation.json create mode 100644 src/test/resources/vc-templates/VerifiableAuthorization.json create mode 100644 src/test/resources/vc-templates/VerifiableDiploma.json create mode 100644 src/test/resources/vc-templates/VerifiableId.json create mode 100644 src/test/resources/vc-templates/VerifiableMandate.json create mode 100644 src/test/resources/vc-templates/VerifiablePresentation.json create mode 100644 src/test/resources/vc-templates/VerifiableVaccinationCertificate.json diff --git a/README.md b/README.md index 7eeb99d3..4f2bff7f 100644 --- a/README.md +++ b/README.md @@ -63,13 +63,13 @@ fun main() { val issuerDid = DidService.create(DidMethod.ebsi) val holderDid = DidService.create(DidMethod.key) - // Issue VC in JSON-LD and JWT format (for show-casing both formats) + // Issue VC with LD_PROOF and JWT format (for show-casing both formats) val vcJson = Signatory.getService().issue( - templateId = "VerifiableId", + templateIdOrFilename = "VerifiableId", config = ProofConfig(issuerDid = issuerDid, subjectDid = holderDid, proofType = ProofType.LD_PROOF) ) val vcJwt = Signatory.getService().issue( - templateId = "Europass", + templateIdOrFilename = "Europass", config = ProofConfig(issuerDid = issuerDid, subjectDid = holderDid, proofType = ProofType.JWT) ) diff --git a/build.gradle.kts b/build.gradle.kts index 09430b4a..a3b619b7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,8 +35,8 @@ dependencies { implementation("com.github.multiformats:java-multibase:v1.1.0") implementation("com.microsoft.azure:azure-keyvault:1.2.6") implementation("com.microsoft.azure:azure-client-authentication:1.7.14") - implementation("com.nimbusds:nimbus-jose-jwt:9.30.1") - implementation("com.nimbusds:oauth2-oidc-sdk:10.5.1") + implementation("com.nimbusds:nimbus-jose-jwt:9.30.2") + implementation("com.nimbusds:oauth2-oidc-sdk:10.7") implementation("org.bouncycastle:bcprov-jdk15to18:1.72") implementation("org.bouncycastle:bcpkix-jdk15to18:1.72") @@ -52,23 +52,23 @@ dependencies { implementation("com.beust:klaxon:5.6") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.0") implementation("com.jayway.jsonpath:json-path:2.7.0") - implementation("com.networknt:json-schema-validator:1.0.77") + implementation("com.networknt:json-schema-validator:1.0.78") implementation("net.pwall.json:json-kotlin-schema:0.39") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1") // DB - implementation("org.xerial:sqlite-jdbc:3.39.4.1") + implementation("org.xerial:sqlite-jdbc:3.40.1.0") implementation("com.zaxxer:HikariCP:5.0.1") // CLI - implementation("com.github.ajalt.clikt:clikt-jvm:3.5.0") + implementation("com.github.ajalt.clikt:clikt-jvm:3.5.2") implementation("com.github.ajalt.clikt:clikt:3.5.0") // Misc implementation("commons-io:commons-io:2.11.0") implementation("io.minio:minio:8.4.6") - implementation("io.github.reactivecircus.cache4k:cache4k:0.9.0") + implementation("com.github.ben-manes.caffeine:caffeine:3.1.5") // HTTP implementation("io.ktor:ktor-client-jackson-jvm:2.2.4") @@ -108,9 +108,9 @@ dependencies { //testImplementation(kotlin("test-junit")) testImplementation("io.mockk:mockk:1.13.2") - testImplementation("io.kotest:kotest-runner-junit5:5.5.4") - testImplementation("io.kotest:kotest-assertions-core:5.5.4") - testImplementation("io.kotest:kotest-assertions-json:5.5.4") + testImplementation("io.kotest:kotest-runner-junit5:5.5.5") + testImplementation("io.kotest:kotest-assertions-core:5.5.5") + testImplementation("io.kotest:kotest-assertions-json:5.5.5") } diff --git a/src/main/kotlin/id/walt/PerformanceRun.kt b/src/main/kotlin/id/walt/PerformanceRun.kt new file mode 100644 index 00000000..93f06fd8 --- /dev/null +++ b/src/main/kotlin/id/walt/PerformanceRun.kt @@ -0,0 +1,43 @@ +package id.walt + +import id.walt.credentials.w3c.VerifiableCredential +import id.walt.credentials.w3c.W3CIssuer +import id.walt.model.DidMethod +import id.walt.servicematrix.ServiceMatrix +import id.walt.services.did.DidService +import id.walt.signatory.ProofConfig +import id.walt.signatory.ProofType +import id.walt.signatory.Signatory +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import java.io.File +import kotlin.system.measureTimeMillis + +val RESOURCES_PATH: String = "src/test/resources" + +fun getTemplate(name: String): VerifiableCredential = + VerifiableCredential.fromString(File("$RESOURCES_PATH/verifiable-credentials/vc-template-default.json").readText(Charsets.UTF_8)) + +suspend fun main() { + ServiceMatrix("service-matrix.properties") + val issuerWebDid: String = DidService.create(DidMethod.web) + + //val credentialService: JwtCredentialService = JwtCredentialService.getService() + val signatory = Signatory.getService() + val template = getTemplate("ebsi-attestation") + + template.issuer = W3CIssuer(issuerWebDid) + template.credentialSubject!!.id = issuerWebDid // self signed + + println("" + measureTimeMillis { + val jobs = ArrayList() + repeat(500000) { + jobs.add(GlobalScope.launch { + signatory.issue("VerifiableId", ProofConfig(issuerDid = issuerWebDid, proofType = ProofType.JWT)) + }) + } + + jobs.forEach { it.join() } + } + " ms overall") +} diff --git a/src/main/kotlin/id/walt/credentials/w3c/JsonConverter.kt b/src/main/kotlin/id/walt/credentials/w3c/JsonConverter.kt index 1c64218d..a919b3c0 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/JsonConverter.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/JsonConverter.kt @@ -1,6 +1,7 @@ package id.walt.credentials.w3c import kotlinx.serialization.json.* +import kotlin.reflect.jvm.jvmName object JsonConverter { @@ -9,34 +10,30 @@ object JsonConverter { is Number -> JsonPrimitive(value) is String -> JsonPrimitive(value) is Boolean -> JsonPrimitive(value) - is List<*> -> buildJsonArray { - value.forEach { add(toJsonElement(it)) } - } + null -> JsonNull - is Map<*, *> -> buildJsonObject { - value.keys.forEach { key -> - put(key.toString(), toJsonElement(value[key])) - } - } + is List<*> -> buildJsonArray { value.forEach { add(toJsonElement(it)) } } + is Map<*, *> -> buildJsonObject { value.keys.forEach { put(it.toString(), toJsonElement(value[it])) } } is JsonElement -> value - null -> JsonNull - else -> throw Exception("Json values can only be Number, String, List or Map") + + //else -> JsonNull + else -> throw Exception("Json values can only be Number, String, Boolean, Null, List or Map, not \"${value::class.jvmName}\": toString = $value") } } fun fromJsonElement(element: JsonElement): Any? { return when (element) { - is JsonPrimitive -> if (element.isString) { - element.contentOrNull - } else { - element.booleanOrNull ?: element.longOrNull ?: element.doubleOrNull + is JsonPrimitive -> when { + element.isString -> element.contentOrNull + else -> element.booleanOrNull ?: element.longOrNull ?: element.doubleOrNull } is JsonArray -> element.map { fromJsonElement(it) }.toList() is JsonObject -> element.keys.associateWith { - fromJsonElement(element[it] ?: JsonNull) - } + fromJsonElement(element[it] ?: JsonNull) + } + else -> throw IllegalArgumentException("Invalid JSON element \"${element::class.jvmName}\": $element") } } diff --git a/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt b/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt index e1feb193..129f46ef 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/VerifiableCredential.kt @@ -74,7 +74,10 @@ open class VerifiableCredential internal constructor( } } - fun toJson() = toJsonObject().toString() + fun toJson(): String { + println("To JSON Object: $type $id") + return toJsonObject().toString() + } fun toJsonElement() = jwt?.let { JsonPrimitive(it) } ?: toJsonObject() override fun toString(): String { diff --git a/src/main/kotlin/id/walt/credentials/w3c/W3CCredentialSubject.kt b/src/main/kotlin/id/walt/credentials/w3c/W3CCredentialSubject.kt index 3aec3d85..23f66e5b 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/W3CCredentialSubject.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/W3CCredentialSubject.kt @@ -9,6 +9,7 @@ open class W3CCredentialSubject(var id: String? = null, override val properties: id?.let { put("id", it) } properties.let { props -> props.keys.forEach { key -> + println("Properties key: $key => ${props[key]}") put(key, JsonConverter.toJsonElement(props[key])) } } diff --git a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt index 615f49f7..db7dc308 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt @@ -1,19 +1,18 @@ package id.walt.credentials.w3c.templates +import com.github.benmanes.caffeine.cache.Caffeine import id.walt.credentials.w3c.VerifiableCredential import id.walt.credentials.w3c.W3CIssuer import id.walt.credentials.w3c.toVerifiableCredential import id.walt.services.context.ContextManager import id.walt.services.hkvstore.HKVKey -import io.github.reactivecircus.cache4k.Cache -import kotlinx.coroutines.runBlocking import mu.KotlinLogging import java.io.File import java.nio.file.FileSystems import java.nio.file.Files +import java.time.Duration import kotlin.io.path.isRegularFile import kotlin.io.path.nameWithoutExtension -import kotlin.time.Duration.Companion.hours object VcTemplateManager { private val log = KotlinLogging.logger {} @@ -28,13 +27,13 @@ object VcTemplateManager { return VcTemplate(name, template, true) } - val templateCache = Cache.Builder() - .maximumCacheSize(1000) - .expireAfterWrite(1.hours) + val templateCache = Caffeine.newBuilder() + .maximumSize(1000) + .expireAfterWrite(Duration.ofMinutes(10)) .build() private fun String?.toVcTemplate(name: String, loadTemplate: Boolean, isMutable: Boolean) = - this?.let { VcTemplate(name, if (loadTemplate) it.toVerifiableCredential() else null, isMutable) } + this?.let { VcTemplate(name, if (loadTemplate && it.isNotBlank()) it.toVerifiableCredential() else null, isMutable) } fun loadTemplateFromHkvStore(name: String, loadTemplate: Boolean) = ContextManager.hkvStore.getAsString(HKVKey(SAVED_VC_TEMPLATES_KEY, name)) @@ -49,19 +48,29 @@ object VcTemplateManager { if (it.exists()) it.readText() else null }.toVcTemplate(name, loadTemplate, false) + fun retrieveOrLoadCachedTemplate( + name: String, + loadTemplate: Boolean = true, + runtimeTemplateFolder: String = "/vc-templates-runtime" + ) = templateCache.get(name) { + loadTemplateFromHkvStore(name, loadTemplate) + ?: loadTemplateFromResources(name, loadTemplate) + ?: loadTemplateFromFile(name, loadTemplate, runtimeTemplateFolder) + ?: throw IllegalArgumentException("No template found with name: $name") + } + fun getTemplate( name: String, loadTemplate: Boolean = true, runtimeTemplateFolder: String = "/vc-templates-runtime" - ): VcTemplate = - runBlocking { - templateCache.get(name) { - loadTemplateFromHkvStore(name, true)//.also { println("At $name HKV: $it") } - ?: loadTemplateFromResources(name, true)//.also { println("At $name resources: $it") } - ?: loadTemplateFromFile(name, true, runtimeTemplateFolder)//.also { println("At $name file: $it") } - ?: throw IllegalArgumentException("No template found with name: $name") - } - } + ): VcTemplate { + val cachedTemplate = retrieveOrLoadCachedTemplate(name, loadTemplate, runtimeTemplateFolder) + + return if (cachedTemplate.template == null && loadTemplate) { + templateCache.invalidate(name) + retrieveOrLoadCachedTemplate(name, true, runtimeTemplateFolder) + } else cachedTemplate + } private val resourceWalk = lazy { diff --git a/src/main/kotlin/id/walt/services/did/DidService.kt b/src/main/kotlin/id/walt/services/did/DidService.kt index 76f2fdcd..057a0701 100644 --- a/src/main/kotlin/id/walt/services/did/DidService.kt +++ b/src/main/kotlin/id/walt/services/did/DidService.kt @@ -1,6 +1,7 @@ package id.walt.services.did import com.beust.klaxon.Klaxon +import com.github.benmanes.caffeine.cache.Caffeine import com.nimbusds.jose.jwk.Curve import com.nimbusds.jose.jwk.JWK import com.nimbusds.jose.util.Base64URL @@ -21,7 +22,6 @@ import id.walt.services.key.KeyService import id.walt.services.keystore.KeyType import id.walt.services.vc.JsonLdCredentialService import id.walt.signatory.ProofConfig -import io.github.reactivecircus.cache4k.Cache import io.ipfs.multibase.Multibase import io.ktor.client.plugins.* import io.ktor.client.request.* @@ -40,8 +40,8 @@ import java.nio.charset.StandardCharsets import java.security.KeyFactory import java.security.KeyPair import java.security.spec.X509EncodedKeySpec +import java.time.Duration import java.util.* -import kotlin.time.Duration.Companion.minutes /** @@ -118,18 +118,17 @@ object DidService { } } - val didCache = Cache.Builder() - .maximumCacheSize(1000) - .expireAfterWrite(15.minutes) + private val didCache = Caffeine.newBuilder() + .maximumSize(1000) + .expireAfterWrite(Duration.ofMinutes(10)) .build() - - fun load(did: String): Did = runBlocking { load(DidUrl.from(did)) } - suspend fun load(didUrl: DidUrl): Did = + fun load(did: String): Did = load(DidUrl.from(did)) + fun load(didUrl: DidUrl): Did = didCache.get(didUrl) { Did.decode( - loadDid(didUrl.did) ?: throw IllegalArgumentException("DID $didUrl not found.") - ) ?: throw IllegalArgumentException("DID $didUrl not found.") + loadDid(didUrl.did) ?: throw IllegalArgumentException("DID $didUrl could not be loaded/found.") + ) ?: throw IllegalArgumentException("DID $didUrl could not be decoded.") } fun resolveDidEbsiRaw(did: String): String = runBlocking { @@ -203,7 +202,7 @@ object DidService { } fun loadDidEbsi(did: String): DidEbsi = loadDidEbsi(DidUrl.from(did)) - fun loadDidEbsi(didUrl: DidUrl): DidEbsi = Did.decode(loadDid(didUrl.did)!!)!! as DidEbsi + fun loadDidEbsi(didUrl: DidUrl): DidEbsi = load(didUrl.did) as DidEbsi fun updateDidEbsi(did: DidEbsi) = storeDid(did.id, did.encode()) @@ -659,6 +658,7 @@ object DidService { fun deleteDid(didUrl: String) { loadOrResolveAnyDid(didUrl)?.let { did -> + didCache.invalidate(DidUrl.from(didUrl)) ContextManager.hkvStore.delete(HKVKey("did", "created", didUrl), recursive = true) did.verificationMethod?.forEach { ContextManager.keyStore.delete(it.id) diff --git a/src/main/kotlin/id/walt/signatory/Signatory.kt b/src/main/kotlin/id/walt/signatory/Signatory.kt index 28c729f0..d3583703 100644 --- a/src/main/kotlin/id/walt/signatory/Signatory.kt +++ b/src/main/kotlin/id/walt/signatory/Signatory.kt @@ -150,8 +150,8 @@ class WaltIdSignatory(configurationPath: String) : Signatory() { val credentialBuilder = when (Files.exists(Path.of(templateIdOrFilename))) { true -> Files.readString(Path.of(templateIdOrFilename)).toVerifiableCredential() else -> VcTemplateManager.getTemplate(templateIdOrFilename, true, configuration.templatesFolder).template - }?.let { /*println("Issuing with $it");*/ W3CCredentialBuilder.fromPartial(it) } - ?: throw Exception("Template could not be loaded") + }?.let { W3CCredentialBuilder.fromPartial(it) } + ?: throw Exception("Template could not be loaded: $templateIdOrFilename") return issue(dataProvider?.populate(credentialBuilder, config) ?: credentialBuilder, config, issuer, storeCredential) } @@ -190,12 +190,14 @@ class WaltIdSignatory(configurationPath: String) : Signatory() { return signedVc } - override fun hasTemplateId(templateId: String) = runCatching { VcTemplateManager.getTemplate(templateId, true) }.getOrNull() != null + override fun hasTemplateId(templateId: String) = + runCatching { VcTemplateManager.getTemplate(templateId, false) }.getOrNull() != null override fun listTemplates(): List = VcTemplateManager.listTemplates(configuration.templatesFolder) override fun listTemplateIds() = VcTemplateManager.listTemplates(configuration.templatesFolder).map { it.name } override fun loadTemplate(templateId: String): VerifiableCredential = - VcTemplateManager.getTemplate(templateId, true, configuration.templatesFolder).template!! + VcTemplateManager.getTemplate(templateId, true, configuration.templatesFolder).template + ?: throw IllegalArgumentException("Could not load template \"$templateId\" into WaltSignatory") override fun importTemplate(templateId: String, template: String) { val vc = VerifiableCredential.fromJson(template) diff --git a/src/test/kotlin/id/walt/Performance.kt b/src/test/kotlin/id/walt/Performance.kt index 70021b1d..8f315bce 100644 --- a/src/test/kotlin/id/walt/Performance.kt +++ b/src/test/kotlin/id/walt/Performance.kt @@ -1,5 +1,5 @@ package id.walt - +/* import id.walt.credentials.w3c.W3CIssuer import id.walt.model.DidMethod import id.walt.servicematrix.ServiceMatrix @@ -16,6 +16,7 @@ import kotlinx.coroutines.launch import kotlin.system.measureTimeMillis + class Performance : StringSpec({ ServiceMatrix("service-matrix.properties") val issuerWebDid: String = DidService.create(DidMethod.web) @@ -41,3 +42,4 @@ class Performance : StringSpec({ } }) +*/ diff --git a/src/test/kotlin/id/walt/services/ReadmeTest.kt b/src/test/kotlin/id/walt/services/ReadmeTest.kt index 20c12831..f626aed9 100644 --- a/src/test/kotlin/id/walt/services/ReadmeTest.kt +++ b/src/test/kotlin/id/walt/services/ReadmeTest.kt @@ -28,7 +28,7 @@ class ReadmeTest : StringSpec({ val issuerDid = DidService.create(DidMethod.ebsi) val holderDid = DidService.create(DidMethod.key) - // Issue VC in JSON-LD and JWT format (for show-casing both formats) + // Issue VC with LD_PROOF and JWT format (for show-casing both formats) val vcJson = Signatory.getService().issue( templateIdOrFilename = "VerifiableId", config = ProofConfig(issuerDid = issuerDid, subjectDid = holderDid, proofType = ProofType.LD_PROOF) diff --git a/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt b/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt index 40ffb784..48785ee2 100644 --- a/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt +++ b/src/test/kotlin/id/walt/services/oidc/OIDC4VCTest.kt @@ -183,7 +183,7 @@ class OIDC4VCTest : AnnotationSpec() { @Test fun testVerifyPresentation() { - val credential = Signatory.getService().issue("VerifiableId", ProofConfig(OIDCTestProvider.ISSUER_DID, SUBJECT_DID)) + val credential = Signatory.getService().issue("VerifiableId", ProofConfig(OIDCTestProvider.ISSUER_DID, SUBJECT_DID, proofType = ProofType.LD_PROOF)) val presentation = Custodian.getService().createPresentation(listOf(credential), SUBJECT_DID) .toVerifiablePresentation() val req = OIDC4VPService.createOIDC4VPRequest( @@ -230,7 +230,7 @@ class OIDC4VCTest : AnnotationSpec() { @Test fun testVerifyMultiplePresentations() { - val credential = Signatory.getService().issue("VerifiableId", ProofConfig(OIDCTestProvider.ISSUER_DID, SUBJECT_DID)) + val credential = Signatory.getService().issue("VerifiableId", ProofConfig(OIDCTestProvider.ISSUER_DID, SUBJECT_DID, proofType = ProofType.LD_PROOF)) val presentation = Custodian.getService().createPresentation(listOf(credential), SUBJECT_DID) .toVerifiablePresentation() val req = OIDC4VPService.createOIDC4VPRequest( diff --git a/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt b/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt index 343447ae..615c7344 100644 --- a/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt +++ b/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt @@ -64,13 +64,17 @@ class SignatoryApiTest : AnnotationSpec() { fun testListVcTemplates() = runBlocking { val templates = client.get("$SIGNATORY_API_URL/v1/templates").bodyAsText() + .also { println("BODY: $it") } .let { KlaxonWithConverters().parseArray(it) }!! - .map { it.name } - VcTemplateManager.listTemplates().map { it.name }.forEach { templateName -> templates shouldContain templateName } + val templateNames = templates.map { it.name } - templates shouldContain "Europass" - templates shouldContain "VerifiablePresentation" + templateNames shouldContain "Europass" + templateNames shouldContain "VerifiablePresentation" + + VcTemplateManager.listTemplates().map { it.name }.forEach { templateName -> templateNames shouldContain templateName } + + println(templates) } diff --git a/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt b/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt index 26352a81..01a33e07 100644 --- a/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt +++ b/src/test/kotlin/id/walt/signatory/SignatoryServiceTest.kt @@ -39,7 +39,8 @@ class SignatoryServiceTest : StringSpec({ subjectDid = did, issuerDid = did, issueDate = LocalDateTime.of(2020, 11, 3, 0, 0).toInstant(ZoneOffset.UTC), - issuerVerificationMethod = vm + issuerVerificationMethod = vm, + proofType = ProofType.LD_PROOF ) ) @@ -59,9 +60,7 @@ class SignatoryServiceTest : StringSpec({ println("ISSUING CREDENTIAL...") val jwtStr = signatory.issue( "VerifiableId", ProofConfig( - subjectDid = did, - issuerDid = did, - proofType = ProofType.JWT + subjectDid = did, issuerDid = did, proofType = ProofType.JWT ) ) @@ -89,7 +88,8 @@ class SignatoryServiceTest : StringSpec({ subjectDid = did, issuerDid = did, issueDate = LocalDateTime.of(2020, 11, 3, 0, 0).toInstant(ZoneOffset.UTC), - issuerVerificationMethod = vm + issuerVerificationMethod = vm, + proofType = ProofType.LD_PROOF ) ) @@ -108,7 +108,8 @@ class SignatoryServiceTest : StringSpec({ "Issue and verify: VerifiableDiploma (JWT-Proof)" { println("ISSUING CREDENTIAL...") val jwtStr = signatory.issue( - "VerifiableDiploma", ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.JWT) + "VerifiableDiploma", + ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.JWT) ) println("VC:") @@ -145,8 +146,7 @@ class SignatoryServiceTest : StringSpec({ val builder = W3CCredentialBuilder() val data = mapOf(Pair("credentialSubject", mapOf(Pair("firstName", "Yves")))) val populated = MergingDataProvider(data).populate( - builder, - ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.LD_PROOF) + builder, ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.LD_PROOF) ).build() populated.credentialSubject?.properties?.get("firstName") shouldBe "Yves" @@ -168,9 +168,7 @@ class SignatoryServiceTest : StringSpec({ """.trimIndent() val signedVC = Signatory.getService().issue( - W3CCredentialBuilder - .fromPartial(template) - .setFromJson(userData), + W3CCredentialBuilder.fromPartial(template).setFromJson(userData), ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.LD_PROOF) ) @@ -189,11 +187,10 @@ class SignatoryServiceTest : StringSpec({ "sign any credential with user data from subject builder" { val signedVC = Signatory.getService().issue( - W3CCredentialBuilder() - .buildSubject { - setProperty("firstName", "Inco") - setProperty("familyName", "GNITO") - }, + W3CCredentialBuilder().buildSubject { + setProperty("firstName", "Inco") + setProperty("familyName", "GNITO") + }, ProofConfig(subjectDid = did, issuerDid = did, proofType = ProofType.LD_PROOF), W3CIssuer(did, mapOf("name" to "Test Issuer")) ) diff --git a/src/test/resources/vc-templates/AmletCredential.json b/src/test/resources/vc-templates/AmletCredential.json new file mode 100644 index 00000000..c7488d35 --- /dev/null +++ b/src/test/resources/vc-templates/AmletCredential.json @@ -0,0 +1,44 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/AmletCredential.json", + "type": "JsonSchemaValidator2018" + }, + "credentialSubject": { + "birthDate": "1995-05-03", + "birthPlace": "Milan", + "company": { + "address": "VLE. BACCHIGLIONE 20, MILANO", + "email": "info@mopso.it", + "foundingDate": "2021-01-01", + "hasInternationalActivity": true, + "isRecipientOfPublicFunds": true, + "isRiskSector": false, + "name": "Mopso", + "revenue": "10000", + "ultimateBeneficialOwners": [{ + "address": "Milan, Italy", + "birthDate": "1995-05-03", + "birthPlace": "Milan", + "citizenship": "Italian", + "familyName": "Doe", + "givenName": "Jane", + "isPep": false, + "taxID": "DOEJNA95M43Z330V" + }], + "vatId": "11717750969" + }, + "execRappr": "LEGAL REPRESENTATIVE", + "familyName": "Doe", + "givenName": "Jane", + "id": "did:ebsi:zukSNRQ7Umb2TzbRLNkTGX4#3bf09a67ac1348f4bfe35b7c7fc54fba", + "taxID": "DOEJNA95M43Z330V" + }, + "expirationDate": "2023-03-21T00:00:00Z", + "id": "", + "issued": "2022-03-21T00:00:00Z", + "issuer": "did:ebsi:zkttgPyjddU8JqrkQ9aV5MW#6462224517f04e9d84ba1ea09c82e01c", + "validFrom": "2022-03-21T00:00:00Z", + "issuanceDate": "2022-03-21T00:00:00Z", + "type": ["VerifiableCredential", "AmletCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/DataConsortium.json b/src/test/resources/vc-templates/DataConsortium.json new file mode 100644 index 00000000..1d20af23 --- /dev/null +++ b/src/test/resources/vc-templates/DataConsortium.json @@ -0,0 +1,18 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "id": "1234" + }, + "dataspace": [{ + "id": "Gaia-X AISBL", + "serviceEndpoint": "https://endpoint1.io" + }, { + "id": "Gaia-X XYZ", + "serviceEndpoint": "https://endpoint2.io" + }], + "expirationDate": "2022-01-06T20:38:38Z", + "id": "1234", + "issued": "2022-01-03T20:38:38Z", + "issuer": "did:web:vc.gaia-x.eu:issuer", + "type": ["VerifiableCredential", "DataConsortiumCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/DataSelfDescription.json b/src/test/resources/vc-templates/DataSelfDescription.json new file mode 100644 index 00000000..356056d2 --- /dev/null +++ b/src/test/resources/vc-templates/DataSelfDescription.json @@ -0,0 +1,19 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "dependsOn": ["Pilot004DataService"], + "description": "AIS demonstrates machine learning application use case.", + "hasCertifications": ["ISO_27001", "GDPR_Compliance"], + "hasMarketingImage": "https://www.data-infrastructure.eu/GAIAX/Redaktion/EN/Bilder/UseCases/ai-marketplace-for-product-development.jpg?__blob=normal", + "hasName": "AIS", + "hasVersion": "0.1.0", + "id": "Pilot004AIService", + "providedBy": "GAIA-X", + "type": "Service", + "utilizes": ["ExampleKubernetesService"] + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "type": ["VerifiableCredential", "DataSelfDescription"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/DataServiceOffering.json b/src/test/resources/vc-templates/DataServiceOffering.json new file mode 100644 index 00000000..fc60de79 --- /dev/null +++ b/src/test/resources/vc-templates/DataServiceOffering.json @@ -0,0 +1,37 @@ +{ + "billing": { + "paymentModel": { + "frequenceOfPayment": "daily", + "unit": "EUR", + "value": "0.0" + }, + "price": "0.0", + "unit": "EUR" + }, + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "dependsOn": ["Pilot004DataService"], + "description": "AIS demonstrates machine learning application use case.", + "hasCertifications": ["ISO_27001", "GDPR_Compliance"], + "hasMarketingImage": "https://www.data-infrastructure.eu/Data/Redaktion/EN/Bilder/UseCases/ai-marketplace-for-product-development.jpg?__blob=normal", + "hasName": "AIS", + "hasVersion": "0.1.0", + "id": "Pilot004AIService", + "providedBy": "GAIA-X", + "type": "Service", + "utilizes": ["ExampleKubernetesService"] + }, + "description": "he data contains the basic information to build a transaction graph for later analysis by the safeFBDC algorithm. Is is one of multiple data sources to be analyzed.", + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "keywords": ["Analytics", "AML", "safeFBDC", "finance", "dataset", "graph"], + "modified": "2020-12-02T23:00:00+01:00", + "providedBy": "did:key:z6Mktqg13w3KRBdcqhyjU99ZhHF8FR6tRJACuQzmVcXNiH25", + "provisionType": "E.g., Hybrid", + "serviceModel": "E.g., DaaS", + "serviceTitle": "safeFBDC - Dataset A for safeFBDC AML analysis", + "version": "0.1", + "webAddress": "https://safefbdc.portal.minimal-gaia-x.eu/asset/did:op:348e1a938dB25221031Ed6b465782efCAcBB214E", + "type": ["VerifiableCredential", "DataServiceOffering"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/EbsiAccreditedVerifiableAttestation.json b/src/test/resources/vc-templates/EbsiAccreditedVerifiableAttestation.json new file mode 100644 index 00000000..1ae4443d --- /dev/null +++ b/src/test/resources/vc-templates/EbsiAccreditedVerifiableAttestation.json @@ -0,0 +1,31 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "urn:uuid:58afcb57-81f9-4e8c-817b-01f0597d6801", + "type": [ + "VerifiableCredential", + "VerifiableAttestation", + "AccreditedVerifiableAttestation" + ], + "issuer": "did:ebsi:00001234", + "issuanceDate": "2021-11-01T00:00:00Z", + "validFrom": "2021-11-01T00:00:00Z", + "expirationDate": "2024-06-22T14:11:44Z", + "issued": "2020-06-22T14:11:44Z", + "termsOfUse": [ + { + "id": "https://essif.europa.eu/accreditation/45", + "type": "VerifiableAccreditation" + } + ], + "credentialSubject": { + "id": "did:ebsi:00001235" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/45", + "type": "CredentialsStatusList2020" + }, + "credentialSchema": { + "id": "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/0x4a131e78474b35ca4c93d4904cae9f2013cd53794ce521f6fcfac17ac460c6e5", + "type": "FullJsonSchemaValidator2021" + } +} diff --git a/src/test/resources/vc-templates/EbsiDiplomaVerifiableAccreditation.json b/src/test/resources/vc-templates/EbsiDiplomaVerifiableAccreditation.json new file mode 100644 index 00000000..beb8e83b --- /dev/null +++ b/src/test/resources/vc-templates/EbsiDiplomaVerifiableAccreditation.json @@ -0,0 +1,51 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://essif.europa.eu/schemas/vc/2020/v1" + ], + "id": "https://essif.europa.eu/tsr/53", + "type": [ + "VerifiableCredential", + "VerifiableAttestation", + "VerifiableAccreditation", + "DiplomaVerifiableAccreditation" + ], + "issuer": "did:ebsi:00001234", + "issuanceDate": "2020-06-22T14:11:44Z", + "issued": "2020-06-22T14:11:44Z", + "credentialSubject": { + "id": "did:ebsi:00001235", + "authorizationClaims": { + "accreditationType": "http://data.europa.eu/snb/accreditation/003293d2ce", + "decision": "fully compliant", + "report": ["https://www.example.com/URLtoreport.pdf"], + "limitQFLevel": [ + "http://data.europa.eu/snb/eqf/6", + "http://data.europa.eu/snb/eqf/7" + ], + "limitField": ["http://data.europa.eu/snb/isced-f/0419"], + "limitJurisdiction": [ + "http://publications.europa.eu/resource/authority/atu/NLD" + ], + "reviewDate": "2024-04-22T14:11:44Z" + } + }, + "expirationDate": "2024-06-22T14:11:44Z", + "validFrom": "2021-11-01T00:00:00Z", + "credentialStatus": { + "id": "https://essif.europa.eu/status/45", + "type": "CredentialsStatusList2020" + }, + "credentialSchema": { + "id": "https://essif.europa.eu/tsr-123/verifiableattestation.json", + "type": "FullJsonSchemaValidator2021" + }, + "proof": { + "type": "EidasSeal2019", + "created": "2019-06-22T14:11:44Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "http://uri-to-verification-method", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..TCYt5X", + "proofValue": "BD21J4fdlnBvBA+y6D...fnC8Y=" + } +} diff --git a/src/test/resources/vc-templates/EbsiEuropass.json b/src/test/resources/vc-templates/EbsiEuropass.json new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/vc-templates/EbsiVerifiableAccreditationToAccredit.json b/src/test/resources/vc-templates/EbsiVerifiableAccreditationToAccredit.json new file mode 100644 index 00000000..8b9c7450 --- /dev/null +++ b/src/test/resources/vc-templates/EbsiVerifiableAccreditationToAccredit.json @@ -0,0 +1,40 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "urn:uuid:8568b525-a24e-4bc0-9d97-6a8459ec0130", + "type": [ + "VerifiableCredential", + "VerifiableAttestation", + "VerifiableAccreditation", + "VerifiableAccreditationToAccredit" + ], + "issuer": "did:ebsi:00001234", + "issuanceDate": "2021-11-01T00:00:00Z", + "validFrom": "2021-11-01T00:00:00Z", + "expirationDate": "2024-06-22T14:11:44Z", + "issued": "2020-06-22T14:11:44Z", + "credentialSubject": { + "id": "did:ebsi:00001235", + "accreditedFor": [ + { + "schemaId": "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/0x010110", + "types": [ + "VerifiableCredential", + "VerifiableAttestation", + "DiplomaCredential" + ], + "limitJurisdiction": "https://publications.europa.eu/resource/authority/atu/FIN" + } + ] + }, + "termsOfUse": [ + { + "id": "https://api-test.ebsi.eu/trusted-issuers-registry/../..xyz", + "type": "IssuanceCertificate" + } + ], + "credentialSchema": + { + "id": "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/0x1d7146f8897aa6cd5c59321ea0756ec61277028e4a8b3c13ec1b310ec47e6495", + "type": "FullJsonSchemaValidator2021" + } +} diff --git a/src/test/resources/vc-templates/EbsiVerifiableAttestationGeneric.json b/src/test/resources/vc-templates/EbsiVerifiableAttestationGeneric.json new file mode 100644 index 00000000..dc37148b --- /dev/null +++ b/src/test/resources/vc-templates/EbsiVerifiableAttestationGeneric.json @@ -0,0 +1,26 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "urn:uuid:003a1dd8-a5d2-42ef-8182-e921c0a9f2cd", + "type": ["VerifiableCredential", "VerifiableAttestation"], + "issuer": "did:ebsi:00001234", + "issuanceDate": "2021-11-01T00:00:00Z", + "validFrom": "2021-11-01T00:00:00Z", + "validUntil": "2050-11-01T00:00:00Z", + "expirationDate": "2024-06-22T14:11:44Z", + "issued": "2020-06-22T14:11:44Z", + "credentialSubject": { + "id": "did:ebsi:00001235" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/45", + "type": "AnyExtension" + }, + "credentialSchema": { + "id": "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/0x23039e6356ea6b703ce672e7cfac0b42765b150f63df78e2bd18ae785787f6a2", + "type": "FullJsonSchemaValidator2021" + }, + "termsOfUse": { + "id": "https://essif.europa.eu/accreditation/45", + "type": "AnyExtension" + } +} diff --git a/src/test/resources/vc-templates/EbsiVerifiableAttestationLegal.json b/src/test/resources/vc-templates/EbsiVerifiableAttestationLegal.json new file mode 100644 index 00000000..cd6137ba --- /dev/null +++ b/src/test/resources/vc-templates/EbsiVerifiableAttestationLegal.json @@ -0,0 +1,49 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https:/.europa.eu/schemas/v-id/2020/v1", + "https:/.europa.eu/schemas/eidas/2020/v1" + ], + "id": "urn:did:123456", + "type": ["VerifiableCredential", "VerifiableAttestation"], + "issuer": "did:ebsi:2757945549477fc571663bee12042873fe555b674bd294a3", + "issuanceDate": "2019-06-22T14:11:44Z", + "validFrom": "2019-06-22T14:11:44Z", + "issued": "2019-06-22T14:11:44Z", + "credentialSubject": { + "id": "did:ebsi:2659b154a445434a39d91149ead3bd993cb99fd5e78281b7", + "identifier": [ + { + "schemeID": "SHACL", + "value": "SHACL ID 1", + "id": "http://student.id/41231232" + } + ], + "legalName": "Example Company" + }, + "credentialStatus": { + "id": "https:/europa.eu/status/identity#1dee355d-0432-4910-ac9c-70d89e8d674e", + "type": "CredentialStatusList2020" + }, + "credentialSchema": { + "id": "https:/.europa.eu/tsr-vid/verifiableid1.json", + "type": "FullJsonSchemaValidator2021" + }, + "evidence": [ + { + "id": "https:/.europa.eu/tsr-vid/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d4231", + "type": ["DocumentVerification"], + "verifier": "did:ebsi:2e81454f76775c687694ee6772a17796436768a30e289555", + "evidenceDocument": ["Passport"], + "subjectPresence": "Physical", + "documentPresence": ["Physical"] + } + ], + "proof": { + "type": "EidasSeal2021", + "created": "2019-06-22T14:11:44Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:ebsi:2757945549477fc571663bee12042873fe555b674bd294a3#2368332668", + "jws": "HG21J4fdlnBvBA+y6D...amP7O=" + } +} diff --git a/src/test/resources/vc-templates/EbsiVerifiableAttestationPerson.json b/src/test/resources/vc-templates/EbsiVerifiableAttestationPerson.json new file mode 100644 index 00000000..dc37148b --- /dev/null +++ b/src/test/resources/vc-templates/EbsiVerifiableAttestationPerson.json @@ -0,0 +1,26 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "urn:uuid:003a1dd8-a5d2-42ef-8182-e921c0a9f2cd", + "type": ["VerifiableCredential", "VerifiableAttestation"], + "issuer": "did:ebsi:00001234", + "issuanceDate": "2021-11-01T00:00:00Z", + "validFrom": "2021-11-01T00:00:00Z", + "validUntil": "2050-11-01T00:00:00Z", + "expirationDate": "2024-06-22T14:11:44Z", + "issued": "2020-06-22T14:11:44Z", + "credentialSubject": { + "id": "did:ebsi:00001235" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/45", + "type": "AnyExtension" + }, + "credentialSchema": { + "id": "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/0x23039e6356ea6b703ce672e7cfac0b42765b150f63df78e2bd18ae785787f6a2", + "type": "FullJsonSchemaValidator2021" + }, + "termsOfUse": { + "id": "https://essif.europa.eu/accreditation/45", + "type": "AnyExtension" + } +} diff --git a/src/test/resources/vc-templates/Email.json b/src/test/resources/vc-templates/Email.json new file mode 100644 index 00000000..14310c5f --- /dev/null +++ b/src/test/resources/vc-templates/Email.json @@ -0,0 +1,11 @@ +{ + "context": ["https://www.w3.org/2018/credentials/v1"], + "credentialStatus": { + "id": "https://credentialstatus.velocitycareerlabs.io", + "type": "VelocityRevocationRegistry" + }, + "credentialSubject": { + "email": "adam.smith@example.com" + }, + "type": ["VerifiableCredential", "Email"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/Europass.json b/src/test/resources/vc-templates/Europass.json new file mode 100644 index 00000000..5db9f2ed --- /dev/null +++ b/src/test/resources/vc-templates/Europass.json @@ -0,0 +1,153 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/Europass.json", + "type": "JsonSchemaValidator2018" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/education#higherEducation#51e42fda-cb0a-4333-b6a6-35cb147e1a88", + "type": "TrustedCredentialStatus2021" + }, + "credentialSubject": { + "achieved": [{ + "entitlesTo": { + "definition": "Competences the student acquires after the completion of Graduate university study are sufficient conditions for attending the programme of Postgraduate doctoral study at the Faculty of Civil Engineering, Architecture and Geodesy in Split, as well as for attending the same or similar programmes and Postgraduate specialist studies at other faculties of Civil Engineering in Croatia. The acquired learning outcomes enable the student to attend other postgraduate study programmes in the field of technical sciences. ", + "id": "urn:epass:entitlement:1", + "issuedDate": "2019-09-20", + "specifiedBy": { + "entitlementType": "http://data.europa.eu/snb/entitlement/64aad92881", + "id": "urn:epass:entitlementspec:1", + "limitJurisdiction": ["http://publications.europa.eu/resource/authority/country/HRV"], + "limitOrganisation": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "status": "http://data.europa.eu/snb/entitlement-status/5b8d6b34fb", + "title": "Postgraduate doctoral study" + }, + "title": "Postgraduate doctoral study" + }, + "hasPart": { + "learningAchievements": [{ + "id": "", + "identifier": [{ + "schemeID": "Achievement ID", + "value": "GAB701" + }], + "specifiedBy": [{ + "eCTSCreditPoints": 5, + "eqflLevel": "http://data.europa.eu/snb/eqf/4", + "id": "urn:epass:qualification:1", + "isPartialQualification": true, + "maximumDuration": "P6M", + "nqflLevel": "http://data.europa.eu/snb/qdr/c_49672c5a", + "title": "Applied mathematics", + "volumeOfLearning": "PT60H" + }], + "title": "", + "wasAwardedBy": { + "awardingBody": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "awardingDate": "2019-09-20T00:00:00+02:00", + "id": "urn:epass:awardingProcess:2" + }, + "wasDerivedFrom": [{ + "assessedBy": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "grade": "good (3)", + "id": "urn:epass:asssessmentspec:2", + "specifiedBy": { + "gradingScheme": { + "definition": "The Croatian national grading scheme consists of five grades with numerical equivalents: izvrstan \u2013 5 (outstanding); vrlo dobar \u2013 4 (very good); dobar \u2013 3 (good); dovoljan \u2013 2 (sufficient); nedovoljan \u2013 1 (insufficient - fail). The minimum passing grade is dovoljan \u2013 2.", + "id": "urn:epass:scoringschemespec:2", + "title": "General grading scheme in Croatia" + }, + "id": "urn:epass:asssessmentspec:2", + "title": "Applied mathematics" + }, + "title": "Applied mathematics" + }], + "wasInfluencedBy": [{ + "directedBy": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "endedAtTime": "2018-01-14T00:00:00+01:00", + "id": "urn:epass:activity:1", + "identifier": [{ + "schemeID": "Activity ID", + "value": "GAB701" + }], + "location": ["urn:epass:location:4"], + "specifiedBy": { + "id": "urn:epass:learningactivityspec:1", + "language": ["http://publications.europa.eu/resource/authority/language/HRV"], + "learningActivityType": ["http://data.europa.eu/snb/learning-activity/fd33e234ae"], + "title": "Applied mathematics", + "workload": "PT60H" + }, + "startedAtTime": "2017-09-04T00:00:00+02:00", + "title": "Applied mathematics", + "workload": "PT60H" + }] + }] + }, + "id": "urn:epass:learningAchievement:1", + "specifiedBy": [{ + "eCTSCreditPoints": 120, + "entryRequirementsNote": "The minimum educational requirement for enrolment into graduate university programmes is the completion of an undergraduate university programme. The university can allow students who have completed a professional programme to also enrol graduate university programmes, but they are allowed to set special requirements in these cases.\nThe minimum educational requirement for enrolment into specialist graduate professional programmes is the completion of an undergraduate university programme or a professional programme (first cycle).", + "eqflLevel": "http://data.europa.eu/snb/eqf/5", + "id": "urn:epass:qualification:20", + "isPartialQualification": false, + "maximumDuration": "P21M", + "nqflLevel": "http://data.europa.eu/snb/qdr/c_dcc9aca1", + "title": "Master of Science in Civil Engineering", + "volumeOfLearning": "PT1440H" + }], + "title": "Master of Science in Civil Engineering", + "wasAwardedBy": { + "awardingBody": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "awardingDate": "2019-09-20T00:00:00+02:00", + "id": "urn:epass:awardingProcess:1" + }, + "wasDerivedFrom": [{ + "assessedBy": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "grade": "excellent (5)", + "id": "urn:epass:assessment:1", + "specifiedBy": { + "gradingScheme": { + "definition": "The Croatian national grading scheme consists of five grades with numerical equivalents: izvrstan \u2013 5 (outstanding); vrlo dobar \u2013 4 (very good); dobar \u2013 3 (good); dovoljan \u2013 2 (sufficient); nedovoljan \u2013 1 (insufficient - fail). The minimum passing grade is dovoljan \u2013 2.", + "id": "urn:epass:scoringschemespec:1", + "title": "General grading scheme in Croatia" + }, + "id": "urn:epass:asssessmentspec:1", + "title": "Overall Diploma Assessment" + }, + "title": "Overall Diploma Assessment" + }], + "wasInfluencedBy": [{ + "directedBy": ["did:ebsi:zsSgDXeYPhZ3AuKhTFneDf1"], + "endedAtTime": "2018-01-14T00:00:00+01:00", + "id": "urn:epass:learningAchievement:1", + "identifier": [{ + "schemeID": "Activity ID", + "value": "GAB701" + }], + "location": ["urn:epass:location:4"], + "specifiedBy": { + "id": "urn:epass:learningactivityspec:1", + "language": ["http://publications.europa.eu/resource/authority/language/HRV"], + "learningActivityType": ["http://data.europa.eu/snb/learning-activity/fd33e234ae"], + "title": "Applied mathematics", + "workload": "PT60H" + }, + "startedAtTime": "2017-09-04T00:00:00+02:00", + "title": "Master of Science in Civil Engineering", + "workload": "PT60H" + }] + }], + "id": "did:epass:person:1", + "identifier": { + "schemeID": "Student identification number", + "value": "99009900" + } + }, + "id": "urn:credential:5a4d5412-27e3-4540-a5e5-f1aa4d55b20c", + "issued": "2020-07-20T13:58:53+02:00", + "issuer": "did:epass:org:1", + "validFrom": "2019-09-20T00:00:00+02:00", + "issuanceDate": "2019-09-20T00:00:00+02:00", + "type": ["VerifiableCredential", "VerifiableAttestation", "Europass"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/EuropeanBankIdentity.json b/src/test/resources/vc-templates/EuropeanBankIdentity.json new file mode 100644 index 00000000..44b19019 --- /dev/null +++ b/src/test/resources/vc-templates/EuropeanBankIdentity.json @@ -0,0 +1,23 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/EuropeanBankIdentity.json", + "type": "JsonSchemaValidator2018" + }, + "credentialSubject": { + "birthDate": "1958-08-17", + "familyName": "DOE", + "givenName": "JOHN", + "id": "identity#verifiableID", + "placeOfBirth": { + "country": "DE", + "locality": "Berlin" + } + }, + "id": "identity#EuropeanBankIdentity#3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:example:456", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "EuropeanBankIdentity"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/GaiaxCredential.json b/src/test/resources/vc-templates/GaiaxCredential.json new file mode 100644 index 00000000..c0be4a1e --- /dev/null +++ b/src/test/resources/vc-templates/GaiaxCredential.json @@ -0,0 +1,40 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "DNSpublicKey": "04:8B:CA:33:B1:A1:3A:69:E6:A2:1E:BE:CB:4E:DF:75:A9:70:8B:AA:51:83:AB:A1:B0:5A:35:20:3D:B4:29:09:AD:67:B4:12:19:3B:6A:B5:7C:12:3D:C4:CA:DD:A5:E0:DA:05:1E:5E:1A:4B:D1:F2:BA:8F:07:4D:C7:B6:AA:23:46", + "brandName": "deltaDAO", + "commercialRegister": { + "countryName": "Germany", + "locality": "Hamburg", + "organizationName": "Amtsgericht Hamburg (-Mitte)", + "organizationUnit": "Registergericht", + "postalCode": "20355", + "streetAddress": "Caffamacherreihe 20" + }, + "corporateEmailAddress": "contact@delta-dao.com", + "ethereumAddress": { + "id": "0x4C84a36fCDb7Bc750294A7f3B5ad5CA8F74C4A52" + }, + "id": "did:key:dummy", + "individualContactLegal": "legal@delta-dao.com", + "individualContactTechnical": "support@delta-dao.com", + "jurisdiction": "Germany", + "legalForm": "Stock Company", + "legalRegistrationNumber": "HRB 170364", + "legallyBindingAddress": { + "countryName": "Germany", + "locality": "Hamburg", + "postalCode": "22303", + "streetAddress": "Geibelstr. 46B" + }, + "legallyBindingName": "deltaDAO AG", + "trustState": "trusted", + "webAddress": { + "url": "https://www.delta-dao.com/" + } + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "type": ["VerifiableCredential", "GaiaxCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/Iso27001Certificate.json b/src/test/resources/vc-templates/Iso27001Certificate.json new file mode 100644 index 00000000..f8d1ea60 --- /dev/null +++ b/src/test/resources/vc-templates/Iso27001Certificate.json @@ -0,0 +1,30 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "brandName": "deltaDAO", + "corporateEmailAddress": "contact@delta-dao.com", + "id": "did:key:dummy", + "individualContactLegal": "legal@delta-dao.com", + "individualContactTechnical": "support@delta-dao.com", + "jurisdiction": "Germany", + "legalForm": "Stock Company", + "legalRegistrationNumber": "HRB 170364", + "legallyBindingAddress": { + "countryName": "Germany", + "locality": "Hamburg", + "postalCode": "22303", + "streetAddress": "Geibelstr. 46B" + }, + "legallyBindingName": "deltaDAO AG", + "webAddress": "https://www.delta-dao.com/" + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "iso2700Criteria": { + "certifcateName": "IOS/IEC 27001:2022", + "certificateNr": "123", + "scope": ["consulting service", "software development", "IT operations"] + }, + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "type": ["VerifiableCredential", "VerifiableAttestation", "Iso27001Certificate"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/KybCredential.json b/src/test/resources/vc-templates/KybCredential.json new file mode 100644 index 00000000..9cfc5919 --- /dev/null +++ b/src/test/resources/vc-templates/KybCredential.json @@ -0,0 +1,33 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "brandName": "deltaDAO", + "commercialRegister": { + "countryName": "Germany", + "locality": "Hamburg", + "organizationName": "Amtsgericht Hamburg (-Mitte)", + "organizationUnit": "Registergericht", + "postalCode": "20355", + "streetAddress": "Caffamacherreihe 20" + }, + "corporateEmailAddress": "contact@delta-dao.com", + "id": "did:key:dummy", + "individualContactLegal": "legal@delta-dao.com", + "individualContactTechnical": "support@delta-dao.com", + "jurisdiction": "Germany", + "legalForm": "Stock Company", + "legalRegistrationNumber": "HRB 170364", + "legallyBindingAddress": { + "countryName": "Germany", + "locality": "Hamburg", + "postalCode": "22303", + "streetAddress": "Geibelstr. 46B" + }, + "legallyBindingName": "deltaDAO AG", + "webAddress": "https://www.delta-dao.com/" + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "type": ["VerifiableCredential", "VerifiableAttestation", "KybCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/KybMonoCredential.json b/src/test/resources/vc-templates/KybMonoCredential.json new file mode 100644 index 00000000..c254eca9 --- /dev/null +++ b/src/test/resources/vc-templates/KybMonoCredential.json @@ -0,0 +1,11 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "id": "did:key:dummy", + "kybValidationPassed": true + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:example:456", + "type": ["VerifiableCredential", "VerifiableAttestation", "KybMonoCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/KycCredential.json b/src/test/resources/vc-templates/KycCredential.json new file mode 100644 index 00000000..f95cfee4 --- /dev/null +++ b/src/test/resources/vc-templates/KycCredential.json @@ -0,0 +1,20 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "currentAddress": "1 Boulevard de la Liberté, 59800 Lille", + "dateOfBirth": "1993-04-08", + "familyName": "DOE", + "firstName": "Jane", + "gender": "FEMALE", + "id": "did:ebsi:2AEMAqXWKYMu1JHPAgGcga4dxu7ThgfgN95VyJBJGZbSJUtp", + "nameAndFamilyNameAtBirth": "Jane DOE", + "personalIdentifier": "0904008084H", + "placeOfBirth": "LILLE, FRANCE" + }, + "id": "identity#KycCredential#3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "VerifiableAttestation", "KycCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/LegalPerson.json b/src/test/resources/vc-templates/LegalPerson.json new file mode 100644 index 00000000..0e34bfea --- /dev/null +++ b/src/test/resources/vc-templates/LegalPerson.json @@ -0,0 +1,29 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "gx-participant:blockchainAccountId": "0x4C84a36fCDb7Bc750294A7f3B5ad5CA8F74C4A52", + "gx-participant:headquarterAddress": { + "gx-participant:addressCode": "DE-HH", + "gx-participant:addressCountryCode": "DE", + "gx-participant:postal-code": "22303", + "gx-participant:street-address": "Geibelstraße 46b" + }, + "gx-participant:legalAddress": { + "gx-participant:addressCode": "DE-HH", + "gx-participant:addressCountryCode": "DE", + "gx-participant:postal-code": "22303", + "gx-participant:street-address": "Geibelstraße 46b" + }, + "gx-participant:legalName": "deltaDAO AG", + "gx-participant:registrationNumber": { + "gx-participant:registrationNumberNumber": "391200FJBNU0YW987L26", + "gx-participant:registrationNumberType": "leiCode" + }, + "gx-participant:termsAndConditions": "70c1d713215f95191a11d38fe2341faed27d19e083917bc8732ca4fea4976700", + "id": "did:web:delta-dao.com" + }, + "id": "https://delta-dao.com/.well-known/participant.json", + "issuanceDate": "2022-09-15T20:05:20.997Z", + "issuer": "did:web:delta-dao.com", + "type": ["VerifiableCredential", "LegalPerson"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/OpenBadgeCredential.json b/src/test/resources/vc-templates/OpenBadgeCredential.json new file mode 100644 index 00000000..eb4caf35 --- /dev/null +++ b/src/test/resources/vc-templates/OpenBadgeCredential.json @@ -0,0 +1,40 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1", "https://purl.imsglobal.org/spec/ob/v3p0/context.json"], + "credentialSchema": [{ + "id": "https://purl.imsglobal.org/spec/ob/v3p0/schema/json/ob_v3p0_achievementcredential_schema.json", + "type": "https://json-schema.org/draft/2019-09/schema" + }], + "credentialSubject": { + "achievement": { + "criteria": { + "narrative": "The cohort of the JFF Plugfest 2 in August-November of 2022 collaborated to push interoperability of VCs in education forward.", + "type": "Criteria" + }, + "description": "This wallet can display this Open Badge 3.0", + "id": "0", + "image": { + "id": "https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png", + "type": "Image" + }, + "name": "Our Wallet Passed JFF Plugfest #2 2022", + "type": "Achievement" + }, + "id": "did:jwk:1235667890", + "type": "AchievementSubject" + }, + "id": "urn:uuid:01af1ece-4279-4c1e-b952-469bc5b5bab2", + "issuanceDate": "2020-03-10T04:24:12.164Z", + "issued": "2020-03-10T04:24:12.164Z", + "issuer": { + "id": "did:key:z6MkrHKzgsahxBLyNAbLQyB1pcWNYC9GmywiWPgkrvntAZcj", + "image": { + "id": "https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png", + "type": "Image" + }, + "name": "Jobs for the Future (JFF)", + "type": "Profile", + "url": "https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png" + }, + "name": "Achievement Credential", + "type": ["VerifiableCredential", "OpenBadgeCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/ParticipantCredential.json b/src/test/resources/vc-templates/ParticipantCredential.json new file mode 100644 index 00000000..55895f51 --- /dev/null +++ b/src/test/resources/vc-templates/ParticipantCredential.json @@ -0,0 +1,28 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1", "https://w3id.org/security/suites/ed25519-2020/v1", "https://w3id.org/security/suites/jws-2020/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/ParticipantCredential.json", + "type": "JsonSchemaValidator2018" + }, + "credentialStatus": { + "id": "https://gaiax.europa.eu/status/participant-credential#392ac7f6-399a-437b-a268-4691ead8f176", + "type": "CredentialStatusList2020" + }, + "credentialSubject": { + "ethereumAddress": "0x4C84a36fCDb7Bc750294A7f3B5ad5CA8F74C4A52", + "hasCountry": "GER", + "hasJurisdiction": "GER", + "hasLegallyBindingName": "deltaDAO AG", + "hasRegistrationNumber": "DEK1101R.HRB170364", + "hash": "9ecf754ffdad0c6de238f60728a90511780b2f7dbe2f0ea015115515f3f389cd", + "id": "did:web:delta-dao.com", + "leiCode": "391200FJBNU0YW987L26", + "parentOrganisation": "", + "subOrganisation": "" + }, + "expirationDate": "2022-01-06T20:38:38Z", + "id": "vc.gaia-x.eu/participant-credential#392ac7f6-399a-437b-a268-4691ead8f176", + "issued": "2022-01-03T20:38:38Z", + "issuer": "did:web:vc.gaia-x.eu:issuer", + "type": ["VerifiableCredential", "ParticipantCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/PeerReview.json b/src/test/resources/vc-templates/PeerReview.json new file mode 100644 index 00000000..88c0dabe --- /dev/null +++ b/src/test/resources/vc-templates/PeerReview.json @@ -0,0 +1,32 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/serialized/PeerReview.json", + "type": "JsonSchemaValidator2018" + }, + "credentialSubject": { + "completedDate": "2021-08-31T00:00:00Z", + "groupingKey": "c4235de3-e35b-4a53-ac46-f62c2b222441", + "id": "identity#verifiableID", + "measurements": [{ + "indicators": [{ + "category": "SKILL", + "name": "Coaching", + "type": "People Skills" + }], + "name": "Coaching", + "numberValue": 87, + "scale": "PERCENTAGE", + "type": "People Skills" + }], + "name": "WorkPi Peer Review", + "providerName": "WorkPi", + "type": "Assessment" + }, + "id": "identity#PeerReview#3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:example:456", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "PeerReview"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/PermanentResidentCard.json b/src/test/resources/vc-templates/PermanentResidentCard.json new file mode 100644 index 00000000..3de9b456 --- /dev/null +++ b/src/test/resources/vc-templates/PermanentResidentCard.json @@ -0,0 +1,11 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1", "https://w3id.org/citizenship/v1"], + "credentialSubject": { + "birthDate": "1958-08-17", + "givenName": "JOHN", + "id": "did:example:123", + "type": ["PermanentResident", "Person"] + }, + "issuer": "did:example:456", + "type": ["VerifiableCredential", "PermanentResidentCard"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/ProofOfResidence.json b/src/test/resources/vc-templates/ProofOfResidence.json new file mode 100644 index 00000000..ed98aa65 --- /dev/null +++ b/src/test/resources/vc-templates/ProofOfResidence.json @@ -0,0 +1,43 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/ProofOfResidence.json", + "type": "JsonSchemaValidator2018" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/identity#verifiableID#1dee355d-0432-4910-ac9c-70d89e8d674e", + "type": "CredentialStatusList2020" + }, + "credentialSubject": { + "address": { + "countryName": "LU", + "locality": "Steinfort", + "postalCode": "L-8410", + "streetAddress": "16 Route D' Arlon" + }, + "dateOfBirth": "1993-04-08", + "familyName": "Beron", + "familyStatus": "Single", + "firstNames": "Domink", + "gender": "Male", + "id": "id123", + "identificationNumber": "123456789", + "nationality": "AT" + }, + "evidence": [{ + "documentPresence": "Physical", + "evidenceDocument": "Passport", + "id": "https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678", + "subjectPresence": "Physical", + "type": ["DocumentVerification"], + "verifier": "did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a" + }], + "expirationDate": "2022-06-22T14:11:44Z", + "id": "residence#3fea53a4-0432-4910-ac9c-69ah8da3c37f", + "issued": "2019-06-22T14:11:44Z", + "issuer": "did:ebsi:2757945549477fc571663bee12042873fe555b674bd294a3", + "title": "Proof of Residence", + "validFrom": "2019-06-22T14:11:44Z", + "issuanceDate": "2019-06-22T14:11:44Z", + "type": ["VerifiableCredential", "VerifiableAttestation", "ProofOfResidence"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/ServiceOfferingCredential.json b/src/test/resources/vc-templates/ServiceOfferingCredential.json new file mode 100644 index 00000000..7ab35790 --- /dev/null +++ b/src/test/resources/vc-templates/ServiceOfferingCredential.json @@ -0,0 +1,27 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "gx-service-offering:dataExport": [{ + "gx-service-offering:accessType": "digital", + "gx-service-offering:formatType": "mime/png", + "gx-service-offering:requestType": "emails" + }], + "gx-service-offering:dataProtectionRegime": ["GDPR2016"], + "gx-service-offering:dependsOn": ["https://compliance.gaia-x.eu/.well-known/serviceManagedPostgreSQLOVH.json", "https://compliance.gaia-x.eu/.well-known/serviceManagedK8sOVH.json"], + "gx-service-offering:description": "The Compliance Service will validate the shape and content of Self Descriptions. Required fields and consistency rules are defined in the Gaia-X Trust Framework.", + "gx-service-offering:gdpr": [{ + "gx-service-offering:imprint": "https://gaia-x.eu/imprint/", + "gx-service-offering:privacyPolicy": "https://gaia-x.eu/privacy-policy/" + }], + "gx-service-offering:name": "Gaia-X Lab Compliance Service", + "gx-service-offering:providedBy": "https://compliance.gaia-x.eu/.well-known/participant.json", + "gx-service-offering:termsAndConditions": [{ + "gx-service-offering:hash": "myrandomhash", + "gx-service-offering:url": "https://compliance.gaia-x.eu/terms" + }], + "gx-service-offering:webAddress": "https://compliance.gaia-x.eu/", + "id": "https://compliance.gaia-x.eu/.well-known/serviceComplianceService.json" + }, + "id": "https://compliance.gaia-x.eu/.well-known/serviceComplianceService.json", + "type": ["VerifiableCredential", "ServiceOfferingExperimental"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/UniversityDegree.json b/src/test/resources/vc-templates/UniversityDegree.json new file mode 100644 index 00000000..0a3f5358 --- /dev/null +++ b/src/test/resources/vc-templates/UniversityDegree.json @@ -0,0 +1,16 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"], + "credentialSubject": { + "degree": { + "name": "Bachelor of Science and Arts", + "type": "BachelorDegree" + }, + "id": "did:example:123" + }, + "id": "http://example.gov/credentials/3732", + "issued": "2020-03-10T04:24:12.164Z", + "issuer": { + "id": "did:example:456" + }, + "type": ["VerifiableCredential", "UniversityDegreeCredential"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/VerifiableAttestation.json b/src/test/resources/vc-templates/VerifiableAttestation.json new file mode 100644 index 00000000..0a8aefbb --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableAttestation.json @@ -0,0 +1,29 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/VerifiableAttestation.json", + "type": "JsonSchemaValidator2018" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/identity#verifiableID#1dee355d-0432-4910-ac9c-70d89e8d674e", + "type": "CredentialStatusList2020" + }, + "credentialSubject": { + "id": "id123" + }, + "evidence": [{ + "documentPresence": "Physical", + "evidenceDocument": "Passport", + "id": "https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678", + "subjectPresence": "Physical", + "type": ["DocumentVerification"], + "verifier": "did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a" + }], + "expirationDate": "2022-06-22T14:11:44Z", + "id": "education#higherEducation#3fea53a4-0432-4910-ac9c-69ah8da3c37f", + "issued": "2019-06-22T14:11:44Z", + "issuer": "did:ebsi:2757945549477fc571663bee12042873fe555b674bd294a3", + "validFrom": "2019-06-22T14:11:44Z", + "issuanceDate": "2019-06-22T14:11:44Z", + "type": ["VerifiableCredential", "VerifiableAttestation"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/VerifiableAuthorization.json b/src/test/resources/vc-templates/VerifiableAuthorization.json new file mode 100644 index 00000000..f0ac6370 --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableAuthorization.json @@ -0,0 +1,13 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "id": "did:ebsi:00000004321", + "naturalPerson": { + "did": "did:example:00001111" + } + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:ebsi:000001234", + "type": ["VerifiableCredential", "VerifiableAuthorization"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/VerifiableDiploma.json b/src/test/resources/vc-templates/VerifiableDiploma.json new file mode 100644 index 00000000..619ed77d --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableDiploma.json @@ -0,0 +1,64 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/VerifiableDiploma.json", + "type": "JsonSchemaValidator2018" + }, + "credentialStatus": { + "id": "https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176", + "type": "CredentialStatusList2020" + }, + "credentialSubject": { + "awardingOpportunity": { + "awardingBody": { + "eidasLegalIdentifier": "Unknown", + "homepage": "https://leaston.bcdiploma.com/", + "id": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "preferredName": "Leaston University", + "registration": "0597065J" + }, + "endedAtTime": "2020-06-26T00:00:00Z", + "id": "https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity", + "identifier": "https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ", + "location": "FRANCE", + "startedAtTime": "2019-09-02T00:00:00Z" + }, + "dateOfBirth": "1993-04-08", + "familyName": "DOE", + "givenNames": "Jane", + "gradingScheme": { + "id": "https://leaston.bcdiploma.com/law-economics-management#GradingScheme", + "title": "Lower Second-Class Honours" + }, + "id": "did:ebsi:2AEMAqXWKYMu1JHPAgGcga4dxu7ThgfgN95VyJBJGZbSJUtp", + "identifier": "0904008084H", + "learningAchievement": { + "additionalNote": ["DISTRIBUTION MANAGEMENT"], + "description": "MARKETING AND SALES", + "id": "https://leaston.bcdiploma.com/law-economics-management#LearningAchievment", + "title": "MASTERS LAW, ECONOMICS AND MANAGEMENT" + }, + "learningSpecification": { + "ectsCreditPoints": 120, + "eqfLevel": 7, + "id": "https://leaston.bcdiploma.com/law-economics-management#LearningSpecification", + "iscedfCode": ["7"], + "nqfLevel": ["7"] + } + }, + "evidence": { + "documentPresence": ["Physical"], + "evidenceDocument": ["Passport"], + "id": "https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678", + "subjectPresence": "Physical", + "type": ["DocumentVerification"], + "verifier": "did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a" + }, + "expirationDate": "2022-08-31T00:00:00Z", + "id": "education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "VerifiableAttestation", "VerifiableDiploma"] +} diff --git a/src/test/resources/vc-templates/VerifiableId.json b/src/test/resources/vc-templates/VerifiableId.json new file mode 100644 index 00000000..b8b4aa04 --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableId.json @@ -0,0 +1,31 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/VerifiableId.json", + "type": "FullJsonSchemaValidator2021" + }, + "credentialSubject": { + "currentAddress": ["1 Boulevard de la Liberté, 59800 Lille"], + "dateOfBirth": "1993-04-08", + "familyName": "DOE", + "firstName": "Jane", + "gender": "FEMALE", + "id": "did:ebsi:2AEMAqXWKYMu1JHPAgGcga4dxu7ThgfgN95VyJBJGZbSJUtp", + "nameAndFamilyNameAtBirth": "Jane DOE", + "personalIdentifier": "0904008084H", + "placeOfBirth": "LILLE, FRANCE" + }, + "evidence": [{ + "documentPresence": ["Physical"], + "evidenceDocument": ["Passport"], + "subjectPresence": "Physical", + "type": ["DocumentVerification"], + "verifier": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN" + }], + "id": "urn:uuid:3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "VerifiableAttestation", "VerifiableId"] +} diff --git a/src/test/resources/vc-templates/VerifiableMandate.json b/src/test/resources/vc-templates/VerifiableMandate.json new file mode 100644 index 00000000..0bffcb4c --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableMandate.json @@ -0,0 +1,28 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSchema": { + "id": "https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xb77f8516a965631b4f197ad54c65a9e2f9936ebfb76bae4906d33744dbcc60ba", + "type": "FullJsonSchemaValidator2021" + }, + "credentialSubject": { + "holder": { + "constraints": {}, + "grant": "apply_to_masters", + "id": "", + "role": "family" + }, + "id": "", + "policySchemaURI": "https://raw.githubusercontent.com/walt-id/waltid-ssikit/master/src/test/resources/verifiable-mandates/test-policy.rego" + }, + "evidence": [{ + "evidenceValue": "", + "id": "https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678", + "type": ["VerifiableMandatePresentation"] + }], + "id": "urn:uuid:3add94f4-28ec-42a1-8704-4e4aa51006b4", + "issued": "2021-08-31T00:00:00Z", + "issuer": "did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN", + "validFrom": "2021-08-31T00:00:00Z", + "issuanceDate": "2021-08-31T00:00:00Z", + "type": ["VerifiableCredential", "VerifiableMandate"] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/VerifiablePresentation.json b/src/test/resources/vc-templates/VerifiablePresentation.json new file mode 100644 index 00000000..db95674d --- /dev/null +++ b/src/test/resources/vc-templates/VerifiablePresentation.json @@ -0,0 +1,43 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "holder": "did:ebsi:00000004321", + "id": "id", + "type": ["VerifiablePresentation"], + "verifiableCredential": [{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "credentialSubject": { + "id": "did:ebsi:00000004321", + "naturalPerson": { + "did": "did:example:00001111" + } + }, + "id": "did:ebsi-eth:00000001/credentials/1872", + "issued": "2020-08-24T14:13:44Z", + "issuer": "did:ebsi:000001234", + "proof": { + "created": "assertionMethod", + "creator": "2020-08-24T14:13:44Z", + "domain": "did:ebsi-eth:000001234#key-1", + "proofPurpose": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19.", + "type": "EcdsaSecp256k1Signature2019" + }, + "type": ["VerifiableCredential", "VerifiableAuthorization"] + }, { + "@context": ["https://www.w3.org/2018/credentials/v1", "https://w3id.org/citizenship/v1"], + "credentialSubject": { + "birthDate": "1958-08-17", + "givenName": "JOHN", + "id": "did:example:123", + "type": ["PermanentResident", "Person"] + }, + "issuer": "did:example:456", + "proof": { + "created": "assertionMethod", + "creator": "2020-04-22T10:37:22Z", + "domain": "did:example:456#key-1", + "proofPurpose": "eyJjcml0IjpbImI2NCJdLCJiNjQiOmZhbHNlLCJhbGciOiJFZERTQSJ9..BhWew0x-txcroGjgdtK-yBCqoetg9DD9SgV4245TmXJi-PmqFzux6Cwaph0r-mbqzlE17yLebjfqbRT275U1AA", + "type": "Ed25519Signature2018" + }, + "type": ["VerifiableCredential", "PermanentResidentCard"] + }] +} \ No newline at end of file diff --git a/src/test/resources/vc-templates/VerifiableVaccinationCertificate.json b/src/test/resources/vc-templates/VerifiableVaccinationCertificate.json new file mode 100644 index 00000000..ddc88964 --- /dev/null +++ b/src/test/resources/vc-templates/VerifiableVaccinationCertificate.json @@ -0,0 +1 @@ +{"@context" : ["https://www.w3.org/2018/credentials/v1"], "credentialSchema" : {"id" : "https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/VerifiableVaccinationCertificate.json", "type" : "JsonSchemaValidator2018"}, "credentialStatus" : {"id" : "https://essif.europa.eu/status/covidvaccination#392ac7f6-399a-437b-a268-4691ead8f176", "type" : "CredentialStatusList2020"}, "credentialSubject" : {"dateOfBirth" : "1993-04-08", "familyName" : "DOE", "givenNames" : "Jane", "id" : "asdf", "personIdentifier" : "optional The type of identifier and identifier of the person, according to the policies applicable in each country. Examples are citizen ID and/or document number (ID- card/passport) or identifier within the health system/IIS/e-registry.", "personSex" : "optional", "uniqueCertificateIdentifier" : "UVCI0904008084H", "vaccinationProphylaxisInformation" : [{"administeringCentre" : "Name/code of administering centre or a health authority responsible for the vaccination event", "batchNumber" : "optional 1234", "countryOfVaccination" : "DE", "dateOfVaccination" : "2021-02-12", "diseaseOrAgentTargeted" : {"code" : "840539006", "system" : "2.16.840.1.113883. 6.96", "version" : "2021-01-31"}, "doseNumber" : "1", "marketingAuthorizationHolder" : "Example Vaccine Manufacturing Company", "nextVaccinationDate" : "optional - 2021-03-28", "totalSeriesOfDoses" : "2", "vaccineMedicinalProduct" : "VACCINE concentrate for dispersion for injection", "vaccineOrProphylaxis" : "1119349007 COVID-19 example vaccine"}]}, "evidence" : {"documentPresence" : ["Physical"], "evidenceDocument" : ["Passport"], "id" : "https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678", "subjectPresence" : "Physical", "type" : ["DocumentVerification"], "verifier" : "did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a"}, "expirationDate" : "2022-08-31T00:00:00Z", "id" : "covidvaccination#392ac7f6-399a-437b-a268-4691ead8f176", "issued" : "2021-08-31T00:00:00Z", "issuer" : "qwer", "validFrom" : "2021-08-31T00:00:00Z", "issuanceDate" : "2021-08-31T00:00:00Z", "type" : ["VerifiableCredential", "VerifiableAttestation", "VerifiableVaccinationCertificate"]} \ No newline at end of file From a78e63edb90ae9645f790a68fd17b569c107ebf1 Mon Sep 17 00:00:00 2001 From: severinstampler Date: Thu, 16 Mar 2023 11:48:27 +0100 Subject: [PATCH 5/5] fix load template api test fix loading templates from cache if loadTemplate = false --- .../credentials/w3c/templates/VcTemplate.kt | 3 +- .../w3c/templates/VcTemplateManager.kt | 43 ++++++++++++------- .../signatory/rest/SignatoryController.kt | 3 +- .../id/walt/signatory/SignatoryApiTest.kt | 6 ++- 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplate.kt b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplate.kt index 04508cce..b49b0492 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplate.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplate.kt @@ -1,9 +1,10 @@ package id.walt.credentials.w3c.templates +import id.walt.common.SingleVCObject import id.walt.credentials.w3c.VerifiableCredential data class VcTemplate( val name: String, - val template: VerifiableCredential? = null, + @SingleVCObject val template: VerifiableCredential? = null, val mutable: Boolean ) diff --git a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt index db7dc308..096040a9 100644 --- a/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt +++ b/src/main/kotlin/id/walt/credentials/w3c/templates/VcTemplateManager.kt @@ -32,41 +32,52 @@ object VcTemplateManager { .expireAfterWrite(Duration.ofMinutes(10)) .build() - private fun String?.toVcTemplate(name: String, loadTemplate: Boolean, isMutable: Boolean) = - this?.let { VcTemplate(name, if (loadTemplate && it.isNotBlank()) it.toVerifiableCredential() else null, isMutable) } + private fun String?.toVcTemplate(name: String, populateTemplate: Boolean, isMutable: Boolean) = + this?.let { VcTemplate(name, if (populateTemplate && it.isNotBlank()) it.toVerifiableCredential() else null, isMutable) } fun loadTemplateFromHkvStore(name: String, loadTemplate: Boolean) = ContextManager.hkvStore.getAsString(HKVKey(SAVED_VC_TEMPLATES_KEY, name)) .toVcTemplate(name, loadTemplate, true) - fun loadTemplateFromResources(name: String, loadTemplate: Boolean) = + fun loadTemplateFromResources(name: String, populateTemplate: Boolean) = object {}.javaClass.getResource("/vc-templates/$name.json")?.readText() - .toVcTemplate(name, loadTemplate, false) + .toVcTemplate(name, populateTemplate, false) - fun loadTemplateFromFile(name: String, loadTemplate: Boolean, runtimeTemplateFolder: String) = + fun loadTemplateFromFile(name: String, populateTemplate: Boolean, runtimeTemplateFolder: String) = File("$runtimeTemplateFolder/$name.json").let { if (it.exists()) it.readText() else null - }.toVcTemplate(name, loadTemplate, false) + }.toVcTemplate(name, populateTemplate, false) + + fun loadTemplate(name: String, populateTemplate: Boolean, runtimeTemplateFolder: String) = loadTemplateFromHkvStore(name, populateTemplate) + ?: loadTemplateFromResources(name, populateTemplate) + ?: loadTemplateFromFile(name, populateTemplate, runtimeTemplateFolder) + ?: throw IllegalArgumentException("No template found with name: $name") fun retrieveOrLoadCachedTemplate( name: String, - loadTemplate: Boolean = true, + populateTemplate: Boolean = true, runtimeTemplateFolder: String = "/vc-templates-runtime" - ) = templateCache.get(name) { - loadTemplateFromHkvStore(name, loadTemplate) - ?: loadTemplateFromResources(name, loadTemplate) - ?: loadTemplateFromFile(name, loadTemplate, runtimeTemplateFolder) - ?: throw IllegalArgumentException("No template found with name: $name") - } + ) = if(populateTemplate) { + // only add to cache, if template is populated + templateCache.get(name) { + loadTemplate(name, true, runtimeTemplateFolder) + } + } else { + // try to get from cache or load unpopulated template + (templateCache.getIfPresent(name) ?: loadTemplate(name, false, runtimeTemplateFolder)).let { + // reset populated template, in case it was loaded from cache + VcTemplate(it.name, null, it.mutable) + } + } fun getTemplate( name: String, - loadTemplate: Boolean = true, + populateTemplate: Boolean = true, runtimeTemplateFolder: String = "/vc-templates-runtime" ): VcTemplate { - val cachedTemplate = retrieveOrLoadCachedTemplate(name, loadTemplate, runtimeTemplateFolder) + val cachedTemplate = retrieveOrLoadCachedTemplate(name, populateTemplate, runtimeTemplateFolder) - return if (cachedTemplate.template == null && loadTemplate) { + return if (cachedTemplate.template == null && populateTemplate) { templateCache.invalidate(name) retrieveOrLoadCachedTemplate(name, true, runtimeTemplateFolder) } else cachedTemplate diff --git a/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt b/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt index fa735f4a..51924c8b 100644 --- a/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt +++ b/src/main/kotlin/id/walt/signatory/rest/SignatoryController.kt @@ -1,5 +1,6 @@ package id.walt.signatory.rest +import id.walt.common.KlaxonWithConverters import id.walt.credentials.w3c.JsonConverter import id.walt.credentials.w3c.VerifiableCredential import id.walt.credentials.w3c.builder.W3CCredentialBuilder @@ -24,7 +25,7 @@ object SignatoryController { val signatory = Signatory.getService() fun listTemplates(ctx: Context) { - ctx.json(signatory.listTemplates()) + ctx.contentType(ContentType.APPLICATION_JSON).result(KlaxonWithConverters().toJsonString(signatory.listTemplates())) } fun listTemplatesDocs() = document().operation { diff --git a/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt b/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt index 615c7344..b67f6c76 100644 --- a/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt +++ b/src/test/kotlin/id/walt/signatory/SignatoryApiTest.kt @@ -13,6 +13,7 @@ import id.walt.signatory.rest.IssueCredentialRequest import id.walt.signatory.rest.SignatoryRestAPI import id.walt.test.RESOURCES_PATH import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.inspectors.shouldForAll import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe @@ -66,7 +67,10 @@ class SignatoryApiTest : AnnotationSpec() { client.get("$SIGNATORY_API_URL/v1/templates").bodyAsText() .also { println("BODY: $it") } .let { KlaxonWithConverters().parseArray(it) }!! - + // make sure templates are not populated + templates.forEach { + it.template shouldBe null + } val templateNames = templates.map { it.name } templateNames shouldContain "Europass"