diff --git a/.github/workflows/android-sdk-tests.yml b/.github/workflows/android-sdk-tests.yml new file mode 100644 index 00000000..8e2a3c18 --- /dev/null +++ b/.github/workflows/android-sdk-tests.yml @@ -0,0 +1,33 @@ +name: WalletAndroid-SDK-Tests + +on: + pull_request: + branches: + - main + - rc + - dev +env: + PACKAGE_TYPE: "aar" + GRADLE_VERSION: "7.5" + JAVA_VERSION: "17" + ARTIFACT_PATH: "VCL/build/outputs" + ANDROID_NEXUS_SIGNING_KEY_ID: ${{ secrets.ANDROID_NEXUS_SIGNING_KEY_ID }} + ANDROID_NEXUS_SIGNING_PWD: ${{ secrets.ANDROID_NEXUS_SIGNING_PWD }} + ANDROID_NEXUS_OSSRH_USERNAME: ${{ secrets.ANDROID_NEXUS_OSSRH_USERNAME }} + ANDROID_NEXUS_OSSRH_PWD: ${{ secrets.ANDROID_NEXUS_OSSRH_PWD }} + ANDROID_NEXUS_STAGING_PROFILE_ID: ${{ secrets.ANDROID_NEXUS_STAGING_PROFILE_ID }} + ANDROID_NEXUS_PRIVATE_KEY: ${{ secrets.ANDROID_NEXUS_PRIVATE_KEY }} +jobs: + test-android-sdk: + runs-on: ubuntu-latest + steps: + - name: Git clone repository + uses: actions/checkout@v4 + # Setup java + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: ${{ env.JAVA_VERSION }} + # Run test + - name: Run Test + run: ./gradlew test \ No newline at end of file diff --git a/.github/workflows/android-sdk.yml b/.github/workflows/android-sdk.yml new file mode 100644 index 00000000..0b7c486c --- /dev/null +++ b/.github/workflows/android-sdk.yml @@ -0,0 +1,83 @@ +name: WalletAndroid-SDK + +on: + push: + branches: + - main + - rc + - dev +env: + PACKAGE_TYPE: "aar" + GRADLE_VERSION: "7.5" + JAVA_VERSION: "17" + ARTIFACT_PATH: "VCL/build/outputs" + ANDROID_NEXUS_SIGNING_KEY_ID: ${{ secrets.ANDROID_NEXUS_SIGNING_KEY_ID }} + ANDROID_NEXUS_SIGNING_PWD: ${{ secrets.ANDROID_NEXUS_SIGNING_PWD }} + ANDROID_NEXUS_OSSRH_USERNAME: ${{ secrets.ANDROID_NEXUS_OSSRH_USERNAME }} + ANDROID_NEXUS_OSSRH_PWD: ${{ secrets.ANDROID_NEXUS_OSSRH_PWD }} + ANDROID_NEXUS_STAGING_PROFILE_ID: ${{ secrets.ANDROID_NEXUS_STAGING_PROFILE_ID }} + ANDROID_NEXUS_PRIVATE_KEY: ${{ secrets.ANDROID_NEXUS_PRIVATE_KEY }} +jobs: + test-android-sdk: + runs-on: ubuntu-latest + steps: + - name: Git clone repository + uses: actions/checkout@v4 + # Setup java + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: ${{ env.JAVA_VERSION }} + # Run test + - name: Run Test + run: ./gradlew test + + build-android-sdk: + runs-on: ubuntu-latest + needs: + - test-android-sdk + steps: + - name: Git clone repository + uses: actions/checkout@v4 + # Setup java + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: ${{ env.JAVA_VERSION }} + # Gradle Assemble + - name: Gradle Assemble + run: ./gradlew assemble${{ env.RELEASE_TAG }} + env: + RELEASE_TAG: ${{ github.ref == 'refs/heads/main' && format('{0}', 'Release' ) || 'Rc' }} + # Convert artifact to lowercase + - name: Rename artifact to lowercase + run: mv ${{ env.ARTIFACT_PATH}}/${{ env.PACKAGE_TYPE }}/VCL-${{ env.RELEASE_TAG }}.${{ env.PACKAGE_TYPE }} ${{ env.ARTIFACT_PATH }}/${{ env.PACKAGE_TYPE }}/vcl-${{ env.RELEASE_TAG }}.${{ env.PACKAGE_TYPE }} + env: + RELEASE_TAG: ${{ github.ref == 'refs/heads/main' && format('{0}', 'release' ) || 'rc' }} + # Show Me Builded packages + - name: Show Me Builded packages + run: ls -al ${{ env.ARTIFACT_PATH}}/${{ env.PACKAGE_TYPE }}/ + # Publish Main/Rc + - name: Publish Main/Rc + if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' }} + run: ./gradlew publish${{ env.RELEASE_TAG }} + env: + RELEASE_TAG: ${{ github.ref == 'refs/heads/main' && format('{0}', 'Release' ) || 'Rc' }} + # Upload Artifacts + - name: Upload Artifact + if: success() + uses: actions/upload-artifact@v3 + with: + name: vcl-${{ env.RELEASE_TAG }}.${{ env.PACKAGE_TYPE }} + path: ${{ env.ARTIFACT_PATH }}/${{ env.PACKAGE_TYPE }} + if-no-files-found: error + retention-days: 1 + env: + RELEASE_TAG: ${{ github.ref == 'refs/heads/main' && format('{0}', 'release' ) || 'rc' }} + + + + + + + diff --git a/VCL/build.gradle b/VCL/build.gradle index 6023dbe2..17d163e7 100644 --- a/VCL/build.gradle +++ b/VCL/build.gradle @@ -13,8 +13,8 @@ android { defaultConfig { minSdk 24 targetSdk 33 - versionName "1.20.1" - versionCode 115 + versionName "1.23.0" + versionCode 119 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } @@ -41,6 +41,13 @@ android { shrinkResources false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } + rc { + buildConfigField "String", "VERSION_NAME", "\"${defaultConfig.versionName}-rc\"" + buildConfigField "int", "VERSION_CODE", "${defaultConfig.versionCode}" + minifyEnabled false + shrinkResources false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } } compileOptions { @@ -96,6 +103,54 @@ artifacts { afterEvaluate { publishing { publications { + rc(MavenPublication) { + groupId getGroupId() + artifactId getArtifactId() + + // define rc version name + def vers = getVersionName() + def rcSuffix = rootProject.ext.rcSuffix + version = "${vers}-${rcSuffix}" + + // Two artifacts, the `aar` (or `jar`) and the sources + if (project.plugins.findPlugin("com.android.library")) { + from components.release + } else { + from components.java + } + + artifact androidSourcesJar + + // Mostly self-explanatory metadata + pom { + name = getArtifactId() + // Library description + description = 'Velocity Career Labs Android library' + // Library Github URL + url = 'https://github.com/velocitycareerlabs/WalletAndroid' + licenses { + license { + name = 'Apache License 2.0' + // Library license + url = 'https://github.com/velocitycareerlabs/WalletAndroid/blob/dev/VCL/LICENSE' + } + } + developers { + developer { + id = 'velocitycareerlabs' + name = 'Michael Avoyan' + email = 'michael.avoyan@velocitycareerlabs.com' + } + } + + scm { + // Library info on Github + connection = 'scm:github.com/velocitycareerlabs/WalletAndroid.git' + developerConnection = 'scm:git:ssh://github.com/velocitycareerlabs/WalletAndroid.git' + url = 'https://github.com/velocitycareerlabs/WalletAndroid' + } + } + } release(MavenPublication) { groupId getGroupId() artifactId getArtifactId() @@ -142,15 +197,14 @@ afterEvaluate { } } } -} - -signing { - useInMemoryPgpKeys( - rootProject.ext.signingKeyId, - rootProject.ext.signingPrivateKey, - rootProject.ext.signingPassword, - ) - sign publishing.publications + signing { + useInMemoryPgpKeys( + rootProject.ext.signingKeyId, + rootProject.ext.signingPrivateKey, + rootProject.ext.signingPassword, + ) + sign publishing.publications + } } repositories { diff --git a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifest.kt b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifest.kt index 40dbd818..02c4a3a0 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifest.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifest.kt @@ -7,40 +7,47 @@ package io.velocitycareerlabs.api.entities -import io.velocitycareerlabs.impl.extensions.getUrlSubPath -import kotlin.io.path.ExperimentalPathApi - data class VCLCredentialManifest( val jwt: VCLJwt, val vendorOriginContext: String? = null, - val verifiedProfile: VCLVerifiedProfile + val verifiedProfile: VCLVerifiedProfile, + val deepLink: VCLDeepLink? = null ) { val iss: String get() = jwt.payload?.toJSONObject()?.get(KeyIss) as? String ?: "" val did: String get() = iss - val issuerId: String get() = retrieveIssuerId() + val aud: String get() = retrieveAud() + val issuerId: String get() = jwt.payload?.toJSONObject()?.get(CodingKeys.KeyIssuer) as? String + ?: (jwt.payload?.toJSONObject()?.get(CodingKeys.KeyIssuer) as? Map<*, *>)?.get(CodingKeys.KeyId) as? String + ?: "" val exchangeId: String get() = jwt.payload?.toJSONObject()?.get(KeyExchangeId) as? String ?: "" - val presentationDefinitionId: String get() = (jwt.payload?.toJSONObject()?.get(KeyPresentationDefinitionId) as? Map<*, *>)?.get( - KeyId) as? String ?: "" - - val finalizeOffersUri: String get() = - (jwt.payload?.toJSONObject()?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get( - VCLCredentialManifest.KeyFinalizeOffersUri - )?.toString() ?: "" - - val checkOffersUri: String get() = - (jwt.payload?.toJSONObject()?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get( - VCLCredentialManifest.KeyCheckOffersUri - )?.toString() ?: "" - - val submitPresentationUri: String get() = - (jwt.payload?.toJSONObject()?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get( - VCLCredentialManifest.KeySubmitIdentificationUri - )?.toString() ?: "" - - @OptIn(ExperimentalStdlibApi::class) - private fun retrieveIssuerId() = - ((jwt.payload?.toJSONObject()?.getOrDefault(CodingKeys.KeyMetadata, HashMap()) as? Map )?.getOrDefault(CodingKeys.KeyFinalizeOffersUri, "") as? String ?: "").substringBefore("/issue/") + val presentationDefinitionId: String + get() = (jwt.payload?.toJSONObject()?.get(KeyPresentationDefinitionId) as? Map<*, *>)?.get( + KeyId + ) as? String ?: "" + + val finalizeOffersUri: String + get() = + (jwt.payload?.toJSONObject() + ?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get( + VCLCredentialManifest.KeyFinalizeOffersUri + )?.toString() ?: "" + val checkOffersUri: String + get() = + (jwt.payload?.toJSONObject() + ?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get( + VCLCredentialManifest.KeyCheckOffersUri + )?.toString() ?: "" + + val submitPresentationUri: String + get() = + (jwt.payload?.toJSONObject() + ?.get(VCLCredentialManifest.KeyMetadata) as? Map<*, *>)?.get( + VCLCredentialManifest.KeySubmitIdentificationUri + )?.toString() ?: "" + + private fun retrieveAud() = + ((jwt.payload?.toJSONObject()?.getOrDefault(CodingKeys.KeyMetadata, HashMap()) as? Map )?.getOrDefault(CodingKeys.KeyFinalizeOffersUri, "") as? String ?: "").substringBefore("/issue/") companion object CodingKeys { const val KeyIssuingRequest = "issuing_request" diff --git a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifestDescriptor.kt b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifestDescriptor.kt index e6d4bd00..b2fd6715 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifestDescriptor.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifestDescriptor.kt @@ -14,7 +14,8 @@ open class VCLCredentialManifestDescriptor( val issuingType: VCLIssuingType = VCLIssuingType.Career, val credentialTypes: List? = null, val pushDelegate: VCLPushDelegate? = null, - val vendorOriginContext: String? = null + val vendorOriginContext: String? = null, + val deepLink: VCLDeepLink? = null ) { val did = uri?.getUrlSubPath(KeyDidPrefix) diff --git a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifestDescriptorByDeepLink.kt b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifestDescriptorByDeepLink.kt index 55804434..dfe36199 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifestDescriptorByDeepLink.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLCredentialManifestDescriptorByDeepLink.kt @@ -10,8 +10,11 @@ package io.velocitycareerlabs.api.entities class VCLCredentialManifestDescriptorByDeepLink( deepLink: VCLDeepLink, issuingType: VCLIssuingType = VCLIssuingType.Career, + pushDelegate: VCLPushDelegate? = null ): VCLCredentialManifestDescriptor( uri = deepLink.requestUri, issuingType = issuingType, - vendorOriginContext = deepLink.vendorOriginContext + pushDelegate = pushDelegate, + vendorOriginContext = deepLink.vendorOriginContext, + deepLink = deepLink ) \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLFinalizeOffersDescriptor.kt b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLFinalizeOffersDescriptor.kt index 0f47f8fc..e19caa1f 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLFinalizeOffersDescriptor.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLFinalizeOffersDescriptor.kt @@ -4,7 +4,6 @@ * Copyright 2022 Velocity Career Labs inc. * SPDX-License-Identifier: Apache-2.0 */ - package io.velocitycareerlabs.api.entities import io.velocitycareerlabs.impl.extensions.toJsonArray @@ -22,8 +21,8 @@ data class VCLFinalizeOffersDescriptor( .putOpt(KeyApprovedOfferIds, approvedOfferIds.toJsonArray()) .putOpt(KeyRejectedOfferIds, rejectedOfferIds.toJsonArray()) - val did: String get() = credentialManifest.did val issuerId: String get() = credentialManifest.issuerId + val aud: String get() = credentialManifest.aud val exchangeId: String get() = credentialManifest.exchangeId val finalizeOffersUri: String get() = credentialManifest.finalizeOffersUri val serviceTypes: VCLServiceTypes get() = credentialManifest.verifiedProfile.serviceTypes diff --git a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLGenerateOffersDescriptor.kt b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLGenerateOffersDescriptor.kt index d0bc7fd8..78cc0ab6 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLGenerateOffersDescriptor.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLGenerateOffersDescriptor.kt @@ -22,9 +22,8 @@ data class VCLGenerateOffersDescriptor( .putOpt(KeyTypes,types?.toJsonArray()) .putOpt(KeyOfferHashes,offerHashes?.toJsonArray()) - val did: String get() = credentialManifest.did + val issuerId: String get() = credentialManifest.issuerId val exchangeId: String get() = credentialManifest.exchangeId - val checkOffersUri: String get() = credentialManifest.checkOffersUri companion object CodingKeys { diff --git a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLOffer.kt b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLOffer.kt new file mode 100644 index 00000000..0870ad06 --- /dev/null +++ b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLOffer.kt @@ -0,0 +1,22 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.api.entities + +import org.json.JSONObject + +data class VCLOffer(val payload: JSONObject) { + val issuerId: String get() = payload.optJSONObject(CodingKeys.KeyIssuer)?.optString(KeyId) + ?: payload.optString(CodingKeys.KeyIssuer) + ?: "" + val id: String get() = payload.optString(CodingKeys.KeyId) ?: "" + + companion object CodingKeys { + const val KeyId = "id" + const val KeyDid = "did" + const val KeyIssuer = "issuer" + } +} diff --git a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLOffers.kt b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLOffers.kt index 5fd3bb7c..89ea8295 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLOffers.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLOffers.kt @@ -12,7 +12,7 @@ import org.json.JSONObject data class VCLOffers ( val payload: JSONObject, - val all: JSONArray, + val all: List, val responseCode: Int, val sessionToken: VCLToken, val challenge: String, diff --git a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLToken.kt b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLToken.kt index 38c52cae..38b663fc 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLToken.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/api/entities/VCLToken.kt @@ -4,7 +4,28 @@ * Copyright 2022 Velocity Career Labs inc. * SPDX-License-Identifier: Apache-2.0 */ - package io.velocitycareerlabs.api.entities -data class VCLToken(val value: String) \ No newline at end of file +data class VCLToken( + /** + * token value represented as jwt string + */ + val value: String +) { + /** + * token value represented as VCLJwt object + */ + val jwtValue = VCLJwt(value) + + constructor(jwtValue: VCLJwt) : this(jwtValue.encodedJwt ?: "") + + /** + * token expiration period in milliseconds + */ + val expiresIn: Long? + get() = jwtValue.signedJwt?.payload?.toJSONObject()?.get(CodingKeys.KeyExp) as? Long + + companion object CodingKeys { + const val KeyExp = "exp" + } +} \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/api/entities/error/VCLError.kt b/VCL/src/main/java/io/velocitycareerlabs/api/entities/error/VCLError.kt index e748cbed..5677b6d8 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/api/entities/error/VCLError.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/api/entities/error/VCLError.kt @@ -14,13 +14,13 @@ class VCLError( ): Error() { var payload: String? = null var error: String? = null - var errorCode: String? = null + var errorCode: String = VCLErrorCode.SdkError.value override var message: String? = null var statusCode: Int? = null constructor( error: String? = null, - errorCode: String? = null, + errorCode: String = VCLErrorCode.SdkError.value, message: String? = null, statusCode: Int? = null, ) : this() { @@ -34,7 +34,7 @@ class VCLError( val payloadJson = payload?.toJsonObject() this.payload = payload this.error = payloadJson?.optString(KeyError) - this.errorCode = errorCode ?: payloadJson?.optString(KeyErrorCode) + this.errorCode = errorCode ?: payloadJson?.optString(KeyErrorCode) ?: VCLErrorCode.SdkError.value this.message = payloadJson?.optString(KeyMessage) this.statusCode = payloadJson?.optInt(KeyStatusCode) } @@ -42,7 +42,7 @@ class VCLError( constructor(exception: Exception, statusCode: Int? = null): this() { this.payload = null this.error = null - this.errorCode = null + this.errorCode = VCLErrorCode.SdkError.value this.message = exception.toString() this.statusCode = statusCode } diff --git a/VCL/src/main/java/io/velocitycareerlabs/api/entities/error/VCLErrorCode.kt b/VCL/src/main/java/io/velocitycareerlabs/api/entities/error/VCLErrorCode.kt index f4cf43d4..b39aac20 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/api/entities/error/VCLErrorCode.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/api/entities/error/VCLErrorCode.kt @@ -7,7 +7,7 @@ package io.velocitycareerlabs.api.entities.error -internal enum class VCLErrorCode(val value: String) { +enum class VCLErrorCode(val value: String) { // Initialization RemoteServicesUrlsNotFount("remote_services_urls_not_found"), InjectedServicesNotFount("injected_services_not_found"), @@ -17,5 +17,12 @@ internal enum class VCLErrorCode(val value: String) { IssuerRequiresNotaryPermission("issuer_requires_notary_permission"), InvalidCredentialSubjectType("invalid_credential_subject_type"), InvalidCredentialSubjectContext("invalid_credential_subject_context"), - IssuerUnexpectedPermissionFailure("issuer_unexpected_permission_failure") + IssuerUnexpectedPermissionFailure("issuer_unexpected_permission_failure"), + // DID consistent with the Deep Link + MismatchedRequestIssuerDid("mismatched_request_issuer_did"), + MismatchedOfferIssuerDid("mismatched_offer_issuer_did"), + MismatchedCredentialIssuerDid("mismatched_credential_issuer_did"), + MismatchedPresentationRequestInspectorDid("mismatched_presentation_request_inspector_did"), + // General error + SdkError("sdk_error") } \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/VclBlocksProvider.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/VclBlocksProvider.kt index 5aa3fe5b..3c005032 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/VclBlocksProvider.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/VclBlocksProvider.kt @@ -13,7 +13,7 @@ import io.velocitycareerlabs.api.entities.VCLCredentialTypes import io.velocitycareerlabs.api.entities.error.VCLError import io.velocitycareerlabs.api.entities.error.VCLErrorCode import io.velocitycareerlabs.api.entities.initialization.VCLCryptoServicesDescriptor -import io.velocitycareerlabs.impl.data.utils.CredentialDidVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.CredentialDidVerifierImpl import io.velocitycareerlabs.impl.data.infrastructure.db.CacheServiceImpl import io.velocitycareerlabs.impl.data.infrastructure.db.SecretStoreServiceImpl import io.velocitycareerlabs.impl.data.infrastructure.network.NetworkServiceImpl @@ -25,11 +25,16 @@ import io.velocitycareerlabs.impl.data.usecases.* import io.velocitycareerlabs.api.jwt.VCLJwtSignService import io.velocitycareerlabs.api.jwt.VCLJwtVerifyService import io.velocitycareerlabs.api.keys.VCLKeyService -import io.velocitycareerlabs.impl.data.utils.CredentialIssuerVerifierEmptyImpl -import io.velocitycareerlabs.impl.data.utils.CredentialIssuerVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.CredentialIssuerVerifierEmptyImpl +import io.velocitycareerlabs.impl.data.verifiers.CredentialIssuerVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.CredentialManifestByDeepLinkVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.CredentialsByDeepLinkVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.OffersByDeepLinkVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.PresentationRequestByDeepLinkVerifierImpl import io.velocitycareerlabs.impl.domain.models.* import io.velocitycareerlabs.impl.domain.usecases.* -import io.velocitycareerlabs.impl.domain.utils.CredentialIssuerVerifier +import io.velocitycareerlabs.impl.domain.verifiers.CredentialIssuerVerifier +import io.velocitycareerlabs.impl.domain.verifiers.PresentationRequestByDeepLinkVerifier import io.velocitycareerlabs.impl.jwt.local.VCLJwtSignServiceLocalImpl import io.velocitycareerlabs.impl.jwt.local.VCLJwtVerifyServiceLocalImpl import io.velocitycareerlabs.impl.jwt.remote.VCLJwtSignServiceRemoteImpl @@ -169,6 +174,7 @@ internal object VclBlocksProvider { chooseJwtSignService(context, cryptoServicesDescriptor), chooseJwtVerifyService(cryptoServicesDescriptor) ), + PresentationRequestByDeepLinkVerifierImpl(), ExecutorImpl() ) @@ -212,6 +218,7 @@ internal object VclBlocksProvider { chooseJwtSignService(context, cryptoServicesDescriptor), chooseJwtVerifyService(cryptoServicesDescriptor) ), + CredentialManifestByDeepLinkVerifierImpl(), ExecutorImpl() ) @@ -244,6 +251,7 @@ internal object VclBlocksProvider { GenerateOffersRepositoryImpl( NetworkServiceImpl() ), + OffersByDeepLinkVerifierImpl(), ExecutorImpl() ) @@ -272,6 +280,7 @@ internal object VclBlocksProvider { ), credentialIssuerVerifier, CredentialDidVerifierImpl(), + CredentialsByDeepLinkVerifierImpl(), ExecutorImpl() ) } diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/FinalizeOffersRepositoryImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/FinalizeOffersRepositoryImpl.kt index 344f2c5e..41541665 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/FinalizeOffersRepositoryImpl.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/FinalizeOffersRepositoryImpl.kt @@ -12,9 +12,12 @@ import io.velocitycareerlabs.api.entities.error.VCLError import io.velocitycareerlabs.impl.data.infrastructure.network.Request import io.velocitycareerlabs.impl.domain.infrastructure.network.NetworkService import io.velocitycareerlabs.impl.domain.repositories.FinalizeOffersRepository +import io.velocitycareerlabs.impl.extensions.toJsonArray +import io.velocitycareerlabs.impl.extensions.toJwtList import io.velocitycareerlabs.impl.extensions.toList import org.json.JSONArray import java.lang.Exception +import java.util.concurrent.CompletableFuture internal class FinalizeOffersRepositoryImpl( private val networkService: NetworkService @@ -25,7 +28,7 @@ internal class FinalizeOffersRepositoryImpl( finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, sessionToken: VCLToken, proof: VCLJwt, - completionBlock: (VCLResult>) -> Unit + completionBlock: (VCLResult>) -> Unit ) { networkService.sendRequest( endpoint = finalizeOffersDescriptor.finalizeOffersUri, @@ -40,14 +43,12 @@ internal class FinalizeOffersRepositoryImpl( result.handleResult( { finalizedOffersResponse -> try { - val encodedJwts = - JSONArray(finalizedOffersResponse.payload).toList() as? List - encodedJwts?.let { + finalizedOffersResponse.payload.toJwtList()?.let { completionBlock(VCLResult.Success(it)) } ?: run { completionBlock( VCLResult.Failure( - VCLError("Failed to parse: $finalizedOffersResponse.payload") + VCLError("Failed to parse: ${finalizedOffersResponse.payload}") ) ) } diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/GenerateOffersRepositoryImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/GenerateOffersRepositoryImpl.kt index abc524d5..8dd2de4d 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/GenerateOffersRepositoryImpl.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/GenerateOffersRepositoryImpl.kt @@ -11,6 +11,7 @@ import io.velocitycareerlabs.api.entities.* import io.velocitycareerlabs.api.entities.error.VCLError import io.velocitycareerlabs.impl.data.infrastructure.network.Request import io.velocitycareerlabs.impl.data.infrastructure.network.Response +import io.velocitycareerlabs.impl.data.utils.Utils import io.velocitycareerlabs.impl.domain.infrastructure.network.NetworkService import io.velocitycareerlabs.impl.domain.repositories.GenerateOffersRepository import io.velocitycareerlabs.impl.extensions.toJsonArray @@ -42,9 +43,11 @@ internal class GenerateOffersRepositoryImpl( result.handleResult( { offersResponse -> try { - completionBlock(VCLResult.Success( - parse(offersResponse, sessionToken) - )) + completionBlock( + VCLResult.Success( + parse(offersResponse, sessionToken) + ) + ) } catch (ex: Exception) { completionBlock(VCLResult.Failure(VCLError(ex))) } @@ -62,17 +65,19 @@ internal class GenerateOffersRepositoryImpl( offersResponse.payload.toJsonObject()?.let { payload -> return VCLOffers( payload = payload, - all = payload.optJSONArray(VCLOffers.CodingKeys.KeyOffers) ?: JSONArray(), + all = Utils.offersFromJsonArray( + payload.optJSONArray(VCLOffers.CodingKeys.KeyOffers) ?: JSONArray() + ), responseCode = offersResponse.code, sessionToken = sessionToken, challenge = payload.optString(VCLOffers.CodingKeys.KeyChallenge) ?: "" ) } ?: run { // VCLXVnfProtocolVersion.XVnfProtocolVersion1 - offersResponse.payload.toJsonArray()?.let { allOffers -> + offersResponse.payload.toJsonArray()?.let { offersJsonArray -> return VCLOffers( payload = JSONObject(), - all = allOffers, + all = Utils.offersFromJsonArray(offersJsonArray), responseCode = offersResponse.code, sessionToken = sessionToken, challenge = "" @@ -81,7 +86,7 @@ internal class GenerateOffersRepositoryImpl( // No offers return VCLOffers( payload = JSONObject(), - all = JSONArray(), + all = listOf(), responseCode = offersResponse.code, sessionToken = sessionToken, challenge = "" diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/SubmissionRepositoryImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/SubmissionRepositoryImpl.kt index f22bfce9..57b8fa6c 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/SubmissionRepositoryImpl.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/repositories/SubmissionRepositoryImpl.kt @@ -12,6 +12,7 @@ import io.velocitycareerlabs.api.entities.error.VCLError import io.velocitycareerlabs.impl.data.infrastructure.network.Request import io.velocitycareerlabs.impl.domain.repositories.SubmissionRepository import io.velocitycareerlabs.impl.domain.infrastructure.network.NetworkService +import io.velocitycareerlabs.impl.extensions.toJsonObject import org.json.JSONObject import java.lang.Exception @@ -34,9 +35,8 @@ internal class SubmissionRepositoryImpl( completionBlock = { result -> result.handleResult({ submissionResponse -> try { - val jsonObj = JSONObject(submissionResponse.payload) - val submissionResult = - parse(jsonObj, submission.jti, submission.submissionId) + val jsonObj = submissionResponse.payload.toJsonObject() + val submissionResult = parse(jsonObj, submission.jti, submission.submissionId) completionBlock(VCLResult.Success(submissionResult)) } catch (ex: Exception) { completionBlock(VCLResult.Failure(VCLError(ex))) @@ -51,13 +51,13 @@ internal class SubmissionRepositoryImpl( } private fun parse( - jsonObj: JSONObject, + jsonObj: JSONObject?, jti: String, submissionId: String ): VCLSubmissionResult { - val exchangeJsonObj = jsonObj.optJSONObject(VCLSubmissionResult.KeyExchange) + val exchangeJsonObj = jsonObj?.optJSONObject(VCLSubmissionResult.KeyExchange) return VCLSubmissionResult( - sessionToken = VCLToken(jsonObj.optString(VCLSubmissionResult.KeyToken)), + sessionToken = VCLToken(jsonObj?.optString(VCLSubmissionResult.KeyToken) ?: ""), exchange = parseExchange(exchangeJsonObj), jti = jti, submissionId = submissionId diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/CredentialManifestUseCaseImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/CredentialManifestUseCaseImpl.kt index d2196708..e18141a5 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/CredentialManifestUseCaseImpl.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/CredentialManifestUseCaseImpl.kt @@ -14,15 +14,20 @@ import io.velocitycareerlabs.impl.domain.repositories.CredentialManifestReposito import io.velocitycareerlabs.impl.domain.repositories.JwtServiceRepository import io.velocitycareerlabs.impl.domain.repositories.ResolveKidRepository import io.velocitycareerlabs.impl.domain.usecases.CredentialManifestUseCase +import io.velocitycareerlabs.impl.domain.verifiers.CredentialManifestByDeepLinkVerifier import io.velocitycareerlabs.impl.extensions.encode +import io.velocitycareerlabs.impl.utils.VCLLog internal class CredentialManifestUseCaseImpl( private val credentialManifestRepository: CredentialManifestRepository, private val resolveKidRepository: ResolveKidRepository, private val jwtServiceRepository: JwtServiceRepository, + private val credentialManifestByDeepLinkVerifier: CredentialManifestByDeepLinkVerifier, private val executor: Executor ): CredentialManifestUseCase { + private val TAG = CredentialManifestUseCaseImpl::class.simpleName + override fun getCredentialManifest( credentialManifestDescriptor: VCLCredentialManifestDescriptor, verifiedProfile: VCLVerifiedProfile, @@ -37,9 +42,12 @@ internal class CredentialManifestUseCaseImpl( { jwtStr -> try { onGetCredentialManifestSuccess( - VCLJwt(jwtStr), - credentialManifestDescriptor, - verifiedProfile, + VCLCredentialManifest( + VCLJwt(jwtStr), + credentialManifestDescriptor.vendorOriginContext, + verifiedProfile, + credentialManifestDescriptor.deepLink + ), remoteCryptoServicesToken, completionBlock ) @@ -56,21 +64,48 @@ internal class CredentialManifestUseCaseImpl( } private fun onGetCredentialManifestSuccess( - jwt: VCLJwt, - credentialManifestDescriptor: VCLCredentialManifestDescriptor, - verifiedProfile: VCLVerifiedProfile, + credentialManifest: VCLCredentialManifest, remoteCryptoServicesToken: VCLToken?, completionBlock: (VCLResult) -> Unit ) { - jwt.kid?.replace("#", "#".encode())?.let { kid -> + credentialManifest.deepLink?.let { deepLink -> + credentialManifestByDeepLinkVerifier.verifyCredentialManifest(credentialManifest, deepLink) { + it.handleResult( + { isVerified -> + VCLLog.d(TAG, "Credential manifest deep link verification result: $isVerified") + onCredentialManifestDidVerificationSuccess( + credentialManifest, + remoteCryptoServicesToken, + completionBlock + ) + }, + { error -> + onError(error, completionBlock) + } + ) + } + } ?: run { + VCLLog.d(TAG, "Deep link was not provided => nothing to verify") + onCredentialManifestDidVerificationSuccess( + credentialManifest, + remoteCryptoServicesToken, + completionBlock + ) + } + } + + private fun onCredentialManifestDidVerificationSuccess( + credentialManifest: VCLCredentialManifest, + remoteCryptoServicesToken: VCLToken?, + completionBlock: (VCLResult) -> Unit + ) { + credentialManifest.jwt.kid?.replace("#", "#".encode())?.let { kid -> resolveKidRepository.getPublicKey(kid) { publicKeyResult -> publicKeyResult.handleResult( { publicKey -> onResolvePublicKeySuccess( publicKey, - jwt, - credentialManifestDescriptor, - verifiedProfile, + credentialManifest, remoteCryptoServicesToken, completionBlock ) @@ -87,14 +122,12 @@ internal class CredentialManifestUseCaseImpl( private fun onResolvePublicKeySuccess( publicJwk: VCLPublicJwk, - jwt: VCLJwt, - credentialManifestDescriptor: VCLCredentialManifestDescriptor, - verifiedProfile: VCLVerifiedProfile, + credentialManifest: VCLCredentialManifest, remoteCryptoServicesToken: VCLToken?, completionBlock: (VCLResult) -> Unit ) { jwtServiceRepository.verifyJwt( - jwt, + credentialManifest.jwt, publicJwk, remoteCryptoServicesToken ) @@ -103,9 +136,7 @@ internal class CredentialManifestUseCaseImpl( { isVerified -> onVerificationSuccess( isVerified, - jwt, - credentialManifestDescriptor, - verifiedProfile, + credentialManifest, completionBlock ) }, @@ -118,21 +149,18 @@ internal class CredentialManifestUseCaseImpl( private fun onVerificationSuccess( isVerified: Boolean, - jwt: VCLJwt, - credentialManifestDescriptor: VCLCredentialManifestDescriptor, - verifiedProfile: VCLVerifiedProfile, + credentialManifest: VCLCredentialManifest, completionBlock: (VCLResult) -> Unit ) { if (isVerified) { executor.runOnMain { - completionBlock(VCLResult.Success(VCLCredentialManifest( - jwt, - credentialManifestDescriptor.vendorOriginContext, - verifiedProfile - ))) + completionBlock(VCLResult.Success(credentialManifest)) } } else { - onError(VCLError("Failed to verify: $jwt"), completionBlock) + onError( + VCLError("Failed to verify credentialManifest jwt:\n${credentialManifest.jwt}"), + completionBlock + ) } } diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/FinalizeOffersUseCaseImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/FinalizeOffersUseCaseImpl.kt index 826a522b..78c5fb8f 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/FinalizeOffersUseCaseImpl.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/FinalizeOffersUseCaseImpl.kt @@ -4,18 +4,19 @@ * Copyright 2022 Velocity Career Labs inc. * SPDX-License-Identifier: Apache-2.0 */ - package io.velocitycareerlabs.impl.data.usecases import io.velocitycareerlabs.api.entities.* import io.velocitycareerlabs.api.entities.VCLFinalizeOffersDescriptor import io.velocitycareerlabs.api.entities.error.VCLError -import io.velocitycareerlabs.impl.domain.utils.CredentialDidVerifier +import io.velocitycareerlabs.impl.domain.verifiers.CredentialDidVerifier import io.velocitycareerlabs.impl.domain.infrastructure.executors.Executor import io.velocitycareerlabs.impl.domain.repositories.FinalizeOffersRepository import io.velocitycareerlabs.impl.domain.repositories.JwtServiceRepository import io.velocitycareerlabs.impl.domain.usecases.FinalizeOffersUseCase -import io.velocitycareerlabs.impl.domain.utils.CredentialIssuerVerifier +import io.velocitycareerlabs.impl.domain.verifiers.CredentialIssuerVerifier +import io.velocitycareerlabs.impl.domain.verifiers.CredentialsByDeepLinkVerifier +import io.velocitycareerlabs.impl.utils.VCLLog import java.util.UUID internal class FinalizeOffersUseCaseImpl( @@ -23,8 +24,11 @@ internal class FinalizeOffersUseCaseImpl( private val jwtServiceRepository: JwtServiceRepository, private val credentialIssuerVerifier: CredentialIssuerVerifier, private val credentialDidVerifier: CredentialDidVerifier, + private val credentialsByDeepLinkVerifier: CredentialsByDeepLinkVerifier, private val executor: Executor ): FinalizeOffersUseCase { + private val TAG = FinalizeOffersUseCaseImpl::class.simpleName + override fun finalizeOffers( finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, didJwk: VCLDidJwk?, @@ -39,7 +43,7 @@ internal class FinalizeOffersUseCaseImpl( jwtDescriptor = VCLJwtDescriptor( keyId = didJwk?.keyId, iss = didJwk?.did ?: UUID.randomUUID().toString(), - aud = finalizeOffersDescriptor.issuerId + aud = finalizeOffersDescriptor.aud ), remoteCryptoServicesToken = remoteCryptoServicesToken ) { proofJwtResult -> @@ -49,22 +53,33 @@ internal class FinalizeOffersUseCaseImpl( sessionToken = sessionToken, proof = proof, finalizeOffersDescriptor = finalizeOffersDescriptor - ) { encodedJwtCredentialsListResult -> - encodedJwtCredentialsListResult.handleResult( - successHandler = { encodedJwtCredentialsList -> - verifyCredentialsByIssuer( - encodedJwtCredentialsList, + ) { jwtCredentialsListResult -> + jwtCredentialsListResult.handleResult( + successHandler = { jwtCredentials -> + verifyCredentialsByDeepLink( + jwtCredentials, finalizeOffersDescriptor - ) { - it.handleResult( + ) { verifyCredentialsByDeepLinkResult -> + verifyCredentialsByDeepLinkResult.handleResult( successHandler = { - verifyCredentialByDid( - encodedJwtCredentialsList, + verifyCredentialsByIssuer( + jwtCredentials, finalizeOffersDescriptor - ) { jwtVerifiableCredentialsResult -> - executor.runOnMain { - completionBlock(jwtVerifiableCredentialsResult) - } + ) { verifyCredentialsByIssuerResult -> + verifyCredentialsByIssuerResult.handleResult({ + verifyCredentialByDid( + jwtCredentials, + finalizeOffersDescriptor + ) { jwtVerifiableCredentialsResult -> + executor.runOnMain { + completionBlock( + jwtVerifiableCredentialsResult + ) + } + } + }, { error -> + onError(error, completionBlock) + }) } }, errorHandler = { error -> @@ -84,14 +99,37 @@ internal class FinalizeOffersUseCaseImpl( } } } + + private fun verifyCredentialsByDeepLink( + jwtCredentials: List, + finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, + completionBlock: (VCLResult) -> Unit + ) { + finalizeOffersDescriptor.credentialManifest.deepLink?.let { deepLink -> + credentialsByDeepLinkVerifier.verifyCredentials( + jwtCredentials, + deepLink + ) { + it.handleResult({ isVerified -> + VCLLog.d(TAG, "Credentials by deep link verification result: $isVerified") + completionBlock(VCLResult.Success(true)) + }, { error -> + completionBlock(VCLResult.Failure(error)) + }) + } + } ?: run { + VCLLog.d(TAG, "Deep link was not provided => nothing to verify") + completionBlock(VCLResult.Success(true)) + } + } private fun verifyCredentialsByIssuer( - encodedJwtCredentialsList: List, + jwtCredentials: List, finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, completionBlock: (VCLResult) -> Unit ) { credentialIssuerVerifier.verifyCredentials( - encodedJwtCredentialsList, + jwtCredentials, finalizeOffersDescriptor ) { credentialIssuerVerifierResult -> credentialIssuerVerifierResult.handleResult( @@ -106,7 +144,7 @@ internal class FinalizeOffersUseCaseImpl( } private fun verifyCredentialByDid( - encodedJwtCredentialsList: List, + encodedJwtCredentialsList: List, finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, completionBlock: (VCLResult) -> Unit ) { diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/GenerateOffersUseCaseImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/GenerateOffersUseCaseImpl.kt index 076c7dc2..23c08089 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/GenerateOffersUseCaseImpl.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/GenerateOffersUseCaseImpl.kt @@ -4,18 +4,23 @@ * Copyright 2022 Velocity Career Labs inc. * SPDX-License-Identifier: Apache-2.0 */ - package io.velocitycareerlabs.impl.data.usecases import io.velocitycareerlabs.api.entities.* +import io.velocitycareerlabs.api.entities.error.VCLError import io.velocitycareerlabs.impl.domain.infrastructure.executors.Executor import io.velocitycareerlabs.impl.domain.repositories.GenerateOffersRepository import io.velocitycareerlabs.impl.domain.usecases.GenerateOffersUseCase +import io.velocitycareerlabs.impl.domain.verifiers.OffersByDeepLinkVerifier +import io.velocitycareerlabs.impl.utils.VCLLog internal class GenerateOffersUseCaseImpl( private val generateOffersRepository: GenerateOffersRepository, + private val offersByDeepLinkVerifier: OffersByDeepLinkVerifier, private val executor: Executor ): GenerateOffersUseCase { + private val TAG = GenerateOffersUseCaseImpl::class.simpleName + override fun generateOffers( generateOffersDescriptor: VCLGenerateOffersDescriptor, sessionToken: VCLToken, @@ -26,10 +31,43 @@ internal class GenerateOffersUseCaseImpl( generateOffersDescriptor, sessionToken ) { - executor.runOnMain { - completionBlock(it) - } + it.handleResult({ offers -> + verifyOffersByDeepLink(offers,generateOffersDescriptor, completionBlock) + },{ error -> + onError(error, completionBlock) + }) } } } + + private fun verifyOffersByDeepLink( + offers: VCLOffers, + generateOffersDescriptor: VCLGenerateOffersDescriptor, + completionBlock: (VCLResult) -> Unit + ) { + generateOffersDescriptor.credentialManifest.deepLink?.let { deepLink -> + offersByDeepLinkVerifier.verifyOffers(offers, deepLink) { + it.handleResult({ isVerified -> + VCLLog.d(TAG, "Offers deep link verification result: $isVerified") + executor.runOnMain { + completionBlock(VCLResult.Success(offers)) + } + }, { error -> + onError(error, completionBlock) + }) + } + } ?: run { + VCLLog.d(TAG, "Deep link was not provided => nothing to verify") + executor.runOnMain { + completionBlock(VCLResult.Success(offers)) + } + } + } + + private fun onError( + error: VCLError, + completionBlock: (VCLResult) -> Unit + ) { + completionBlock(VCLResult.Failure(error)) + } } \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/PresentationRequestUseCaseImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/PresentationRequestUseCaseImpl.kt index d39f819e..4e26b5c2 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/PresentationRequestUseCaseImpl.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/usecases/PresentationRequestUseCaseImpl.kt @@ -4,7 +4,6 @@ * Copyright 2022 Velocity Career Labs inc. * SPDX-License-Identifier: Apache-2.0 */ - package io.velocitycareerlabs.impl.data.usecases import io.velocitycareerlabs.api.entities.* @@ -12,16 +11,21 @@ import io.velocitycareerlabs.api.entities.error.VCLError import io.velocitycareerlabs.impl.domain.infrastructure.executors.Executor import io.velocitycareerlabs.impl.domain.repositories.* import io.velocitycareerlabs.impl.domain.usecases.PresentationRequestUseCase +import io.velocitycareerlabs.impl.domain.verifiers.PresentationRequestByDeepLinkVerifier import io.velocitycareerlabs.impl.extensions.encode +import io.velocitycareerlabs.impl.utils.VCLLog import java.lang.Exception internal class PresentationRequestUseCaseImpl( private val presentationRequestRepository: PresentationRequestRepository, private val resolveKidRepository: ResolveKidRepository, private val jwtServiceRepository: JwtServiceRepository, + private val presentationRequestByDeepLinkVerifier: PresentationRequestByDeepLinkVerifier, private val executor: Executor ): PresentationRequestUseCase { + private val TAG = PresentationRequestUseCaseImpl::class.simpleName + override fun getPresentationRequest( presentationRequestDescriptor: VCLPresentationRequestDescriptor, remoteCryptoServicesToken: VCLToken?, @@ -34,7 +38,7 @@ internal class PresentationRequestUseCaseImpl( encodedJwtStrResult.handleResult( { encodedJwtStr -> onGetJwtSuccess( - encodedJwtStr, + VCLJwt(encodedJwtStr), presentationRequestDescriptor, remoteCryptoServicesToken, completionBlock @@ -49,30 +53,6 @@ internal class PresentationRequestUseCaseImpl( } private fun onGetJwtSuccess( - encodedJwtStr: String, - presentationRequestDescriptor: VCLPresentationRequestDescriptor, - remoteCryptoServicesToken: VCLToken?, - completionBlock: (VCLResult) -> Unit - ) { - try { - jwtServiceRepository.decode(encodedJwtStr) { jwtResult -> - jwtResult.handleResult({ jwt -> - onDecodeJwtSuccess( - jwt, - presentationRequestDescriptor, - remoteCryptoServicesToken, - completionBlock - ) - }, { error -> - onError(error, completionBlock) - }) - } - } catch (ex: Exception) { - onError(VCLError(ex), completionBlock) - } - } - - private fun onDecodeJwtSuccess( jwt: VCLJwt, presentationRequestDescriptor: VCLPresentationRequestDescriptor, remoteCryptoServicesToken: VCLToken?, @@ -114,17 +94,26 @@ internal class PresentationRequestUseCaseImpl( presentationRequest.jwt, presentationRequest.publicJwk, remoteCryptoServicesToken, - ) { isVerifiedResult -> - isVerifiedResult.handleResult({ isVerified -> - onVerificationSuccess( - isVerified, + ) { jwtVerificationRes -> + jwtVerificationRes.handleResult({ + presentationRequestByDeepLinkVerifier.verifyPresentationRequest( presentationRequest, - completionBlock - ) - }, - { error -> - onError(error, completionBlock) - }) + presentationRequestDescriptor.deepLink + ) { byDeepLinkVerificationRes -> + byDeepLinkVerificationRes.handleResult({ isVerified -> + VCLLog.d(TAG, "Presentation request by deep link verification result: $isVerified") + onVerificationSuccess( + isVerified, + presentationRequest, + completionBlock + ) + }, { error -> + onError(error, completionBlock) + }) + } + }, { error -> + onError(error, completionBlock) + }) } } diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/Utils.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/Utils.kt index aba16209..a70fde6a 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/Utils.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/Utils.kt @@ -8,6 +8,9 @@ package io.velocitycareerlabs.impl.data.utils import io.velocitycareerlabs.api.entities.VCLJwt +import io.velocitycareerlabs.api.entities.VCLOffer +import io.velocitycareerlabs.impl.data.verifiers.CredentialIssuerVerifierImpl +import org.json.JSONArray internal class Utils { companion object { @@ -79,5 +82,15 @@ internal class Utils { return (credentialSubject as? Map<*, *>)?.get("identifier") as? String ?: (credentialSubject as? Map<*, *>)?.get("id") as? String } + + internal fun offersFromJsonArray(offersJsonArray: JSONArray): List { + val allOffers = mutableListOf() + for (i in 0 until offersJsonArray.length()) { + offersJsonArray.optJSONObject(i)?.let { offerJsonObject -> + allOffers.add(VCLOffer(offerJsonObject)) + } + } + return allOffers + } } } \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/CredentialDidVerifierImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialDidVerifierImpl.kt similarity index 61% rename from VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/CredentialDidVerifierImpl.kt rename to VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialDidVerifierImpl.kt index 4a4e37dc..2b818d46 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/CredentialDidVerifierImpl.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialDidVerifierImpl.kt @@ -5,37 +5,30 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.velocitycareerlabs.impl.data.utils +package io.velocitycareerlabs.impl.data.verifiers import io.velocitycareerlabs.api.entities.VCLFinalizeOffersDescriptor import io.velocitycareerlabs.api.entities.VCLJwt import io.velocitycareerlabs.api.entities.VCLJwtVerifiableCredentials import io.velocitycareerlabs.api.entities.VCLResult -import io.velocitycareerlabs.impl.domain.utils.CredentialDidVerifier -import java.util.concurrent.CompletableFuture +import io.velocitycareerlabs.impl.domain.verifiers.CredentialDidVerifier internal class CredentialDidVerifierImpl: CredentialDidVerifier { override fun verifyCredentials( - jwtEncodedCredentials: List, + jwtCredentials: List, finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, completionBlock: (VCLResult) -> Unit ) { val passedCredentials = mutableListOf() val failedCredentials = mutableListOf() - val completableFutures = jwtEncodedCredentials.map { jwtEncodedCredential -> - CompletableFuture.supplyAsync { - val jwtCredential = VCLJwt(jwtEncodedCredential) - if (verifyCredential(jwtCredential, finalizeOffersDescriptor)) { - passedCredentials += jwtCredential - } else { - failedCredentials += jwtCredential - } + jwtCredentials.forEach { jwtCredential -> + if (verifyCredential(jwtCredential, finalizeOffersDescriptor)) { + passedCredentials += jwtCredential + } else { + failedCredentials += jwtCredential } } - val allFutures = CompletableFuture.allOf(*completableFutures.toTypedArray()) - allFutures.join() - completionBlock( VCLResult.Success( VCLJwtVerifiableCredentials( @@ -49,5 +42,6 @@ internal class CredentialDidVerifierImpl: CredentialDidVerifier { private fun verifyCredential( jwtCredential: VCLJwt, finalizeOffersDescriptor: VCLFinalizeOffersDescriptor - ) = jwtCredential.payload?.toJSONObject()?.get("iss") as? String == finalizeOffersDescriptor.did + // iss == vc.issuer.id + ) = jwtCredential.payload?.toJSONObject()?.get("iss") as? String == finalizeOffersDescriptor.issuerId } \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/CredentialIssuerVerifierEmptyImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialIssuerVerifierEmptyImpl.kt similarity index 78% rename from VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/CredentialIssuerVerifierEmptyImpl.kt rename to VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialIssuerVerifierEmptyImpl.kt index cdc4b66a..52cfefd0 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/CredentialIssuerVerifierEmptyImpl.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialIssuerVerifierEmptyImpl.kt @@ -5,18 +5,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.velocitycareerlabs.impl.data.utils +package io.velocitycareerlabs.impl.data.verifiers import io.velocitycareerlabs.api.entities.VCLFinalizeOffersDescriptor +import io.velocitycareerlabs.api.entities.VCLJwt import io.velocitycareerlabs.api.entities.VCLResult -import io.velocitycareerlabs.impl.domain.utils.CredentialIssuerVerifier +import io.velocitycareerlabs.impl.domain.verifiers.CredentialIssuerVerifier import io.velocitycareerlabs.impl.utils.VCLLog internal class CredentialIssuerVerifierEmptyImpl: CredentialIssuerVerifier { val TAG = CredentialIssuerVerifierEmptyImpl::class.java.simpleName override fun verifyCredentials( - jwtEncodedCredentials: List, + jwtCredentials: List, finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, completionBlock: (VCLResult) -> Unit ) { diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/CredentialIssuerVerifierImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialIssuerVerifierImpl.kt similarity index 95% rename from VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/CredentialIssuerVerifierImpl.kt rename to VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialIssuerVerifierImpl.kt index 9449fa18..1fb8ef69 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/data/utils/CredentialIssuerVerifierImpl.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialIssuerVerifierImpl.kt @@ -4,8 +4,7 @@ * Copyright 2022 Velocity Career Labs inc. * SPDX-License-Identifier: Apache-2.0 */ - -package io.velocitycareerlabs.impl.data.utils +package io.velocitycareerlabs.impl.data.verifiers import io.velocitycareerlabs.api.entities.VCLCredentialType import io.velocitycareerlabs.api.entities.error.VCLError @@ -19,15 +18,15 @@ import io.velocitycareerlabs.api.entities.handleResult import io.velocitycareerlabs.impl.data.infrastructure.network.Request import io.velocitycareerlabs.impl.data.repositories.HeaderKeys import io.velocitycareerlabs.impl.data.repositories.HeaderValues +import io.velocitycareerlabs.impl.data.utils.Utils import io.velocitycareerlabs.impl.domain.infrastructure.network.NetworkService import io.velocitycareerlabs.impl.domain.models.CredentialTypesModel -import io.velocitycareerlabs.impl.domain.utils.CredentialIssuerVerifier +import io.velocitycareerlabs.impl.domain.verifiers.CredentialIssuerVerifier import io.velocitycareerlabs.impl.extensions.toJsonObject import io.velocitycareerlabs.impl.extensions.toMap import io.velocitycareerlabs.impl.utils.VCLLog -import org.json.JSONObject import java.util.concurrent.CompletableFuture -import java.util.Stack +import java.util.concurrent.CopyOnWriteArrayList internal class CredentialIssuerVerifierImpl( private val credentialTypesModel: CredentialTypesModel, @@ -36,19 +35,18 @@ internal class CredentialIssuerVerifierImpl( val TAG = CredentialIssuerVerifierImpl::class.java.simpleName override fun verifyCredentials( - jwtEncodedCredentials: List, + jwtCredentials: List, finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, completionBlock: (VCLResult) -> Unit ) { - if (jwtEncodedCredentials.isEmpty()) /* nothing to verify */ { + if (jwtCredentials.isEmpty()) /* nothing to verify */ { completionBlock(VCLResult.Success(true)) } else if (finalizeOffersDescriptor.serviceTypes.all.isEmpty()) { completionBlock(VCLResult.Failure(VCLError(errorCode = VCLErrorCode.CredentialTypeNotRegistered.value))) } else { var globalError: VCLError? = null - val completableFutures = jwtEncodedCredentials.map { encodedJwtCredential -> + val completableFutures = jwtCredentials.map { jwtCredential -> CompletableFuture.supplyAsync { - val jwtCredential = VCLJwt(encodedJwt = encodedJwtCredential) Utils.getCredentialType(jwtCredential)?.let { credentialTypeName -> credentialTypesModel.credentialTypeByTypeName(credentialTypeName) ?.let { credentialType -> @@ -194,7 +192,7 @@ internal class CredentialIssuerVerifierImpl( credentialSubjectContexts: List<*>, completionBlock: (VCLResult>>) -> Unit ) { - val completeContexts = mutableListOf>() + val completeContexts = CopyOnWriteArrayList(mutableListOf>()) val completableFutures = credentialSubjectContexts.map { credentialSubjectContext -> CompletableFuture.supplyAsync { networkService.sendRequest( @@ -295,7 +293,8 @@ internal class CredentialIssuerVerifierImpl( activeContext.forEach { (key, value) -> (value as? Map<*, *>)?.let { valueMap -> if (valueMap[KeyId] == ValPrimaryOrganization || - valueMap[KeyId] == ValPrimarySourceProfile) { + valueMap[KeyId] == ValPrimarySourceProfile + ) { return key as? String } } diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialManifestByDeepLinkVerifierImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialManifestByDeepLinkVerifierImpl.kt new file mode 100644 index 00000000..a4177df5 --- /dev/null +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialManifestByDeepLinkVerifierImpl.kt @@ -0,0 +1,32 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.impl.data.verifiers + +import io.velocitycareerlabs.api.entities.VCLCredentialManifest +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLResult +import io.velocitycareerlabs.api.entities.error.VCLError +import io.velocitycareerlabs.api.entities.error.VCLErrorCode +import io.velocitycareerlabs.impl.domain.verifiers.CredentialManifestByDeepLinkVerifier +import io.velocitycareerlabs.impl.utils.VCLLog + +class CredentialManifestByDeepLinkVerifierImpl: CredentialManifestByDeepLinkVerifier { + private val TAG = CredentialManifestByDeepLinkVerifierImpl::class.simpleName + + override fun verifyCredentialManifest( + credentialManifest: VCLCredentialManifest, + deepLink: VCLDeepLink, + completionBlock: (VCLResult) -> Unit + ) { + if (credentialManifest.issuerId == deepLink.did) { + completionBlock(VCLResult.Success(true)) + } else { + VCLLog.e(TAG, "credential manifest: ${credentialManifest.jwt.encodedJwt} \ndeepLink: ${deepLink.value}") + completionBlock((VCLResult.Failure(VCLError(errorCode = VCLErrorCode.MismatchedRequestIssuerDid.value)))) + } + } +} \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialsByDeepLinkVerifierImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialsByDeepLinkVerifierImpl.kt new file mode 100644 index 00000000..22591f92 --- /dev/null +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/CredentialsByDeepLinkVerifierImpl.kt @@ -0,0 +1,32 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.impl.data.verifiers + +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLJwt +import io.velocitycareerlabs.api.entities.VCLResult +import io.velocitycareerlabs.api.entities.error.VCLError +import io.velocitycareerlabs.api.entities.error.VCLErrorCode +import io.velocitycareerlabs.impl.domain.verifiers.CredentialsByDeepLinkVerifier +import io.velocitycareerlabs.impl.utils.VCLLog + +class CredentialsByDeepLinkVerifierImpl: CredentialsByDeepLinkVerifier { + private val TAG = CredentialsByDeepLinkVerifierImpl::class.simpleName + + override fun verifyCredentials( + jwtCredentials: List, + deepLink: VCLDeepLink, + completionBlock: (VCLResult) -> Unit + ) { + jwtCredentials.find { it.iss != deepLink.did }?.let { mismatchedCredential -> + VCLLog.e(TAG, "mismatched credential: ${mismatchedCredential.encodedJwt} \ndeepLink: ${deepLink.value}") + completionBlock(VCLResult.Failure(VCLError(errorCode = VCLErrorCode.MismatchedCredentialIssuerDid.value))) + } ?: run { + completionBlock(VCLResult.Success(true)) + } + } +} \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/OffersByDeepLinkVerifierImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/OffersByDeepLinkVerifierImpl.kt new file mode 100644 index 00000000..aaca3e5e --- /dev/null +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/OffersByDeepLinkVerifierImpl.kt @@ -0,0 +1,32 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.impl.data.verifiers + +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLOffers +import io.velocitycareerlabs.api.entities.VCLResult +import io.velocitycareerlabs.api.entities.error.VCLError +import io.velocitycareerlabs.api.entities.error.VCLErrorCode +import io.velocitycareerlabs.impl.domain.verifiers.OffersByDeepLinkVerifier +import io.velocitycareerlabs.impl.utils.VCLLog + +class OffersByDeepLinkVerifierImpl: OffersByDeepLinkVerifier { + private val TAG = OffersByDeepLinkVerifierImpl::class.simpleName + + override fun verifyOffers( + offers: VCLOffers, + deepLink: VCLDeepLink, + completionBlock: (VCLResult) -> Unit + ) { + offers.all.find { it.issuerId != deepLink.did }?.let { mismatchedOffer -> + VCLLog.e(TAG, "mismatched offer: ${mismatchedOffer.payload} \ndeepLink: ${deepLink.value}") + completionBlock(VCLResult.Failure(VCLError(errorCode = VCLErrorCode.MismatchedOfferIssuerDid.value))) + } ?: run { + completionBlock(VCLResult.Success(true)) + } + } +} \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/PresentationRequestByDeepLinkVerifierImpl.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/PresentationRequestByDeepLinkVerifierImpl.kt new file mode 100644 index 00000000..25b846c5 --- /dev/null +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/data/verifiers/PresentationRequestByDeepLinkVerifierImpl.kt @@ -0,0 +1,34 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.impl.data.verifiers + +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLPresentationRequest +import io.velocitycareerlabs.api.entities.VCLResult +import io.velocitycareerlabs.api.entities.error.VCLError +import io.velocitycareerlabs.api.entities.error.VCLErrorCode +import io.velocitycareerlabs.impl.domain.verifiers.PresentationRequestByDeepLinkVerifier +import io.velocitycareerlabs.impl.utils.VCLLog + +class PresentationRequestByDeepLinkVerifierImpl: PresentationRequestByDeepLinkVerifier { + private val TAG = PresentationRequestByDeepLinkVerifierImpl::class.simpleName + + override fun verifyPresentationRequest( + presentationRequest: VCLPresentationRequest, + deepLink: VCLDeepLink, + completionBlock: (VCLResult) -> Unit + ) { + if (presentationRequest.iss == deepLink.did) { + completionBlock(VCLResult.Success(true)) + } else { + VCLLog.e(TAG, "presentation request: ${presentationRequest.jwt.encodedJwt} \ndeepLink: ${deepLink.value}") + completionBlock(VCLResult.Failure( + VCLError(errorCode = VCLErrorCode.MismatchedPresentationRequestInspectorDid.value) + )) + } + } +} \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/domain/repositories/FinalizeOffersRepository.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/repositories/FinalizeOffersRepository.kt index ba2ec505..997512a9 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/domain/repositories/FinalizeOffersRepository.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/repositories/FinalizeOffersRepository.kt @@ -17,6 +17,6 @@ internal interface FinalizeOffersRepository { finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, sessionToken: VCLToken, proof: VCLJwt, - completionBlock: (VCLResult>) -> Unit + completionBlock: (VCLResult>) -> Unit ) } \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/domain/utils/CredentialDidVerifier.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialDidVerifier.kt similarity index 79% rename from VCL/src/main/java/io/velocitycareerlabs/impl/domain/utils/CredentialDidVerifier.kt rename to VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialDidVerifier.kt index 017f256f..04d9c119 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/domain/utils/CredentialDidVerifier.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialDidVerifier.kt @@ -5,15 +5,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.velocitycareerlabs.impl.domain.utils +package io.velocitycareerlabs.impl.domain.verifiers import io.velocitycareerlabs.api.entities.VCLFinalizeOffersDescriptor +import io.velocitycareerlabs.api.entities.VCLJwt import io.velocitycareerlabs.api.entities.VCLJwtVerifiableCredentials import io.velocitycareerlabs.api.entities.VCLResult internal interface CredentialDidVerifier { fun verifyCredentials( - jwtEncodedCredentials: List, + jwtCredentials: List, finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, completionBlock: (VCLResult) -> Unit ) diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/domain/utils/CredentialIssuerVerifier.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialIssuerVerifier.kt similarity index 76% rename from VCL/src/main/java/io/velocitycareerlabs/impl/domain/utils/CredentialIssuerVerifier.kt rename to VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialIssuerVerifier.kt index 849a984c..a2ea2474 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/domain/utils/CredentialIssuerVerifier.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialIssuerVerifier.kt @@ -5,14 +5,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.velocitycareerlabs.impl.domain.utils +package io.velocitycareerlabs.impl.domain.verifiers import io.velocitycareerlabs.api.entities.VCLFinalizeOffersDescriptor +import io.velocitycareerlabs.api.entities.VCLJwt import io.velocitycareerlabs.api.entities.VCLResult internal interface CredentialIssuerVerifier { fun verifyCredentials( - jwtEncodedCredentials: List, + jwtCredentials: List, finalizeOffersDescriptor: VCLFinalizeOffersDescriptor, completionBlock: (VCLResult) -> Unit ) diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialManifestByDeepLinkVerifier.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialManifestByDeepLinkVerifier.kt new file mode 100644 index 00000000..969d6cbe --- /dev/null +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialManifestByDeepLinkVerifier.kt @@ -0,0 +1,19 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.impl.domain.verifiers + +import io.velocitycareerlabs.api.entities.VCLCredentialManifest +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLResult + +interface CredentialManifestByDeepLinkVerifier { + fun verifyCredentialManifest( + credentialManifest: VCLCredentialManifest, + deepLink: VCLDeepLink, + completionBlock: (VCLResult) -> Unit + ) +} \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialsByDeepLinkVerifier.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialsByDeepLinkVerifier.kt new file mode 100644 index 00000000..3451c8e8 --- /dev/null +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/CredentialsByDeepLinkVerifier.kt @@ -0,0 +1,19 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.impl.domain.verifiers + +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLJwt +import io.velocitycareerlabs.api.entities.VCLResult + +interface CredentialsByDeepLinkVerifier { + fun verifyCredentials( + jwtCredentials: List, + deepLink: VCLDeepLink, + completionBlock: (VCLResult) -> Unit + ) +} \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/OffersByDeepLinkVerifier.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/OffersByDeepLinkVerifier.kt new file mode 100644 index 00000000..5bff1738 --- /dev/null +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/OffersByDeepLinkVerifier.kt @@ -0,0 +1,19 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.impl.domain.verifiers + +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLOffers +import io.velocitycareerlabs.api.entities.VCLResult + +interface OffersByDeepLinkVerifier { + fun verifyOffers( + offers: VCLOffers, + deepLink: VCLDeepLink, + completionBlock: (VCLResult) -> Unit + ) +} \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/PresentationRequestByDeepLinkVerifier.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/PresentationRequestByDeepLinkVerifier.kt new file mode 100644 index 00000000..966d2a27 --- /dev/null +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/domain/verifiers/PresentationRequestByDeepLinkVerifier.kt @@ -0,0 +1,19 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.impl.domain.verifiers + +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLPresentationRequest +import io.velocitycareerlabs.api.entities.VCLResult + +interface PresentationRequestByDeepLinkVerifier { + fun verifyPresentationRequest( + presentationRequest: VCLPresentationRequest, + deepLink: VCLDeepLink, + completionBlock: (VCLResult) -> Unit + ) +} \ No newline at end of file diff --git a/VCL/src/main/java/io/velocitycareerlabs/impl/extensions/StringExtensions.kt b/VCL/src/main/java/io/velocitycareerlabs/impl/extensions/StringExtensions.kt index 00c7df43..748ee0af 100644 --- a/VCL/src/main/java/io/velocitycareerlabs/impl/extensions/StringExtensions.kt +++ b/VCL/src/main/java/io/velocitycareerlabs/impl/extensions/StringExtensions.kt @@ -9,7 +9,10 @@ package io.velocitycareerlabs.impl.extensions import android.util.Base64 import io.velocitycareerlabs.api.entities.VCLDidJwk +import io.velocitycareerlabs.api.entities.VCLJwt import io.velocitycareerlabs.api.entities.VCLPublicJwk +import io.velocitycareerlabs.api.entities.VCLResult +import io.velocitycareerlabs.api.entities.error.VCLError import io.velocitycareerlabs.impl.utils.VCLLog import org.json.JSONArray import org.json.JSONObject @@ -19,6 +22,7 @@ import java.net.URLEncoder import java.text.ParseException import java.text.SimpleDateFormat import java.util.* +import java.util.concurrent.CompletableFuture import java.util.regex.Pattern //internal fun String.isUrlEquivalentTo(url: String): Boolean { @@ -128,6 +132,22 @@ internal fun String.toJsonArray(): JSONArray? { internal fun String.toPublicJwk() = VCLPublicJwk(this.removePrefix(VCLDidJwk.DidJwkPrefix).decodeBase64()) +internal fun String.toJwtList(): List? { + this.toJsonArray()?.toList()?.let { encodedCredentialList -> + val jwtCredentials = mutableListOf() + val completableFutures = encodedCredentialList.map { jwtEncodedCredential -> + CompletableFuture.supplyAsync { + jwtCredentials.add(VCLJwt(jwtEncodedCredential as? String ?: "")) + } + } + val allFutures = CompletableFuture.allOf(*completableFutures.toTypedArray()) + allFutures.join() + + return jwtCredentials + } + return null +} + internal fun randomString(length: Int): String = List(length) { (('a'..'z') + ('A'..'Z') + ('0'..'9')).random() diff --git a/VCL/src/test/java/io/velocitycareerlabs/entities/VCLCredentialManifestDescriptorByDeepLinkTest.kt b/VCL/src/test/java/io/velocitycareerlabs/entities/VCLCredentialManifestDescriptorByDeepLinkTest.kt index 335edb7c..2253a2c4 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/entities/VCLCredentialManifestDescriptorByDeepLinkTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/entities/VCLCredentialManifestDescriptorByDeepLinkTest.kt @@ -9,6 +9,7 @@ package io.velocitycareerlabs.entities import io.velocitycareerlabs.api.entities.VCLCredentialManifestDescriptorByDeepLink import io.velocitycareerlabs.api.entities.VCLIssuingType +import io.velocitycareerlabs.api.entities.VCLPushDelegate import io.velocitycareerlabs.impl.extensions.decode import io.velocitycareerlabs.impl.extensions.isUrlEquivalentTo import io.velocitycareerlabs.infrastructure.resources.valid.CredentialManifestDescriptorMocks @@ -29,7 +30,8 @@ internal class VCLCredentialManifestDescriptorByDeepLinkTest { fun testCredentialManifestDescriptorFullValidByDeepLinkSuccess() { subject = VCLCredentialManifestDescriptorByDeepLink( deepLink = CredentialManifestDescriptorMocks.DeepLink, - issuingType = VCLIssuingType.Career + issuingType = VCLIssuingType.Career, + pushDelegate = VCLPushDelegate(pushUrl = "some url", pushToken = "some token") ) assert( @@ -37,6 +39,8 @@ internal class VCLCredentialManifestDescriptorByDeepLinkTest { ?.isUrlEquivalentTo(CredentialManifestDescriptorMocks.DeepLinkRequestUri.decode()) == true ) assert(subject.did == CredentialManifestDescriptorMocks.IssuerDid) + assert(subject.pushDelegate?.pushUrl == "some url") + assert(subject.pushDelegate?.pushToken == "some token") } @After diff --git a/VCL/src/test/java/io/velocitycareerlabs/entities/VCLCredentialManifestTest.kt b/VCL/src/test/java/io/velocitycareerlabs/entities/VCLCredentialManifestTest.kt index 5850801f..c8f2712e 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/entities/VCLCredentialManifestTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/entities/VCLCredentialManifestTest.kt @@ -36,7 +36,8 @@ class VCLCredentialManifestTest { fun testProps() { assert(subject.iss == "did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA") assert(subject.did == "did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA") - assert(subject.issuerId == "https://devagent.velocitycareerlabs.io/api/holder/v0.6/org/did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA") + assert(subject.issuerId == "did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA") + assert(subject.aud == "https://devagent.velocitycareerlabs.io/api/holder/v0.6/org/did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA") assert(subject.exchangeId == "645e315309237c760ac022b1") assert(subject.presentationDefinitionId == "645e315309237c760ac022b1.6384a3ad148b1991687f67c9") assert(subject.finalizeOffersUri == "https://devagent.velocitycareerlabs.io/api/holder/v0.6/org/did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA/issue/finalize-offers") diff --git a/VCL/src/test/java/io/velocitycareerlabs/entities/VCLFinalizeOffersDescriptorTest.kt b/VCL/src/test/java/io/velocitycareerlabs/entities/VCLFinalizeOffersDescriptorTest.kt index 1e00410b..d8b4adbd 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/entities/VCLFinalizeOffersDescriptorTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/entities/VCLFinalizeOffersDescriptorTest.kt @@ -38,7 +38,7 @@ class VCLFinalizeOffersDescriptorTest { private val offers = VCLOffers( payload = JSONObject(), - all = JSONArray(), + all = listOf(), responseCode = 200, sessionToken = VCLToken(value = ""), challenge = "" diff --git a/VCL/src/test/java/io/velocitycareerlabs/entities/VCLSubmissionTest.kt b/VCL/src/test/java/io/velocitycareerlabs/entities/VCLSubmissionTest.kt index aa12eac5..f50b6491 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/entities/VCLSubmissionTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/entities/VCLSubmissionTest.kt @@ -1,5 +1,8 @@ /** * Created by Michael Avoyan on 11/12/2022. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 */ package io.velocitycareerlabs.entities diff --git a/VCL/src/test/java/io/velocitycareerlabs/entities/VCLTokenTest.kt b/VCL/src/test/java/io/velocitycareerlabs/entities/VCLTokenTest.kt new file mode 100644 index 00000000..09fd70e7 --- /dev/null +++ b/VCL/src/test/java/io/velocitycareerlabs/entities/VCLTokenTest.kt @@ -0,0 +1,33 @@ +/** + * Created by Michael Avoyan on 24/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.entities + +import io.velocitycareerlabs.api.entities.VCLToken +import io.velocitycareerlabs.infrastructure.resources.valid.TokenMocks +import org.junit.Test + +class VCLTokenTest { + lateinit var subject: VCLToken + + @Test + fun testToken1() { + subject = VCLToken(value = TokenMocks.TokenStr) + + assert(subject.value == TokenMocks.TokenStr) + assert(subject.jwtValue.encodedJwt == TokenMocks.TokenStr) + assert(subject.expiresIn == 1704020514L) + } + + @Test + fun testToken2() { + subject = VCLToken(jwtValue = TokenMocks.TokenJwt) + + assert(subject.value == TokenMocks.TokenJwt.encodedJwt) + assert(subject.jwtValue.encodedJwt == TokenMocks.TokenJwt.encodedJwt) + assert(subject.expiresIn == 1704020514L) + } +} \ No newline at end of file diff --git a/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/DeepLinkMocks.kt b/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/DeepLinkMocks.kt index 879113ac..0ee4a7fa 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/DeepLinkMocks.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/DeepLinkMocks.kt @@ -24,16 +24,16 @@ class DeepLinkMocks { const val OpenidInitiateIssuanceStrDev = "openid-initiate-issuance://?issuer=$Issuer" - const val InspectorDid = "did:ion:EiByBvq95tfmhl41DOxJeaa26HjSxAUoz908PITFwMRDNA" + const val InspectorDid = "did:velocity:0xd4df29726d500f9b85bc6c7f1b3c021f16305692" const val PresentationRequestVendorOriginContext = "{\"SubjectKey\":{\"BusinessUnit\":\"ZC\",\"KeyCode\":\"54514480\"},\"Token\":\"832077a4\"}" var PresentationRequestRequestDecodedUriStr = - "https://agent.velocitycareerlabs.io/api/holder/v0.6/org/$InspectorDid/inspect/get-presentation-request?id=62e0e80c5ebfe73230b0becc&inspectorDid=${InspectorDid.encode()}&vendorOriginContext=%7B%22SubjectKey%22%3A%7B%22BusinessUnit%22%3A%22ZC%22,%22KeyCode%22%3A%2254514480%22%7D,%22Token%22%3A%22832077a4%22%7D".decode() + "https://agent.velocitycareerlabs.io/api/holder/v0.6/org/$InspectorDid/inspect/get-presentation-request?id=62e0e80c5ebfe73230b0becc&inspectorDid=${InspectorDid}&vendorOriginContext=%7B%22SubjectKey%22%3A%7B%22BusinessUnit%22%3A%22ZC%22,%22KeyCode%22%3A%2254514480%22%7D,%22Token%22%3A%22832077a4%22%7D".decode() const val PresentationRequestRequestUriStr = - "https%3A%2F%2Fagent.velocitycareerlabs.io%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3AEiByBvq95tfmhl41DOxJeaa26HjSxAUoz908PITFwMRDNA%2Finspect%2Fget-presentation-request%3Fid%3D62e0e80c5ebfe73230b0becc&inspectorDid=did%3Aion%3AEiByBvq95tfmhl41DOxJeaa26HjSxAUoz908PITFwMRDNA&vendorOriginContext=%7B%22SubjectKey%22%3A%7B%22BusinessUnit%22%3A%22ZC%22,%22KeyCode%22%3A%2254514480%22%7D,%22Token%22%3A%22832077a4%22%7D" + "https%3A%2F%2Fagent.velocitycareerlabs.io%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Avelocity%3A0xd4df29726d500f9b85bc6c7f1b3c021f16305692%2Finspect%2Fget-presentation-request%3Fid%3D62e0e80c5ebfe73230b0becc%26inspectorDid%3Ddid%3Avelocity%3A0xd4df29726d500f9b85bc6c7f1b3c021f16305692%26vendorOriginContext%3D%7B%22SubjectKey%22%3A%7B%22BusinessUnit%22%3A%22ZC%22%2C%22KeyCode%22%3A%2254514480%22%7D%2C%22Token%22%3A%22832077a4%22%7D" const val PresentationRequestDeepLinkDevNetStr = "$DevNetProtocol://inspect?request_uri=$PresentationRequestRequestUriStr" @@ -42,13 +42,13 @@ class DeepLinkMocks { const val PresentationRequestDeepLinkMainNetStr = "$MainNetProtocol://inspect?request_uri=$PresentationRequestRequestUriStr" - const val IssuerDid = "did:velocity:0xd4df29726d500f9b85bc6c7f1b3c021f16305692" + const val IssuerDid = "did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA" const val CredentialManifestRequestDecodedUriStr = - "https://devagent.velocitycareerlabs.io/api/holder/v0.6/org/$IssuerDid/issue/get-credential-manifest?id=611b5836e93d08000af6f1bc&credential_types=PastEmploymentPosition&issuerDid=did:velocity:0xd4df29726d500f9b85bc6c7f1b3c021f16305692" + "https://devagent.velocitycareerlabs.io/api/holder/v0.6/org/$IssuerDid/issue/get-credential-manifest?id=611b5836e93d08000af6f1bc&credential_types=PastEmploymentPosition&issuerDid=$IssuerDid" const val CredentialManifestRequestUriStr = - "https%3A%2F%2Fdevagent.velocitycareerlabs.io%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Avelocity%3A0xd4df29726d500f9b85bc6c7f1b3c021f16305692%2Fissue%2Fget-credential-manifest%3Fid%3D611b5836e93d08000af6f1bc%26credential_types%3DPastEmploymentPosition%26issuerDid%3Ddid%3Avelocity%3A0xd4df29726d500f9b85bc6c7f1b3c021f16305692" + "https%3A%2F%2Fdevagent.velocitycareerlabs.io%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3AEiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA%2Fissue%2Fget-credential-manifest%3Fid%3D611b5836e93d08000af6f1bc%26credential_types%3DPastEmploymentPosition%26issuerDid%3Ddid%3Aion%3AEiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA" const val CredentialManifestDeepLinkDevNetStr = "$DevNetProtocol://issue?request_uri=$CredentialManifestRequestUriStr" const val CredentialManifestDeepLinkTestNetStr = "$TestNetProtocol://issue?request_uri=$CredentialManifestRequestUriStr" diff --git a/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/GenerateOffersMocks.kt b/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/GenerateOffersMocks.kt index c5ea1a56..9d22c954 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/GenerateOffersMocks.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/GenerateOffersMocks.kt @@ -9,10 +9,15 @@ package io.velocitycareerlabs.infrastructure.resources.valid class GenerateOffersMocks { companion object { - const val Offers = "[{\"offer1\":\"some offer 1\"},{\"offer2\":\"some offer 2\"}]" + const val Offer1 = "{\"offer1\":\"some offer 1\"}" + const val Offer2 = "{\"offer2\":\"some offer 2\"}" + const val Offers = "[$Offer1,$Offer2]" const val Challenge = "CSASLD10103aa_RW" const val GeneratedOffers = "{\"offers\":$Offers,\"challenge\":\"$Challenge\"}" const val GeneratedOffersEmptyJsonObj = "{}" const val GeneratedOffersEmptyJsonArr = "[]" + + const val RealOffers = + "{\"offers\":[{\"type\":[\"EducationDegreeStudyV1.0\"],\"issuer\":{\"id\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"name\":\"Coca Cola\",\"image\":\"https:\\/\\/example.com\\/logo.jpg\"},\"credentialSubject\":{\"@context\":[\"https:\\/\\/lib.velocitynetwork.foundation\\/layer1-context-v1.0.json\"],\"type\":\"EducationDegree\",\"institution\":{\"type\":\"Organization\",\"name\":\"XYZ University with brand\",\"identifier\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"place\":{\"type\":\"Place\",\"addressLocality\":\"Chicago\",\"addressRegion\":\"US-IL\",\"addressCountry\":\"US\"},\"image\":\"https:\\/\\/www.clipartmax.com\\/png\\/small\\/180-1809319_picture-300-x-300-pixel.png\"},\"school\":{\"type\":\"Organization\",\"name\":\"School of nursing\",\"place\":{\"type\":\"Place\",\"name\":\"Florida Branch\",\"addressLocality\":\"Fort Myers\",\"addressRegion\":\"US-FL\",\"addressCountry\":\"US\"}},\"programName\":\"RN to BSN\",\"programType\":\"1 year full time program\",\"programMode\":\"Online\",\"degreeName\":\"EducationDegreeStudyV1.0\",\"degreeMajor\":[\"Nursing\",\"English\"],\"degreeMinor\":[\"Low\",\"Sociology\"],\"description\":\"Starfield College’s RN to BSN Online Option allows working nurses to advance your degree in as few as 3 semesters.\\n If you are a registered nurse with an active RN license looking to earn your BSN, Chamberlain's online RN to BSN option can help prepare you for the next step in your career.\\n Our RN to BSN online option allows you to earn your degree while you work.\",\"alignment\":[{\"type\":\"AlignmentObject\",\"targetName\":\"Bachelor of Science in Nursing, RN to BSN\",\"targetUrl\":\"https:\\/\\/credentialfinder.org\\/credential\\/5769\\/Bachelor_of_Science_in_Nursing,_RN_to_BSN\",\"targetDescription\":\"The RN to BSN completion program has been developed to address the specific needs of working registered nurses who return to the university to earn the BSN.\",\"targetFramework\":\"Credential Engine's Credential Registry\"}],\"startDate\":\"2022-10\"},\"credentialSchema\":{\"type\":\"JsonSchemaValidator2018\",\"id\":\"https:\\/\\/devlib.velocitynetwork.foundation\\/schemas\\/education-degree-study-v1.0.schema.json\"},\"offerCreationDate\":\"2023-08-27T09:27:15.084Z\",\"offerExpirationDate\":\"2024-08-27T09:27:15.084Z\",\"offerId\":\"6LsoXUY5kT2RZ0tXfuEN-\",\"id\":\"6578897c770790a0fdff7c90\",\"exchangeId\":\"6578896f770790a0fdff7c8f\",\"hash\":\"f3e9a8bd5800e9c293e3103daeca6cfe27b1646431fd917ad667c23b4bdc8523\"},{\"type\":[\"EducationDegreeStudyV1.0\"],\"issuer\":{\"id\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"name\":\"Coca Cola\",\"image\":\"https:\\/\\/example.com\\/logo.jpg\"},\"credentialSubject\":{\"@context\":[\"https:\\/\\/lib.velocitynetwork.foundation\\/layer1-context-v1.0.json\"],\"type\":\"EducationDegree\",\"institution\":{\"type\":\"Organization\",\"name\":\"XYZ University\",\"identifier\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"place\":{\"type\":\"Place\",\"addressLocality\":\"Chicago\",\"addressRegion\":\"US-IL\",\"addressCountry\":\"US\"},\"image\":\"https:\\/\\/www.clipartmax.com\\/png\\/small\\/180-1809319_picture-300-x-300-pixel.png\"},\"school\":{\"type\":\"Organization\",\"name\":\"School of nursing\",\"place\":{\"type\":\"Place\",\"name\":\"Florida Branch\",\"addressLocality\":\"Fort Myers\",\"addressRegion\":\"US-FL\",\"addressCountry\":\"US\"}},\"programName\":\"RN to BSN\",\"programType\":\"1 year full time program\",\"programMode\":\"Online\",\"degreeName\":\"EducationDegreeStudyV1.0\",\"degreeMajor\":[\"Nursing\",\"English\"],\"degreeMinor\":[\"Low\",\"Sociology\"],\"description\":\"Starfield College’s RN to BSN Online Option allows working nurses to advance your degree in as few as 3 semesters.\\n If you are a registered nurse with an active RN license looking to earn your BSN, Chamberlain's online RN to BSN option can help prepare you for the next step in your career.\\n Our RN to BSN online option allows you to earn your degree while you work.\",\"alignment\":[{\"type\":\"AlignmentObject\",\"targetName\":\"Bachelor of Science in Nursing, RN to BSN\",\"targetUrl\":\"https:\\/\\/credentialfinder.org\\/credential\\/5769\\/Bachelor_of_Science_in_Nursing,_RN_to_BSN\",\"targetDescription\":\"The RN to BSN completion program has been developed to address the specific needs of working registered nurses who return to the university to earn the BSN.\",\"targetFramework\":\"Credential Engine's Credential Registry\"}],\"startDate\":\"2022-10\"},\"credentialSchema\":{\"type\":\"JsonSchemaValidator2018\",\"id\":\"https:\\/\\/devlib.velocitynetwork.foundation\\/schemas\\/education-degree-study-v1.0.schema.json\"},\"offerCreationDate\":\"2023-08-27T09:21:49.729Z\",\"offerExpirationDate\":\"2024-08-27T09:21:49.729Z\",\"offerId\":\"gcid6O7gOOkGnAmqIrcy3\",\"id\":\"6578897c770790a0fdff7c91\",\"exchangeId\":\"6578896f770790a0fdff7c8f\",\"hash\":\"9cb721e57266f6783158dc7ee68640240929fca8fc6b0299448467ffffa45b68\"},{\"type\":[\"EducationDegreeStudyV1.0\"],\"issuer\":{\"id\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\"},\"credentialSubject\":{\"@context\":[\"https:\\/\\/lib.velocitynetwork.foundation\\/layer1-context-v1.0.json\"],\"type\":\"EducationDegree\",\"institution\":{\"type\":\"Organization\",\"name\":\"XYZ University\",\"identifier\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"place\":{\"type\":\"Place\",\"addressLocality\":\"Chicago\",\"addressRegion\":\"US-IL\",\"addressCountry\":\"US\"},\"image\":\"https:\\/\\/www.clipartmax.com\\/png\\/small\\/180-1809319_picture-300-x-300-pixel.png\"},\"school\":{\"type\":\"Organization\",\"name\":\"School of nursing\",\"place\":{\"type\":\"Place\",\"name\":\"Florida Branch\",\"addressLocality\":\"Fort Myers\",\"addressRegion\":\"US-FL\",\"addressCountry\":\"US\"}},\"programName\":\"RN to BSN\",\"programType\":\"1 year full time program\",\"programMode\":\"Online\",\"degreeName\":\"EducationDegreeStudyV1.0\",\"degreeMajor\":[\"Nursing\",\"English\"],\"degreeMinor\":[\"Low\",\"Sociology\"],\"description\":\"Starfield College’s RN to BSN Online Option allows working nurses to advance your degree in as few as 3 semesters.\\n If you are a registered nurse with an active RN license looking to earn your BSN, Chamberlain's online RN to BSN option can help prepare you for the next step in your career.\\n Our RN to BSN online option allows you to earn your degree while you work.\",\"alignment\":[{\"type\":\"AlignmentObject\",\"targetName\":\"Bachelor of Science in Nursing, RN to BSN\",\"targetUrl\":\"https:\\/\\/credentialfinder.org\\/credential\\/5769\\/Bachelor_of_Science_in_Nursing,_RN_to_BSN\",\"targetDescription\":\"The RN to BSN completion program has been developed to address the specific needs of working registered nurses who return to the university to earn the BSN.\",\"targetFramework\":\"Credential Engine's Credential Registry\"}],\"startDate\":\"2022-10\"},\"credentialSchema\":{\"type\":\"JsonSchemaValidator2018\",\"id\":\"https:\\/\\/devlib.velocitynetwork.foundation\\/schemas\\/education-degree-study-v1.0.schema.json\"},\"offerCreationDate\":\"2023-07-08T20:39:02.076Z\",\"offerExpirationDate\":\"2024-07-08T20:39:02.076Z\",\"offerId\":\"-GJXLBwj50QOM39JIRC4c\",\"id\":\"6578897c770790a0fdff7c92\",\"exchangeId\":\"6578896f770790a0fdff7c8f\",\"hash\":\"9cb721e57266f6783158dc7ee68640240929fca8fc6b0299448467ffffa45b68\"},{\"type\":[\"OpenBadgeCredential\"],\"issuer\":{\"id\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\"},\"credentialSubject\":{\"type\":\"AchievementSubject\",\"achievement\":{\"id\":\"VNF0007\",\"type\":\"Achievement\",\"name\":\"I attended the 2023 Velocity Network Foundation General Assembly\",\"achievementType\":\"Badge\",\"criteria\":{\"narrative\":\"Attendance at the 2023 Velocity Network Foundation General Assembly\"},\"description\":\"Badge awarded to individuals who attended the Velocity Network Foundation General Assembly\",\"image\":{\"id\":\"https:\\/\\/assets.velocitynetwork.foundation\\/developers\\/general\\/vnf-general-assembly-2023.png\",\"type\":\"Image\"},\"alignment\":[{\"type\":\"Alignment\",\"targetName\":\"Velocity Network Foundation Website\",\"targetUrl\":\"https:\\/\\/www.velocitynetwork.foundation\\/\",\"targetFramework\":\"Target Framework\"}]},\"source\":{\"id\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"name\":\"University of Massachusetts Amherst ion\",\"type\":\"Profile\"}},\"credentialSchema\":{\"type\":\"JsonSchemaValidator2018\",\"id\":\"https:\\/\\/devlib.velocitynetwork.foundation\\/schemas\\/open-badge-credential.schema.json\"},\"offerCreationDate\":\"2023-06-14T13:00:09.898Z\",\"offerExpirationDate\":\"2030-01-01T00:00:00.000Z\",\"offerId\":\"Adam_OBC_3\",\"id\":\"6578897c770790a0fdff7c93\",\"exchangeId\":\"6578896f770790a0fdff7c8f\",\"hash\":\"056fdb38b1279d8d3b074ccdd3d35aeff6740c0b9db4aaca6d04433dc2e4da5e\"},{\"type\":[\"EducationDegreeStudyV1.0\"],\"issuer\":{\"id\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\"},\"credentialSubject\":{\"@context\":\"https:\\/\\/velocitynetwork.foundation\\/contexts\\/education-degree\",\"institution\":{\"name\":\"Image instead of Logo\",\"identifier\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"place\":{\"addressLocality\":\"Cardiff\",\"addressRegion\":\"GB-WLS\",\"addressCountry\":\"GB\",\"type\":\"Place\"},\"image\":\"https:\\/\\/upload.wikimedia.org\\/wikipedia\\/commons\\/8\\/84\\/Mozilla_Firefox_3.5_logo.png\",\"type\":\"Organization\"},\"school\":{\"name\":\"College of Liberal Arts and Sciences\",\"place\":{\"name\":\"diff photo\",\"addressLocality\":\"Barry\",\"addressRegion\":\"GB-WLS\",\"addressCountry\":\"GB\",\"type\":\"Place\"},\"type\":\"Organization\"},\"programName\":\"Undergraduate program in psychology\",\"programType\":\"60 credits full program\",\"degreeName\":\"Image instead of Logo\",\"degreeMajor\":[\"Psychology\"],\"degreeMinor\":[\"English\"],\"description\":\"The program prepares students for graduate study, and so emphasizes practice in the research techniques which are used in graduate school and professionally later on.\",\"alignment\":[{\"targetName\":\"Bachelor of Science (BS) in Psychology - General Psychology\",\"targetUrl\":\"https:\\/\\/nces.ed.gov\\/ipeds\\/cipcode\\/cipdetail.aspx?y=55&cipid=88527\",\"targetDescription\":\"A general program that focuses on the scientific study of individual and collective behavior, the physical and environmental bases of behavior, and the analysis and treatment of behavior problems and disorders. Includes instruction in the principles of the various subfields of psychology, research methods, and psychological assessment and testing methods.\",\"targetCode\":\"42.0101\",\"targetFramework\":\"CIP - Classification of Instructional Programs\",\"type\":\"AlignmentObject\"}],\"grade\":{\"scoreMethod\":{\"scoreMethodDescription\":\"Poor, Fair, Good, Very Good\",\"passingScore\":\"Fair\",\"scoreMethodType\":\"CriterionReferenced\"},\"result\":\"Study\",\"passFail\":\"Pass\",\"scoreValue\":\"1.5\",\"type\":\"ScoreMethod\"},\"registrationDate\":\"2021-05-15\",\"startDate\":\"2021-10-01\",\"type\":\"EducationDegree\"},\"credentialSchema\":{\"type\":\"JsonSchemaValidator2018\",\"id\":\"https:\\/\\/devlib.velocitynetwork.foundation\\/schemas\\/education-degree-study-v1.0.schema.json\"},\"offerCreationDate\":\"2022-12-06T11:28:43.574Z\",\"offerExpirationDate\":\"2030-01-01T00:00:00.000Z\",\"offerId\":\"XE50THPBfzAYDKmB1GTvwv\",\"id\":\"6578897c770790a0fdff7c94\",\"exchangeId\":\"6578896f770790a0fdff7c8f\",\"hash\":\"19aa50f902464c25c11458dc19d55a6a45b4232156b5de85443dc922edfc9e64\"},{\"type\":[\"EducationDegreeStudyV1.0\"],\"issuer\":{\"id\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"name\":\"Coca Cola\",\"image\":\"https:\\/\\/example.com\\/logo.jpg\"},\"credentialSubject\":{\"@context\":[\"https:\\/\\/lib.velocitynetwork.foundation\\/layer1-context-v1.0.json\"],\"type\":\"EducationDegree\",\"institution\":{\"type\":\"Organization\",\"name\":\"XYZ University with brand\",\"identifier\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"place\":{\"type\":\"Place\",\"addressLocality\":\"Chicago\",\"addressRegion\":\"US-IL\",\"addressCountry\":\"US\"},\"image\":\"https:\\/\\/www.clipartmax.com\\/png\\/small\\/180-1809319_picture-300-x-300-pixel.png\"},\"school\":{\"type\":\"Organization\",\"name\":\"School of nursing\",\"place\":{\"type\":\"Place\",\"name\":\"Florida Branch\",\"addressLocality\":\"Fort Myers\",\"addressRegion\":\"US-FL\",\"addressCountry\":\"US\"}},\"programName\":\"RN to BSN\",\"programType\":\"1 year full time program\",\"programMode\":\"Online\",\"degreeName\":\"EducationDegreeStudyV1.0\",\"degreeMajor\":[\"Nursing\",\"English\"],\"degreeMinor\":[\"Low\",\"Sociology\"],\"description\":\"Starfield College’s RN to BSN Online Option allows working nurses to advance your degree in as few as 3 semesters.\\n If you are a registered nurse with an active RN license looking to earn your BSN, Chamberlain's online RN to BSN option can help prepare you for the next step in your career.\\n Our RN to BSN online option allows you to earn your degree while you work.\",\"alignment\":[{\"type\":\"AlignmentObject\",\"targetName\":\"Bachelor of Science in Nursing, RN to BSN\",\"targetUrl\":\"https:\\/\\/credentialfinder.org\\/credential\\/5769\\/Bachelor_of_Science_in_Nursing,_RN_to_BSN\",\"targetDescription\":\"The RN to BSN completion program has been developed to address the specific needs of working registered nurses who return to the university to earn the BSN.\",\"targetFramework\":\"Credential Engine's Credential Registry\"}],\"startDate\":\"2022-10\"},\"credentialSchema\":{\"type\":\"JsonSchemaValidator2018\",\"id\":\"https:\\/\\/devlib.velocitynetwork.foundation\\/schemas\\/education-degree-study-v1.0.schema.json\"},\"offerCreationDate\":\"2023-08-28T07:25:45.000Z\",\"offerExpirationDate\":\"2025-01-04T08:34:00.162Z\",\"offerId\":\"Offer-2.1 (push)\",\"id\":\"6578897c770790a0fdff7c95\",\"exchangeId\":\"6578896f770790a0fdff7c8f\",\"hash\":\"f3e9a8bd5800e9c293e3103daeca6cfe27b1646431fd917ad667c23b4bdc8523\"},{\"type\":[\"EducationDegreeStudyV1.0\"],\"issuer\":{\"id\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\"},\"credentialSubject\":{\"@context\":\"https:\\/\\/velocitynetwork.foundation\\/contexts\\/education-degree\",\"institution\":{\"name\":\"University of Massachusetts Amherst ion\",\"identifier\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"place\":{\"addressLocality\":\"Cardiff\",\"addressRegion\":\"GB-WLS\",\"addressCountry\":\"GB\",\"type\":\"Place\"},\"type\":\"Organization\"},\"school\":{\"name\":\"College of Liberal Arts and Sciences\",\"place\":{\"name\":\"Graduate Studies Building\",\"addressLocality\":\"Barry\",\"addressRegion\":\"GB-WLS\",\"addressCountry\":\"GB\",\"type\":\"Place\"},\"type\":\"Organization\"},\"programName\":\"Undergraduate program in psychology\",\"programType\":\"60 credits full program\",\"degreeName\":\"StudyV1.0\",\"degreeMajor\":[\"Psychology\",\"Psychology2\"],\"degreeMinor\":[\"English\",\"Psychology2\"],\"description\":\"The program prepares students for graduate study, and so emphasizes practice in the research techniques which are used in graduate school and professionally later on.\",\"alignment\":[{\"targetName\":\"Bachelor of Science (BS) in Psychology - General Psychology\",\"targetUrl\":\"https:\\/\\/nces.ed.gov\\/ipeds\\/cipcode\\/cipdetail.aspx?y=55&cipid=88527\",\"targetDescription\":\"A general program that focuses on the scientific study of individual and collective behavior, the physical and environmental bases of behavior, and the analysis and treatment of behavior problems and disorders. Includes instruction in the principles of the various subfields of psychology, research methods, and psychological assessment and testing methods.\",\"targetCode\":\"42.0101\",\"targetFramework\":\"CIP - Classification of Instructional Programs\",\"type\":\"AlignmentObject\"}],\"registrationDate\":\"2021-05-15\",\"startDate\":\"2021-10-01\",\"type\":\"EducationDegree\"},\"credentialSchema\":{\"type\":\"JsonSchemaValidator2018\",\"id\":\"https:\\/\\/devlib.velocitynetwork.foundation\\/schemas\\/education-degree-study-v1.0.schema.json\"},\"offerCreationDate\":\"2022-12-06T11:28:43.574Z\",\"offerExpirationDate\":\"2030-01-01T00:00:00.000Z\",\"offerId\":\"XE50THPBfzAYDKmB1GTvw\",\"id\":\"6578897c770790a0fdff7c96\",\"exchangeId\":\"6578896f770790a0fdff7c8f\",\"hash\":\"10b0e97ed421df2e6ff0e8c033eb64dde12bc3327ad797a6909220fbfb9a7e45\"},{\"type\":[\"EducationDegreeRegistrationV1.0\"],\"issuer\":{\"id\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\"},\"credentialSubject\":{\"@context\":\"https:\\/\\/velocitynetwork.foundation\\/contexts\\/education-degree\",\"institution\":{\"name\":\"University of Massachusetts Amherst ion\",\"identifier\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"place\":{\"addressLocality\":\"Chicago\",\"addressRegion\":\"US-IL\",\"addressCountry\":\"US\",\"type\":\"Place\"},\"type\":\"Organization\"},\"school\":{\"name\":\"School of nursing\",\"place\":{\"name\":\"Florida Branch\",\"addressLocality\":\"Fort Myers\",\"addressRegion\":\"US-FL\",\"addressCountry\":\"US\",\"type\":\"Place\"},\"type\":\"Organization\"},\"programName\":\"RN to BSN\",\"programType\":\"1 year full time program\",\"programMode\":\"Online\",\"degreeName\":\"Student (RegistrationV1.0)\",\"degreeMajor\":[\"Nursing\",\"Nursing2\"],\"degreeMinor\":[\"Nursing\",\"Nursing\"],\"description\":\"Starfield College’s RN to BSN Online Option allows working nurses to advance your degree in as few as 3 semesters.\\n If you are a registered nurse with an active RN license looking to earn your BSN, Chamberlain's online RN to BSN option can help prepare you for the next step in your career.\\n Our RN to BSN online option allows you to earn your degree while you work.\",\"alignment\":[{\"targetName\":\"Bachelor of Science in Nursing, RN to BSN\",\"targetUrl\":\"https:\\/\\/credentialfinder.org\\/credential\\/5769\\/Bachelor_of_Science_in_Nursing,_RN_to_BSN\",\"targetDescription\":\"The RN to BSN completion program has been developed to address the specific needs of working registered nurses who return to the university to earn the BSN.\",\"targetFramework\":\"Credential Engine's Credential Registry\",\"type\":\"AlignmentObject\"}],\"registrationDate\":\"2011-05\",\"startDate\":\"2011-10\",\"type\":\"EducationDegree\"},\"credentialSchema\":{\"type\":\"JsonSchemaValidator2018\",\"id\":\"https:\\/\\/devlib.velocitynetwork.foundation\\/schemas\\/education-degree-registration-v1.0.schema.json\"},\"offerCreationDate\":\"2022-12-06T11:25:32.955Z\",\"offerExpirationDate\":\"2030-01-01T00:00:00.000Z\",\"offerId\":\"nH6b5jBMJ6XeL1nEk5qNa\",\"id\":\"6578897c770790a0fdff7c97\",\"exchangeId\":\"6578896f770790a0fdff7c8f\",\"hash\":\"6adae30b7023c54929961d9c55e5c5c5c54a4022bea03e74cdfacbdf6380be83\"},{\"type\":[\"EducationDegreeGraduationV1.0\"],\"issuer\":{\"id\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\"},\"credentialSubject\":{\"@context\":\"https:\\/\\/velocitynetwork.foundation\\/contexts\\/education-degree\",\"institution\":{\"name\":\"University of Massachusetts Amherst ion\",\"identifier\":\"did:ion:EiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbtJA\",\"place\":{\"addressLocality\":\"Chicago\",\"addressRegion\":\"US-IL\",\"addressCountry\":\"US\",\"type\":\"Place\"},\"type\":\"Organization\"},\"school\":{\"name\":\"School of nursing (GraduationV1.0)\",\"place\":{\"name\":\"Florida Branch\",\"addressLocality\":\"Fort Myers\",\"addressRegion\":\"US-FL\",\"addressCountry\":\"US\",\"type\":\"Place\"},\"type\":\"Organization\"},\"programName\":\"RN to BSN\",\"programType\":\"1 year full time program\",\"programMode\":\"Online\",\"degreeName\":\"Bachelor of Science (GraduationV1.0)\",\"degreeMajor\":[\"Nursing\",\"Nursing2\"],\"degreeMinor\":[\"Nursing\",\"Nursing2\"],\"description\":\"Starfield College’s RN to BSN Online Option allows working nurses to advance your degree in as few as 3 semesters.\\n If you are a registered nurse with an active RN license looking to earn your BSN, Chamberlain's online RN to BSN option can help prepare you for the next step in your career.\\n Our RN to BSN online option allows you to earn your degree while you work.\",\"alignment\":[{\"targetName\":\"Bachelor of Science in Nursing, RN to BSN\",\"targetUrl\":\"https:\\/\\/credentialfinder.org\\/credential\\/5769\\/Bachelor_of_Science_in_Nursing_RN_to_BSN\",\"targetDescription\":\"The RN to BSN completion program has been developed to address the specific needs of working registered nurses who return to the university to earn the BSN.\",\"targetFramework\":\"Credential Engine's Credential Registry\",\"type\":\"AlignmentObject\"}],\"startDate\":\"2011-08\",\"endDate\":\"2013-06\",\"conferredDate\":\"2014-01\",\"honors\":\"cum laude\",\"type\":\"EducationDegree\"},\"credentialSchema\":{\"type\":\"JsonSchemaValidator2018\",\"id\":\"https:\\/\\/devlib.velocitynetwork.foundation\\/schemas\\/education-degree-graduation-v1.0.schema.json\"},\"offerCreationDate\":\"2022-12-06T11:16:20.136Z\",\"offerExpirationDate\":\"2030-01-01T00:00:00.000Z\",\"offerId\":\"h9tpQcpjbJ19faZr_ySaR\",\"id\":\"6578897c770790a0fdff7c98\",\"exchangeId\":\"6578896f770790a0fdff7c8f\",\"hash\":\"854c1328e55387a500988809a6c7022f5098f5953d38a5795ad2ac285b61f5f1\"}],\"challenge\":\"tGQLKQEhvi-tiXr8\"}" } } \ No newline at end of file diff --git a/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/PresentationRequestMocks.kt b/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/PresentationRequestMocks.kt index 98da3f5e..45e1048f 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/PresentationRequestMocks.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/PresentationRequestMocks.kt @@ -20,15 +20,6 @@ class PresentationRequestMocks { const val EncodedPresentationRequestResponse = "{\"presentation_request\":\"$EncodedPresentationRequest\"}" - const val PresentationRequestJwtDecodedHeaderJson = - "{ \"typ\": \"JWT\", \"kid\": \"did:velocity:0xd4df29726d500f9b85bc6c7f1b3c021f16305692#key-1\", \"alg\": \"ES256K\" }" - - const val PresentationRequestJwtDecodedPayloadJson = - "{ \"exchange_id\": \"60ec1f766d274e00086748ca\", \"metadata\": { \"client_name\": \"Microsoft Corporation\", \"logo_uri\": \"https://agsol.com/wp-content/uploads/2018/09/new-microsoft-logo-SIZED-SQUARE.jpg\", \"tos_uri\": \"https://www.velocityexperiencecenter.com/terms-and-conditions-vnf\", \"max_retention_period\": \"6m\" }, \"presentation_definition\": { \"id\": \"60ec1f766d274e00086748ca.60ec143e6d274e00086748bc\", \"purpose\": \"Id Check\", \"format\": { \"jwt_vp\": { \"alg\": [ \"secp256k1\" ] } }, \"input_descriptors\": [ { \"id\": \"IdDocument\", \"schema\": [ { \"uri\": \"https://devservices.velocitycareerlabs.io/api/v0.6/schemas/id-document.v1.schema.json\" } ] }, { \"id\": \"Email\", \"schema\": [ { \"uri\": \"https://devservices.velocitycareerlabs.io/api/v0.6/schemas/email.schema.json\" } ] }, { \"id\": \"Phone\", \"schema\": [ { \"uri\": \"https://devservices.velocitycareerlabs.io/api/v0.6/schemas/phone.schema.json\" } ] }, { \"id\": \"PastEmploymentPosition\", \"schema\": [ { \"uri\": \"https://devservices.velocitycareerlabs.io/api/v0.6/schemas/past-employment-position.schema.json\" } ] }, { \"id\": \"CurrentEmploymentPosition\", \"schema\": [ { \"uri\": \"https://devservices.velocitycareerlabs.io/api/v0.6/schemas/current-employment-position.schema.json\" } ] }, { \"id\": \"EducationDegree\", \"schema\": [ { \"uri\": \"https://devservices.velocitycareerlabs.io/api/v0.6/schemas/education-degree.schema.json\" } ] } ] }, \"iss\": \"did:velocity:0xd4df29726d500f9b85bc6c7f1b3c021f16305692\", \"iat\": 1626087286, \"exp\": 1626692086, \"nbf\": 1626087286 }" - - const val PresentationRequestJwtSignature = - "BSQnMNNJyicCsh6zeh7k5GBHC6T9QgPNV4SHhSXsnz3sBJMwNBFz7v4axLCoCiKHtIxNj5" - val PresentationRequestJwt = VCLJwt( encodedJwt = EncodedPresentationRequest ) diff --git a/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/TokenMocks.kt b/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/TokenMocks.kt new file mode 100644 index 00000000..e8a2233c --- /dev/null +++ b/VCL/src/test/java/io/velocitycareerlabs/infrastructure/resources/valid/TokenMocks.kt @@ -0,0 +1,17 @@ +/** + * Created by Michael Avoyan on 24/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.infrastructure.resources.valid + +import io.velocitycareerlabs.api.entities.VCLJwt + +class TokenMocks { + companion object { + const val TokenStr = + "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiI2NTg4MGY5ZThkMjY3NWE0NTBhZDVhYjgiLCJpc3MiOiJkaWQ6aW9uOkVpQXBNTGRNYjROUGI4c2FlOS1oWEdIUDc5VzFnaXNBcFZTRTgwVVNQRWJ0SkEiLCJhdWQiOiJkaWQ6aW9uOkVpQXBNTGRNYjROUGI4c2FlOS1oWEdIUDc5VzFnaXNBcFZTRTgwVVNQRWJ0SkEiLCJleHAiOjE3MDQwMjA1MTQsInN1YiI6IjYzODZmODI0ZTc3NDc4OWM0MDNjOTZhMCIsImlhdCI6MTcwMzQxNTcxNH0.AJwKvQ_YNviFTjcuoJUR7ZHFEIbKY9zLCJv4DfC_PPk3Q-15rwKucYy8GdlfKnHLioBA5X37lpG-js8EztEKDg" + val TokenJwt = VCLJwt(encodedJwt = TokenMocks.TokenStr) + } +} \ No newline at end of file diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/CountriesUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/CountriesUseCaseTest.kt index e93293ba..0cfbe545 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/CountriesUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/CountriesUseCaseTest.kt @@ -7,7 +7,9 @@ package io.velocitycareerlabs.usecases +import android.os.Build import io.velocitycareerlabs.api.entities.VCLCountries +import io.velocitycareerlabs.api.entities.error.VCLErrorCode import io.velocitycareerlabs.api.entities.handleResult import io.velocitycareerlabs.impl.data.infrastructure.executors.ExecutorImpl import io.velocitycareerlabs.impl.data.repositories.CountriesRepositoryImpl @@ -15,13 +17,12 @@ import io.velocitycareerlabs.impl.data.usecases.CountriesUseCaseImpl import io.velocitycareerlabs.impl.domain.usecases.CountriesUseCase import io.velocitycareerlabs.infrastructure.resources.EmptyCacheService import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.CountriesMocks import org.junit.After import org.junit.Before import org.junit.Test -//@RunWith(RobolectricTestRunner::class) -//@Config(sdk = [Build.VERSION_CODES.O_MR1]) class CountriesUseCaseTest { internal lateinit var subject: CountriesUseCase @@ -59,7 +60,31 @@ class CountriesUseCaseTest { assert(afghanistanRegions.all[2].code == CountriesMocks.AfghanistanRegion3Code) }, errorHandler = { - assert(false) { "$it" } + assert(false) { "${it.toJsonObject()}" } + } + ) + } + } + + @Test + fun testGetCountriesFailure() { + subject = CountriesUseCaseImpl( + CountriesRepositoryImpl( + NetworkServiceSuccess( + "wrong payload" + ), + EmptyCacheService() + ), + EmptyExecutor() + ) + + subject.getCountries(0) { + it.handleResult( + successHandler = { + assert(false) { "${VCLErrorCode.SdkError.value} error code is expected" } + }, + errorHandler = { error -> + assert(error.errorCode == VCLErrorCode.SdkError.value) } ) } diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialManifestUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialManifestUseCaseTest.kt index 61b4c264..453daefd 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialManifestUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialManifestUseCaseTest.kt @@ -9,18 +9,21 @@ package io.velocitycareerlabs.usecases import android.os.Build import io.velocitycareerlabs.api.entities.* +import io.velocitycareerlabs.api.entities.error.VCLErrorCode import io.velocitycareerlabs.impl.data.infrastructure.executors.ExecutorImpl import io.velocitycareerlabs.impl.keys.VCLKeyServiceLocalImpl import io.velocitycareerlabs.impl.data.repositories.CredentialManifestRepositoryImpl import io.velocitycareerlabs.impl.data.repositories.JwtServiceRepositoryImpl import io.velocitycareerlabs.impl.data.repositories.ResolveKidRepositoryImpl import io.velocitycareerlabs.impl.data.usecases.CredentialManifestUseCaseImpl +import io.velocitycareerlabs.impl.data.verifiers.CredentialManifestByDeepLinkVerifierImpl import io.velocitycareerlabs.impl.domain.usecases.CredentialManifestUseCase import io.velocitycareerlabs.impl.extensions.toJsonObject import io.velocitycareerlabs.impl.jwt.local.VCLJwtSignServiceLocalImpl import io.velocitycareerlabs.impl.jwt.local.VCLJwtVerifyServiceLocalImpl import io.velocitycareerlabs.infrastructure.db.SecretStoreServiceMock import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.CredentialManifestMocks import io.velocitycareerlabs.infrastructure.resources.valid.DeepLinkMocks import io.velocitycareerlabs.infrastructure.resources.valid.VerifiedProfileMocks @@ -44,7 +47,7 @@ internal class CredentialManifestUseCaseTest { } @Test - fun testGetCredentialManifest() { + fun testGetCredentialManifestSuccess() { // Arrange subject = CredentialManifestUseCaseImpl( CredentialManifestRepositoryImpl( @@ -57,7 +60,8 @@ internal class CredentialManifestUseCaseTest { VCLJwtSignServiceLocalImpl(VCLKeyServiceLocalImpl(SecretStoreServiceMock.Instance)), VCLJwtVerifyServiceLocalImpl() ), - ExecutorImpl() + CredentialManifestByDeepLinkVerifierImpl(), + EmptyExecutor() ) subject.getCredentialManifest( @@ -89,7 +93,44 @@ internal class CredentialManifestUseCaseTest { assert(credentialManifest.jwt.signature.toString() == CredentialManifestMocks.Signature) }, { - assert(false) { "$it" } + assert(false) { "${it.toJsonObject()}" } + } + ) + } + } + + @Test + fun testGetCredentialManifestFailure() { + // Arrange + subject = CredentialManifestUseCaseImpl( + CredentialManifestRepositoryImpl( + NetworkServiceSuccess("wrong payload") + ), + ResolveKidRepositoryImpl( + NetworkServiceSuccess(CredentialManifestMocks.JWK) + ), + JwtServiceRepositoryImpl( + VCLJwtSignServiceLocalImpl(VCLKeyServiceLocalImpl(SecretStoreServiceMock.Instance)), + VCLJwtVerifyServiceLocalImpl() + ), + CredentialManifestByDeepLinkVerifierImpl(), + EmptyExecutor() + ) + + subject.getCredentialManifest( + credentialManifestDescriptor = VCLCredentialManifestDescriptorByDeepLink( + deepLink = DeepLinkMocks.CredentialManifestDeepLinkDevNet, + issuingType = VCLIssuingType.Career + ), + verifiedProfile = VCLVerifiedProfile(VerifiedProfileMocks.VerifiedProfileIssuerJsonStr1.toJsonObject()!!), + remoteCryptoServicesToken = null + ) { + it.handleResult( + successHandler = { + assert(false) { "${VCLErrorCode.SdkError.value} error code is expected" } + }, + errorHandler = { error -> + assert(error.errorCode == VCLErrorCode.SdkError.value) } ) } diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypeSchemaUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypeSchemaUseCaseTest.kt index a47359d1..2f974490 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypeSchemaUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypeSchemaUseCaseTest.kt @@ -14,6 +14,7 @@ import io.velocitycareerlabs.impl.data.usecases.CredentialTypeSchemasUseCaseImpl import io.velocitycareerlabs.impl.domain.usecases.CredentialTypeSchemasUseCase import io.velocitycareerlabs.infrastructure.resources.EmptyCacheService import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.CredentialTypeSchemaMocks import org.json.JSONObject import org.junit.After @@ -36,7 +37,7 @@ internal class CredentialTypeSchemaUseCaseTest { EmptyCacheService() ), CredentialTypeSchemaMocks.CredentialTypes, - ExecutorImpl() + EmptyExecutor() ) subject.getCredentialTypeSchemas(0) { diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypesUIFormSchemaUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypesUIFormSchemaUseCaseTest.kt index 58ec0409..616a0e31 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypesUIFormSchemaUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypesUIFormSchemaUseCaseTest.kt @@ -8,11 +8,13 @@ package io.velocitycareerlabs.usecases import io.velocitycareerlabs.api.entities.* +import io.velocitycareerlabs.api.entities.error.VCLErrorCode import io.velocitycareerlabs.impl.data.infrastructure.executors.ExecutorImpl import io.velocitycareerlabs.impl.data.repositories.CredentialTypesUIFormSchemaRepositoryImpl import io.velocitycareerlabs.impl.data.usecases.CredentialTypesUIFormSchemaUseCaseImpl import io.velocitycareerlabs.impl.domain.usecases.CredentialTypesUIFormSchemaUseCase import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.CredentialTypesUIFormSchemaMocks import org.json.JSONArray import org.json.JSONObject @@ -36,7 +38,7 @@ internal class CredentialTypesUIFormSchemaUseCaseTest { CredentialTypesUIFormSchemaRepositoryImpl( NetworkServiceSuccess(CredentialTypesUIFormSchemaMocks.UISchemaFormJsonFull) ), - ExecutorImpl() + EmptyExecutor() ) subject.getCredentialTypesUIFormSchema( @@ -79,7 +81,7 @@ internal class CredentialTypesUIFormSchemaUseCaseTest { CredentialTypesUIFormSchemaRepositoryImpl( NetworkServiceSuccess(CredentialTypesUIFormSchemaMocks.UISchemaFormJsonOnlyCountries) ), - ExecutorImpl() + EmptyExecutor() ) subject.getCredentialTypesUIFormSchema( @@ -122,7 +124,7 @@ internal class CredentialTypesUIFormSchemaUseCaseTest { CredentialTypesUIFormSchemaRepositoryImpl( NetworkServiceSuccess(CredentialTypesUIFormSchemaMocks.UISchemaFormJsonOnlyRegions) ), - ExecutorImpl() + EmptyExecutor() ) subject.getCredentialTypesUIFormSchema( @@ -165,7 +167,7 @@ internal class CredentialTypesUIFormSchemaUseCaseTest { CredentialTypesUIFormSchemaRepositoryImpl( NetworkServiceSuccess(CredentialTypesUIFormSchemaMocks.UISchemaFormJsonOnlyEnums) ), - ExecutorImpl() + EmptyExecutor() ) subject.getCredentialTypesUIFormSchema( @@ -203,6 +205,30 @@ internal class CredentialTypesUIFormSchemaUseCaseTest { } } + @Test + fun testCredentialTypesFormSchemaFailure() { + subject = CredentialTypesUIFormSchemaUseCaseImpl( + CredentialTypesUIFormSchemaRepositoryImpl( + NetworkServiceSuccess("wrong payload") + ), + EmptyExecutor() + ) + + subject.getCredentialTypesUIFormSchema( + VCLCredentialTypesUIFormSchemaDescriptor("some type", VCLCountries.CA), + mockedCountries + ) { + it.handleResult( + successHandler = { + assert(false) { "${VCLErrorCode.SdkError.value} error code is expected" } + }, + errorHandler = { error -> + assert(error.errorCode == VCLErrorCode.SdkError.value) + } + ) + } + } + private fun jsonArrToCountries(countriesJsonArr: JSONArray): VCLCountries { val countries = mutableListOf() for (i in 0 until countriesJsonArr.length()) { diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypesUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypesUseCaseTest.kt index 95423b1f..a62407a5 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypesUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/CredentialTypesUseCaseTest.kt @@ -8,12 +8,14 @@ package io.velocitycareerlabs.usecases import io.velocitycareerlabs.api.entities.* +import io.velocitycareerlabs.api.entities.error.VCLErrorCode import io.velocitycareerlabs.impl.data.infrastructure.executors.ExecutorImpl import io.velocitycareerlabs.impl.data.repositories.CredentialTypesRepositoryImpl import io.velocitycareerlabs.impl.data.usecases.CredentialTypesUseCaseImpl import io.velocitycareerlabs.impl.domain.usecases.CredentialTypesUseCase import io.velocitycareerlabs.infrastructure.resources.EmptyCacheService import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.CredentialTypesMocks import org.json.JSONObject import org.junit.After @@ -29,13 +31,13 @@ internal class CredentialTypesUseCaseTest { } @Test - fun testGetCredentialTypes() { + fun testGetCredentialTypesSuccess() { subject = CredentialTypesUseCaseImpl( CredentialTypesRepositoryImpl( NetworkServiceSuccess(CredentialTypesMocks.CredentialTypesJson), EmptyCacheService() ), - ExecutorImpl() + EmptyExecutor() ) subject.getCredentialTypes(0) { @@ -51,6 +53,28 @@ internal class CredentialTypesUseCaseTest { } } + @Test + fun testGetCredentialTypesFailure() { + subject = CredentialTypesUseCaseImpl( + CredentialTypesRepositoryImpl( + NetworkServiceSuccess("wrong payload"), + EmptyCacheService() + ), + EmptyExecutor() + ) + + subject.getCredentialTypes(0) { + it.handleResult( + successHandler = { + assert(false) { "${VCLErrorCode.SdkError.value} error code is expected" } + }, + errorHandler = { error -> + assert(error.errorCode == VCLErrorCode.SdkError.value) + } + ) + } + } + private fun compareCredentialTypes( credentialTypesArr1: List, credentialTypesArr2: List diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/ExchangeProgressUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/ExchangeProgressUseCaseTest.kt index f1dd45ad..ce47fe8e 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/ExchangeProgressUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/ExchangeProgressUseCaseTest.kt @@ -9,11 +9,13 @@ package io.velocitycareerlabs.usecases import io.velocitycareerlabs.api.entities.* import io.velocitycareerlabs.api.entities.VCLExchangeDescriptor +import io.velocitycareerlabs.api.entities.error.VCLErrorCode import io.velocitycareerlabs.impl.data.infrastructure.executors.ExecutorImpl import io.velocitycareerlabs.impl.data.repositories.ExchangeProgressRepositoryImpl import io.velocitycareerlabs.impl.data.usecases.ExchangeProgressUseCaseImpl import io.velocitycareerlabs.impl.domain.usecases.ExchangeProgressUseCase import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.ExchangeProgressMocks import org.json.JSONObject import org.junit.After @@ -38,12 +40,12 @@ internal class ExchangeProgressUseCaseTest { } @Test - fun testGetExchangeProgress() { + fun testGetExchangeProgressSuccess() { subject = ExchangeProgressUseCaseImpl( ExchangeProgressRepositoryImpl( NetworkServiceSuccess(ExchangeProgressMocks.ExchangeProgressJson) ), - ExecutorImpl() + EmptyExecutor() ) subject.getExchangeProgress(exchangeDescriptor) { @@ -58,6 +60,27 @@ internal class ExchangeProgressUseCaseTest { } } + @Test + fun testGetExchangeProgressFailure() { + subject = ExchangeProgressUseCaseImpl( + ExchangeProgressRepositoryImpl( + NetworkServiceSuccess("wrong payload") + ), + EmptyExecutor() + ) + + subject.getExchangeProgress(exchangeDescriptor) { + it.handleResult( + successHandler = { + assert(false) { "${VCLErrorCode.SdkError.value} error code is expected" } + }, + errorHandler = { error -> + assert(error.errorCode == VCLErrorCode.SdkError.value) + } + ) + } + } + private fun expectedExchange(exchangeJsonObj: JSONObject) = VCLExchange( id = exchangeJsonObj.getString(VCLExchange.KeyId), diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/FinalizeOffersUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/FinalizeOffersUseCaseTest.kt index 379ca0e5..ad99ff0b 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/FinalizeOffersUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/FinalizeOffersUseCaseTest.kt @@ -9,14 +9,17 @@ package io.velocitycareerlabs.usecases import android.os.Build import io.velocitycareerlabs.api.entities.* +import io.velocitycareerlabs.api.entities.error.VCLErrorCode import io.velocitycareerlabs.impl.keys.VCLKeyServiceLocalImpl import io.velocitycareerlabs.impl.data.repositories.FinalizeOffersRepositoryImpl import io.velocitycareerlabs.impl.data.repositories.GenerateOffersRepositoryImpl import io.velocitycareerlabs.impl.data.repositories.JwtServiceRepositoryImpl import io.velocitycareerlabs.impl.data.usecases.FinalizeOffersUseCaseImpl import io.velocitycareerlabs.impl.data.usecases.GenerateOffersUseCaseImpl -import io.velocitycareerlabs.impl.data.utils.CredentialDidVerifierImpl -import io.velocitycareerlabs.impl.data.utils.CredentialIssuerVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.CredentialDidVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.CredentialIssuerVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.CredentialsByDeepLinkVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.OffersByDeepLinkVerifierImpl import io.velocitycareerlabs.impl.domain.usecases.FinalizeOffersUseCase import io.velocitycareerlabs.impl.extensions.toJsonArray import io.velocitycareerlabs.impl.extensions.toJsonObject @@ -76,6 +79,7 @@ internal class FinalizeOffersUseCaseTest { GenerateOffersRepositoryImpl( NetworkServiceSuccess(validResponse = GenerateOffersMocks.GeneratedOffers) ), + OffersByDeepLinkVerifierImpl(), EmptyExecutor() ).generateOffers( sessionToken = VCLToken(value = ""), @@ -134,6 +138,7 @@ internal class FinalizeOffersUseCaseTest { NetworkServiceSuccess(validResponse = JsonLdMocks.Layer1v10Jsonld), ), CredentialDidVerifierImpl(), + CredentialsByDeepLinkVerifierImpl(), EmptyExecutor() ) @@ -182,6 +187,7 @@ internal class FinalizeOffersUseCaseTest { NetworkServiceSuccess(validResponse = JsonLdMocks.Layer1v10Jsonld), ), CredentialDidVerifierImpl(), + CredentialsByDeepLinkVerifierImpl(), EmptyExecutor() ) @@ -231,6 +237,7 @@ internal class FinalizeOffersUseCaseTest { NetworkServiceSuccess(validResponse = JsonLdMocks.Layer1v10Jsonld), ), CredentialDidVerifierImpl(), + CredentialsByDeepLinkVerifierImpl(), EmptyExecutor() ) @@ -251,4 +258,42 @@ internal class FinalizeOffersUseCaseTest { ) } } + + @Test + fun testFailure() { + subject = FinalizeOffersUseCaseImpl( + FinalizeOffersRepositoryImpl( + NetworkServiceSuccess("wrong payload") + ), + JwtServiceRepositoryImpl( + VCLJwtSignServiceLocalImpl(keyService), + VCLJwtVerifyServiceLocalImpl() + ), + CredentialIssuerVerifierImpl( + CredentialTypesModelMock( + issuerCategory = CredentialTypesModelMock.issuerCategoryRegularIssuer + ), + NetworkServiceSuccess(validResponse = JsonLdMocks.Layer1v10Jsonld), + ), + CredentialDidVerifierImpl(), + CredentialsByDeepLinkVerifierImpl(), + EmptyExecutor() + ) + + subject.finalizeOffers( + finalizeOffersDescriptor = finalizeOffersDescriptorPassed, + didJwk = didJwk, + sessionToken = VCLToken(value = ""), + remoteCryptoServicesToken = null + ) { + it.handleResult( + successHandler = { + assert(false) { "${VCLErrorCode.SdkError.value} error code is expected" } + }, + errorHandler = { error -> + assert(error.errorCode == VCLErrorCode.SdkError.value) + } + ) + } + } } \ No newline at end of file diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/GenerateOffersUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/GenerateOffersUseCaseTest.kt index 6270978e..60e36bbc 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/GenerateOffersUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/GenerateOffersUseCaseTest.kt @@ -9,14 +9,17 @@ package io.velocitycareerlabs.usecases import android.os.Build import io.velocitycareerlabs.api.entities.* +import io.velocitycareerlabs.api.entities.error.VCLErrorCode import io.velocitycareerlabs.impl.data.infrastructure.executors.ExecutorImpl import io.velocitycareerlabs.impl.data.repositories.GenerateOffersRepositoryImpl import io.velocitycareerlabs.impl.data.usecases.GenerateOffersUseCaseImpl +import io.velocitycareerlabs.impl.data.verifiers.OffersByDeepLinkVerifierImpl import io.velocitycareerlabs.impl.domain.usecases.GenerateOffersUseCase import io.velocitycareerlabs.impl.extensions.toJsonArray import io.velocitycareerlabs.impl.extensions.toJsonObject import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess import io.velocitycareerlabs.infrastructure.resources.CommonMocks +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.GenerateOffersMocks import io.velocitycareerlabs.infrastructure.resources.valid.VerifiedProfileMocks import org.junit.After @@ -37,7 +40,8 @@ internal class GenerateOffersUseCaseTest { GenerateOffersRepositoryImpl( NetworkServiceSuccess(validResponse = GenerateOffersMocks.GeneratedOffers) ), - ExecutorImpl() + OffersByDeepLinkVerifierImpl(), + EmptyExecutor() ) val generateOffersDescriptor = VCLGenerateOffersDescriptor( @@ -71,7 +75,8 @@ internal class GenerateOffersUseCaseTest { GenerateOffersRepositoryImpl( NetworkServiceSuccess(validResponse = GenerateOffersMocks.GeneratedOffersEmptyJsonObj) ), - ExecutorImpl() + OffersByDeepLinkVerifierImpl(), + EmptyExecutor() ) val generateOffersDescriptor = VCLGenerateOffersDescriptor( @@ -88,7 +93,7 @@ internal class GenerateOffersUseCaseTest { ) { it.handleResult( {offers -> - assert(offers.all == "[]".toJsonArray()) + assert(offers.all == listOf()) }, { assert(false) { "${it.toJsonObject()}" } @@ -103,7 +108,8 @@ internal class GenerateOffersUseCaseTest { GenerateOffersRepositoryImpl( NetworkServiceSuccess(validResponse = GenerateOffersMocks.GeneratedOffersEmptyJsonArr) ), - ExecutorImpl() + OffersByDeepLinkVerifierImpl(), + EmptyExecutor() ) val generateOffersDescriptor = VCLGenerateOffersDescriptor( @@ -120,7 +126,7 @@ internal class GenerateOffersUseCaseTest { ) { it.handleResult( { offers -> - assert(offers.all == GenerateOffersMocks.GeneratedOffersEmptyJsonArr.toJsonArray()) + assert(offers.all == listOf()) }, { assert(false) { "${it.toJsonObject()}" } diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/JwtServiceUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/JwtServiceUseCaseTest.kt index 4f8835cf..f483bb7c 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/JwtServiceUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/JwtServiceUseCaseTest.kt @@ -20,6 +20,7 @@ import io.velocitycareerlabs.impl.extensions.toPublicJwk import io.velocitycareerlabs.impl.jwt.local.VCLJwtSignServiceLocalImpl import io.velocitycareerlabs.impl.jwt.local.VCLJwtVerifyServiceLocalImpl import io.velocitycareerlabs.infrastructure.db.SecretStoreServiceMock +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.JwtServiceMocks import org.junit.After import org.junit.Before @@ -43,7 +44,7 @@ internal class JwtServiceUseCaseTest { VCLJwtSignServiceLocalImpl(keyService), VCLJwtVerifyServiceLocalImpl() ), - ExecutorImpl() + EmptyExecutor() ) } diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/KeyServiceUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/KeyServiceUseCaseTest.kt index 217157c4..e937892c 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/KeyServiceUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/KeyServiceUseCaseTest.kt @@ -18,6 +18,7 @@ import io.velocitycareerlabs.impl.data.repositories.KeyServiceRepositoryImpl import io.velocitycareerlabs.impl.data.usecases.KeyServiceUseCaseImpl import io.velocitycareerlabs.impl.domain.usecases.KeyServiceUseCase import io.velocitycareerlabs.infrastructure.db.SecretStoreServiceMock +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -37,7 +38,7 @@ class KeyServiceUseCaseTest { KeyServiceRepositoryImpl( VCLKeyServiceLocalImpl(SecretStoreServiceMock.Instance) ), - ExecutorImpl() + EmptyExecutor() ) } @@ -58,7 +59,7 @@ class KeyServiceUseCaseTest { assert(jwkJsonObj.optString("y") != null) }, errorHandler = { - assert(false) { "$it" } + assert(false) { "${it.toJsonObject()}" } } ) } diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/OrganizationsUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/OrganizationsUseCaseTest.kt index 12c59e80..5314a883 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/OrganizationsUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/OrganizationsUseCaseTest.kt @@ -7,18 +7,15 @@ package io.velocitycareerlabs.usecases -import io.velocitycareerlabs.api.entities.VCLOrganizations -import io.velocitycareerlabs.api.entities.VCLResult import io.velocitycareerlabs.api.entities.VCLService -import io.velocitycareerlabs.api.entities.data import io.velocitycareerlabs.api.entities.VCLOrganizationsSearchDescriptor import io.velocitycareerlabs.api.entities.handleResult -import io.velocitycareerlabs.impl.data.infrastructure.executors.ExecutorImpl import io.velocitycareerlabs.impl.data.repositories.OrganizationsRepositoryImpl import io.velocitycareerlabs.impl.data.usecases.OrganizationsUseCaseImpl import io.velocitycareerlabs.impl.domain.usecases.OrganizationsUseCase import io.velocitycareerlabs.impl.extensions.toList import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.OrganizationsMocks import org.json.JSONObject import org.junit.After @@ -41,7 +38,7 @@ internal class OrganizationsUseCaseTest { OrganizationsMocks.OrganizationJsonResult ), ), - ExecutorImpl() + EmptyExecutor() ) val serviceJsonMock = JSONObject(OrganizationsMocks.ServiceJsonStr) @@ -60,7 +57,7 @@ internal class OrganizationsUseCaseTest { assert(serviceCredentialAgentIssuer.serviceEndpoint == OrganizationsMocks.ServiceEndpoint) }, { - assert(false) { "$it" } + assert(false) { "${it.toJsonObject()}" } } ) } diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/PresentationRequestUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/PresentationRequestUseCaseTest.kt index 1f20aee9..d51f3e06 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/PresentationRequestUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/PresentationRequestUseCaseTest.kt @@ -9,18 +9,21 @@ package io.velocitycareerlabs.usecases import android.os.Build import io.velocitycareerlabs.api.entities.* +import io.velocitycareerlabs.api.entities.error.VCLErrorCode import io.velocitycareerlabs.impl.data.infrastructure.executors.ExecutorImpl import io.velocitycareerlabs.impl.keys.VCLKeyServiceLocalImpl import io.velocitycareerlabs.impl.data.repositories.JwtServiceRepositoryImpl import io.velocitycareerlabs.impl.data.repositories.PresentationRequestRepositoryImpl import io.velocitycareerlabs.impl.data.repositories.ResolveKidRepositoryImpl import io.velocitycareerlabs.impl.data.usecases.PresentationRequestUseCaseImpl +import io.velocitycareerlabs.impl.data.verifiers.PresentationRequestByDeepLinkVerifierImpl import io.velocitycareerlabs.impl.domain.usecases.PresentationRequestUseCase import io.velocitycareerlabs.impl.extensions.toJsonObject import io.velocitycareerlabs.impl.jwt.local.VCLJwtSignServiceLocalImpl import io.velocitycareerlabs.impl.jwt.local.VCLJwtVerifyServiceLocalImpl import io.velocitycareerlabs.infrastructure.db.SecretStoreServiceMock import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.DeepLinkMocks import io.velocitycareerlabs.infrastructure.resources.valid.PresentationRequestMocks import org.junit.Test @@ -35,8 +38,7 @@ internal class PresentationRequestUseCaseTest { lateinit var subject: PresentationRequestUseCase @Test - fun testCountryCodesSuccess() { - // Arrange + fun testGetPresentationRequestSuccess() { val pushUrl = "push_url" val pushToken = "push_token" subject = PresentationRequestUseCaseImpl( @@ -50,11 +52,10 @@ internal class PresentationRequestUseCaseTest { VCLJwtSignServiceLocalImpl(VCLKeyServiceLocalImpl(SecretStoreServiceMock.Instance)), VCLJwtVerifyServiceLocalImpl() ), - ExecutorImpl() + PresentationRequestByDeepLinkVerifierImpl(), + EmptyExecutor() ) - var result: VCLResult? = null - // Action subject.getPresentationRequest( presentationRequestDescriptor = VCLPresentationRequestDescriptor( deepLink = DeepLinkMocks.PresentationRequestDeepLinkDevNet, @@ -88,7 +89,41 @@ internal class PresentationRequestUseCaseTest { assert(presentationRequest.pushDelegate!!.pushToken == pushToken) }, errorHandler = { - assert(false) { "$it" } + assert(false) { "${it.toJsonObject()}" } + } + ) + } + } + + @Test + fun testGetPresentationRequestFailure() { + subject = PresentationRequestUseCaseImpl( + PresentationRequestRepositoryImpl( + NetworkServiceSuccess(validResponse = "wrong payload") + ), + ResolveKidRepositoryImpl( + NetworkServiceSuccess(validResponse = PresentationRequestMocks.JWK) + ), + JwtServiceRepositoryImpl( + VCLJwtSignServiceLocalImpl(VCLKeyServiceLocalImpl(SecretStoreServiceMock.Instance)), + VCLJwtVerifyServiceLocalImpl() + ), + PresentationRequestByDeepLinkVerifierImpl(), + EmptyExecutor() + ) + + subject.getPresentationRequest( + presentationRequestDescriptor = VCLPresentationRequestDescriptor( + deepLink = DeepLinkMocks.PresentationRequestDeepLinkDevNet + ), + null + ) { + it.handleResult( + successHandler = { + assert(false) { "${VCLErrorCode.SdkError.value} error code is expected" } + }, + errorHandler = { error -> + assert(error.errorCode == VCLErrorCode.SdkError.value) } ) } diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/PresentationSubmissionUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/PresentationSubmissionUseCaseTest.kt index dbd555f2..f6314c2d 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/PresentationSubmissionUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/PresentationSubmissionUseCaseTest.kt @@ -9,7 +9,6 @@ package io.velocitycareerlabs.usecases import android.os.Build import io.velocitycareerlabs.api.entities.* -import io.velocitycareerlabs.impl.data.infrastructure.executors.ExecutorImpl import io.velocitycareerlabs.impl.keys.VCLKeyServiceLocalImpl import io.velocitycareerlabs.impl.data.repositories.JwtServiceRepositoryImpl import io.velocitycareerlabs.impl.data.repositories.PresentationSubmissionRepositoryImpl @@ -21,6 +20,7 @@ import io.velocitycareerlabs.impl.jwt.local.VCLJwtVerifyServiceLocalImpl import io.velocitycareerlabs.infrastructure.db.SecretStoreServiceMock import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess import io.velocitycareerlabs.infrastructure.resources.CommonMocks +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.PresentationSubmissionMocks import org.json.JSONObject import org.junit.After @@ -60,7 +60,7 @@ internal class PresentationSubmissionUseCaseTest { VCLJwtSignServiceLocalImpl(keyService), VCLJwtVerifyServiceLocalImpl() ), - ExecutorImpl() + EmptyExecutor() ) val presentationSubmission = VCLPresentationSubmission( presentationRequest = VCLPresentationRequest( diff --git a/VCL/src/test/java/io/velocitycareerlabs/usecases/VerifiedProfileUseCaseTest.kt b/VCL/src/test/java/io/velocitycareerlabs/usecases/VerifiedProfileUseCaseTest.kt index 75616d7d..fc9faf44 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/usecases/VerifiedProfileUseCaseTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/usecases/VerifiedProfileUseCaseTest.kt @@ -8,11 +8,11 @@ package io.velocitycareerlabs.usecases import io.velocitycareerlabs.api.entities.* -import io.velocitycareerlabs.impl.data.infrastructure.executors.ExecutorImpl import io.velocitycareerlabs.impl.data.repositories.VerifiedProfileRepositoryImpl import io.velocitycareerlabs.impl.data.usecases.VerifiedProfileUseCaseImpl import io.velocitycareerlabs.impl.domain.usecases.VerifiedProfileUseCase import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess +import io.velocitycareerlabs.infrastructure.resources.EmptyExecutor import io.velocitycareerlabs.infrastructure.resources.valid.VerifiedProfileMocks import org.junit.Test @@ -26,7 +26,7 @@ internal class VerifiedProfileUseCaseTest { VerifiedProfileRepositoryImpl( NetworkServiceSuccess(VerifiedProfileMocks.VerifiedProfileIssuerJsonStr1) ), - ExecutorImpl() + EmptyExecutor() ) subject.getVerifiedProfile( @@ -53,7 +53,7 @@ internal class VerifiedProfileUseCaseTest { VerifiedProfileRepositoryImpl( NetworkServiceSuccess(VerifiedProfileMocks.VerifiedProfileIssuerInspectorJsonStr) ), - ExecutorImpl() + EmptyExecutor() ) subject.getVerifiedProfile( @@ -80,7 +80,7 @@ internal class VerifiedProfileUseCaseTest { VerifiedProfileRepositoryImpl( NetworkServiceSuccess(VerifiedProfileMocks.VerifiedProfileIssuerInspectorJsonStr) ), - ExecutorImpl() + EmptyExecutor() ) subject.getVerifiedProfile( @@ -107,7 +107,7 @@ internal class VerifiedProfileUseCaseTest { VerifiedProfileRepositoryImpl( NetworkServiceSuccess(VerifiedProfileMocks.VerifiedProfileNotaryIssuerJsonStr) ), - ExecutorImpl() + EmptyExecutor() ) subject.getVerifiedProfile( @@ -134,7 +134,7 @@ internal class VerifiedProfileUseCaseTest { VerifiedProfileRepositoryImpl( NetworkServiceSuccess(VerifiedProfileMocks.VerifiedProfileNotaryIssuerJsonStr) ), - ExecutorImpl() + EmptyExecutor() ) subject.getVerifiedProfile( diff --git a/VCL/src/test/java/io/velocitycareerlabs/utils/CredentialDidVerifierTest.kt b/VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialDidVerifierTest.kt similarity index 89% rename from VCL/src/test/java/io/velocitycareerlabs/utils/CredentialDidVerifierTest.kt rename to VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialDidVerifierTest.kt index 0af852db..5e50bf7a 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/utils/CredentialDidVerifierTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialDidVerifierTest.kt @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.velocitycareerlabs.utils +package io.velocitycareerlabs.verifiers import io.velocitycareerlabs.api.entities.VCLCredentialManifest import io.velocitycareerlabs.api.entities.VCLFinalizeOffersDescriptor @@ -14,9 +14,10 @@ import io.velocitycareerlabs.api.entities.VCLOffers import io.velocitycareerlabs.api.entities.VCLToken import io.velocitycareerlabs.api.entities.VCLVerifiedProfile import io.velocitycareerlabs.api.entities.handleResult -import io.velocitycareerlabs.impl.data.utils.CredentialDidVerifierImpl +import io.velocitycareerlabs.impl.data.verifiers.CredentialDidVerifierImpl import io.velocitycareerlabs.impl.extensions.toJsonArray import io.velocitycareerlabs.impl.extensions.toJsonObject +import io.velocitycareerlabs.impl.extensions.toJwtList import io.velocitycareerlabs.impl.extensions.toList import io.velocitycareerlabs.infrastructure.resources.valid.CredentialManifestMocks import io.velocitycareerlabs.infrastructure.resources.valid.CredentialMocks @@ -33,7 +34,7 @@ internal class CredentialDidVerifierTest { CredentialMocks.JwtCredentialsFromNotaryIssuer.toJsonArray()?.length() private val credentialsFromRegularIssuerAmount = CredentialMocks.JwtCredentialsFromRegularIssuer.toJsonArray()?.length() - private val OffersMock = VCLOffers(JSONObject(), JSONArray(), 1, VCLToken(".."), "") + private val OffersMock = VCLOffers(JSONObject(), listOf(), 1, VCLToken(".."), "") lateinit var finalizeOffersDescriptorOfNotaryIssuer: VCLFinalizeOffersDescriptor lateinit var credentialManifestFromNotaryIssuer: VCLCredentialManifest @@ -69,8 +70,7 @@ internal class CredentialDidVerifierTest { @Test fun testVerifyCredentialsSuccess() { subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsFromNotaryIssuer.toJsonArray()!! - .toList() as List, + jwtCredentials = CredentialMocks.JwtCredentialsFromNotaryIssuer.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfNotaryIssuer ) { verifiableCredentialsResult -> verifiableCredentialsResult.handleResult( @@ -98,8 +98,7 @@ internal class CredentialDidVerifierTest { @Test fun testVerifyCredentialsFailed() { subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsFromRegularIssuer.toJsonArray()!! - .toList() as List, + jwtCredentials = CredentialMocks.JwtCredentialsFromRegularIssuer.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfNotaryIssuer ) { verifiableCredentialsResult -> verifiableCredentialsResult.handleResult( @@ -127,8 +126,8 @@ internal class CredentialDidVerifierTest { @Test fun testVerifyCredentials1Passed1Failed() { subject.verifyCredentials( - jwtEncodedCredentials = "[\"${CredentialMocks.JwtCredentialEmploymentPastFromNotaryIssuer}\", \"${CredentialMocks.JwtCredentialEmailFromIdentityIssuer}\"]" - .toJsonArray()!!.toList() as List, + jwtCredentials = "[\"${CredentialMocks.JwtCredentialEmploymentPastFromNotaryIssuer}\", \"${CredentialMocks.JwtCredentialEmailFromIdentityIssuer}\"]" + .toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfNotaryIssuer ) { verifiableCredentialsResult -> verifiableCredentialsResult.handleResult( @@ -157,8 +156,7 @@ internal class CredentialDidVerifierTest { @Test fun testVerifyCredentialsEmpty() { subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtEmptyCredentials.toJsonArray()!! - .toList() as List, + jwtCredentials = CredentialMocks.JwtEmptyCredentials.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfNotaryIssuer ) { verifiableCredentialsResult -> verifiableCredentialsResult.handleResult( diff --git a/VCL/src/test/java/io/velocitycareerlabs/utils/CredentialIssuerVerifierTest.kt b/VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialIssuerVerifierTest.kt similarity index 88% rename from VCL/src/test/java/io/velocitycareerlabs/utils/CredentialIssuerVerifierTest.kt rename to VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialIssuerVerifierTest.kt index d4586149..c8251677 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/utils/CredentialIssuerVerifierTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialIssuerVerifierTest.kt @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.velocitycareerlabs.utils +package io.velocitycareerlabs.verifiers import io.velocitycareerlabs.api.entities.VCLCredentialManifest import io.velocitycareerlabs.api.entities.error.VCLErrorCode @@ -15,10 +15,11 @@ import io.velocitycareerlabs.api.entities.VCLOffers import io.velocitycareerlabs.api.entities.VCLToken import io.velocitycareerlabs.api.entities.VCLVerifiedProfile import io.velocitycareerlabs.api.entities.handleResult -import io.velocitycareerlabs.impl.data.utils.CredentialIssuerVerifierImpl -import io.velocitycareerlabs.impl.domain.utils.CredentialIssuerVerifier +import io.velocitycareerlabs.impl.data.verifiers.CredentialIssuerVerifierImpl +import io.velocitycareerlabs.impl.domain.verifiers.CredentialIssuerVerifier import io.velocitycareerlabs.impl.extensions.toJsonArray import io.velocitycareerlabs.impl.extensions.toJsonObject +import io.velocitycareerlabs.impl.extensions.toJwtList import io.velocitycareerlabs.impl.extensions.toListOfStrings import io.velocitycareerlabs.infrastructure.network.NetworkServiceSuccess import io.velocitycareerlabs.infrastructure.resources.CredentialTypesModelMock @@ -35,7 +36,7 @@ internal class CredentialIssuerVerifierTest { lateinit var subject: CredentialIssuerVerifier - private val OffersMock = VCLOffers(JSONObject(), JSONArray(), 1, VCLToken(""), "") + private val OffersMock = VCLOffers(JSONObject(), listOf(), 1, VCLToken(""), "") lateinit var finalizeOffersDescriptorWithoutPermittedServices: VCLFinalizeOffersDescriptor lateinit var credentialManifestWithoutPermittedServices: VCLCredentialManifest @@ -106,8 +107,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsFromNotaryIssuer.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtCredentialsFromNotaryIssuer.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfNotaryIssuer, ) { verificationResult -> verificationResult.handleResult( @@ -131,8 +131,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsWithoutSubject.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtCredentialsWithoutSubject.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfNotaryIssuer, ) { verificationResult -> verificationResult.handleResult( @@ -156,8 +155,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsFromRegularIssuer.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtCredentialsFromRegularIssuer.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfRegularIssuer, ) { verificationResult -> verificationResult.handleResult( @@ -181,8 +179,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsFromNotaryIssuer.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtCredentialsFromNotaryIssuer.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfRegularIssuer, ) { verificationResult -> verificationResult.handleResult( @@ -206,8 +203,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsFromNotaryIssuer.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtCredentialsFromNotaryIssuer.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorWithoutPermittedServices, ) { verificationResult -> verificationResult.handleResult( @@ -231,8 +227,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsWithoutSubject.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtCredentialsWithoutSubject.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfRegularIssuer, ) { verificationResult -> verificationResult.handleResult( @@ -256,8 +251,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsFromRegularIssuer.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtCredentialsFromRegularIssuer.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfIdentityIssuer, ) { verificationResult -> verificationResult.handleResult( @@ -281,8 +275,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtEmptyCredentials.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtEmptyCredentials.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfIdentityIssuer, ) { verificationResult -> verificationResult.handleResult( @@ -306,8 +299,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsFromIdentityIssuer.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtCredentialsFromIdentityIssuer.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfIdentityIssuer, ) { verificationResult -> verificationResult.handleResult( @@ -331,8 +323,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsFromRegularIssuer.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtCredentialsFromRegularIssuer.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfRegularIssuer, ) { verificationResult -> verificationResult.handleResult( @@ -356,8 +347,7 @@ internal class CredentialIssuerVerifierTest { ) subject.verifyCredentials( - jwtEncodedCredentials = CredentialMocks.JwtCredentialsFromRegularIssuer.toJsonArray() - !!.toListOfStrings(), + jwtCredentials = CredentialMocks.JwtCredentialsFromRegularIssuer.toJwtList()!!, finalizeOffersDescriptor = finalizeOffersDescriptorOfRegularIssuer, ) { verificationResult -> verificationResult.handleResult( diff --git a/VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialManifestByDeepLinkVerifierTest.kt b/VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialManifestByDeepLinkVerifierTest.kt new file mode 100644 index 00000000..956f9525 --- /dev/null +++ b/VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialManifestByDeepLinkVerifierTest.kt @@ -0,0 +1,59 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.verifiers + +import io.velocitycareerlabs.api.entities.VCLCredentialManifest +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLJwt +import io.velocitycareerlabs.api.entities.VCLVerifiedProfile +import io.velocitycareerlabs.api.entities.error.VCLErrorCode +import io.velocitycareerlabs.api.entities.handleResult +import io.velocitycareerlabs.impl.data.verifiers.CredentialManifestByDeepLinkVerifierImpl +import io.velocitycareerlabs.impl.extensions.toJsonObject +import io.velocitycareerlabs.infrastructure.resources.valid.CredentialManifestMocks +import io.velocitycareerlabs.infrastructure.resources.valid.DeepLinkMocks +import io.velocitycareerlabs.infrastructure.resources.valid.VerifiedProfileMocks +import org.junit.Test + +class CredentialManifestByDeepLinkVerifierTest { + private val subject = CredentialManifestByDeepLinkVerifierImpl() + private val credentialManifest = VCLCredentialManifest( + jwt = VCLJwt(CredentialManifestMocks.JwtCredentialManifest1), + verifiedProfile = VCLVerifiedProfile(VerifiedProfileMocks.VerifiedProfileOfRegularIssuer.toJsonObject()!!) + ) + private val correctDeepLink = DeepLinkMocks.CredentialManifestDeepLinkDevNet + private val wrongDeepLink = + VCLDeepLink("velocity-network-devnet://issue?request_uri=https%3A%2F%2Fdevagent.velocitycareerlabs.io%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3AEiBMsw27IKRYIdwUOfDeBd0LnWVeG2fPxxJi9L1fvjM20g%2Fissue%2Fget-credential-manifest%3Fid%3D611b5836e93d08000af6f1bc%26credential_types%3DPastEmploymentPosition%26issuerDid%3Ddid%3Aion%3AEiBMsw27IKRYIdwUOfDeBd0LnWVeG2fPxxJi9L1fvjM20g") + + @Test + fun testVerifyCredentialManifestSuccess() { + subject.verifyCredentialManifest( + credentialManifest, + correctDeepLink + ) { + it.handleResult({ isVerified -> + assert(isVerified) + }, { error -> + assert(false) { "${error.toJsonObject()}" } + }) + } + } + + @Test + fun testVerifyCredentialManifestError() { + subject.verifyCredentialManifest( + credentialManifest, + wrongDeepLink + ) { + it.handleResult({ + assert(false) { "${VCLErrorCode.MismatchedRequestIssuerDid.value} error code is expected" } + }, { error -> + assert(error.errorCode == VCLErrorCode.MismatchedRequestIssuerDid.value) + }) + } + } +} \ No newline at end of file diff --git a/VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialsByDeepLinkVerifierTest.kt b/VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialsByDeepLinkVerifierTest.kt new file mode 100644 index 00000000..4d0f9759 --- /dev/null +++ b/VCL/src/test/java/io/velocitycareerlabs/verifiers/CredentialsByDeepLinkVerifierTest.kt @@ -0,0 +1,58 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.verifiers + +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLJwt +import io.velocitycareerlabs.api.entities.error.VCLErrorCode +import io.velocitycareerlabs.api.entities.handleResult +import io.velocitycareerlabs.impl.data.verifiers.CredentialsByDeepLinkVerifierImpl +import io.velocitycareerlabs.infrastructure.resources.valid.CredentialMocks +import io.velocitycareerlabs.infrastructure.resources.valid.DeepLinkMocks +import org.junit.Test + +class CredentialsByDeepLinkVerifierTest { + private val subject = CredentialsByDeepLinkVerifierImpl() + + private val correctDeepLink = + VCLDeepLink("velocity-network-devnet://issue?request_uri=https%3A%2F%2Fdevagent.velocitycareerlabs.io%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3AEiBMsw27IKRYIdwUOfDeBd0LnWVeG2fPxxJi9L1fvjM20g%2Fissue%2Fget-credential-manifest%3Fid%3D611b5836e93d08000af6f1bc%26credential_types%3DPastEmploymentPosition%26issuerDid%3Ddid%3Aion%3AEiBMsw27IKRYIdwUOfDeBd0LnWVeG2fPxxJi9L1fvjM20g") + private val wrongDeepLink = DeepLinkMocks.CredentialManifestDeepLinkDevNet + + @Test + fun testVerifyCredentialsSuccess() { + subject.verifyCredentials( + listOf( + VCLJwt(CredentialMocks.JwtCredentialEmploymentPastFromRegularIssuer), + VCLJwt(CredentialMocks.JwtCredentialEducationDegreeRegistrationFromRegularIssuer) + ), + correctDeepLink + ) { + it.handleResult({ isVerified -> + assert(isVerified) + }, { error -> + assert(false) { "${error.toJsonObject()}" } + }) + } + } + + @Test + fun testVerifyCredentialsError() { + subject.verifyCredentials( + listOf( + VCLJwt(CredentialMocks.JwtCredentialEmploymentPastFromRegularIssuer), + VCLJwt(CredentialMocks.JwtCredentialEducationDegreeRegistrationFromRegularIssuer) + ), + wrongDeepLink + ) { + it.handleResult({ + assert(false) { "${VCLErrorCode.MismatchedCredentialIssuerDid.value} error code is expected" } + }, { error -> + assert(error.errorCode == VCLErrorCode.MismatchedCredentialIssuerDid.value) + }) + } + } +} \ No newline at end of file diff --git a/VCL/src/test/java/io/velocitycareerlabs/verifiers/OffersByDeepLinkVerifierTest.kt b/VCL/src/test/java/io/velocitycareerlabs/verifiers/OffersByDeepLinkVerifierTest.kt new file mode 100644 index 00000000..2298e7e3 --- /dev/null +++ b/VCL/src/test/java/io/velocitycareerlabs/verifiers/OffersByDeepLinkVerifierTest.kt @@ -0,0 +1,69 @@ +/** + * Created by Michael Avoyan on 10/12/2023. + * + * Copyright 2022 Velocity Career Labs inc. + * SPDX-License-Identifier: Apache-2.0 + */ +package io.velocitycareerlabs.verifiers + +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.VCLOffer +import io.velocitycareerlabs.api.entities.VCLOffers +import io.velocitycareerlabs.api.entities.VCLToken +import io.velocitycareerlabs.api.entities.error.VCLErrorCode +import io.velocitycareerlabs.api.entities.handleResult +import io.velocitycareerlabs.impl.data.utils.Utils +import io.velocitycareerlabs.impl.data.verifiers.OffersByDeepLinkVerifierImpl +import io.velocitycareerlabs.impl.extensions.toJsonObject +import io.velocitycareerlabs.infrastructure.resources.valid.DeepLinkMocks +import io.velocitycareerlabs.infrastructure.resources.valid.GenerateOffersMocks +import org.json.JSONArray +import org.json.JSONObject +import org.junit.Test + +class OffersByDeepLinkVerifierTest { + val subject = OffersByDeepLinkVerifierImpl() + + private val offersPayload = GenerateOffersMocks.RealOffers.toJsonObject() ?: JSONObject() + private val offers = VCLOffers( + payload = offersPayload, + all = Utils.offersFromJsonArray( + offersPayload.optJSONArray(VCLOffers.CodingKeys.KeyOffers) ?: JSONArray() + ), + responseCode = 0, + sessionToken = VCLToken(""), + challenge = "" + ) + private val correctDeepLink = DeepLinkMocks.CredentialManifestDeepLinkDevNet + private val wrongDeepLink = VCLDeepLink( + "velocity-network-devnet://issue?request_uri=https%3A%2F%2Fdevagent.velocitycareerlabs.io%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Aion%3AEiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbt%2Fissue%2Fget-credential-manifest%3Fid%3D611b5836e93d08000af6f1bc%26credential_types%3DPastEmploymentPosition%26issuerDid%3Ddid%3Aion%3AEiApMLdMb4NPb8sae9-hXGHP79W1gisApVSE80USPEbt" + ) + + @Test + fun verifyOffersSuccess() { + subject.verifyOffers( + offers, + correctDeepLink + ) { + it.handleResult({ isVerified -> + assert(isVerified) + }, { error -> + assert(false) { "${error.toJsonObject()}" } + }) + } + } + + @Test + fun verifyOffersError() { + subject.verifyOffers( + offers, + wrongDeepLink + ) { + it.handleResult({ + assert(false) { "${VCLErrorCode.MismatchedOfferIssuerDid.value} error code is expected" } + }, { error -> + assert(error.errorCode == VCLErrorCode.MismatchedOfferIssuerDid.value) + }) + } + } +} \ No newline at end of file diff --git a/VCL/src/test/java/io/velocitycareerlabs/verifiers/PresentationRequestByDeepLinkVerifierTest.kt b/VCL/src/test/java/io/velocitycareerlabs/verifiers/PresentationRequestByDeepLinkVerifierTest.kt new file mode 100644 index 00000000..fdc0e26b --- /dev/null +++ b/VCL/src/test/java/io/velocitycareerlabs/verifiers/PresentationRequestByDeepLinkVerifierTest.kt @@ -0,0 +1,47 @@ +package io.velocitycareerlabs.verifiers + +import io.velocitycareerlabs.api.entities.VCLDeepLink +import io.velocitycareerlabs.api.entities.error.VCLErrorCode +import io.velocitycareerlabs.api.entities.handleResult +import io.velocitycareerlabs.impl.data.verifiers.PresentationRequestByDeepLinkVerifierImpl +import io.velocitycareerlabs.infrastructure.resources.valid.DeepLinkMocks +import io.velocitycareerlabs.infrastructure.resources.valid.PresentationRequestMocks +import org.junit.Test + +class PresentationRequestByDeepLinkVerifierTest { + private val subject = PresentationRequestByDeepLinkVerifierImpl() + private val presentationRequest = PresentationRequestMocks.PresentationRequest + + private val correctDeepLink = DeepLinkMocks.PresentationRequestDeepLinkDevNet + private val wrongDeepLink = VCLDeepLink( + "velocity-network-devnet://inspect?request_uri=https%3A%2F%2Fagent.velocitycareerlabs.io%2Fapi%2Fholder%2Fv0.6%2Forg%2Fdid%3Avelocity%3A0xd4df29726d500f9b85bc6c7f1b3c021f163056%2Finspect%2Fget-presentation-request%3Fid%3D62e0e80c5ebfe73230b0becc%26inspectorDid%3Ddid%3Avelocity%3A0xd4df29726d500f9b85bc6c7f1b3c021f163056%26vendorOriginContext%3D%7B%22SubjectKey%22%3A%7B%22BusinessUnit%22%3A%22ZC%22%2C%22KeyCode%22%3A%2254514480%22%7D%2C%22Token%22%3A%22832077a4%22%7D" + ) + + @Test + fun testVerifyPresentationRequestSuccess() { + subject.verifyPresentationRequest( + presentationRequest, + correctDeepLink + ) { + it.handleResult({ isVerified -> + assert(isVerified) + }, { error -> + assert(false) { "${error.toJsonObject()}" } + }) + } + } + + @Test + fun testVerifyPresentationRequestError() { + subject.verifyPresentationRequest( + presentationRequest, + wrongDeepLink + ) { + it.handleResult({ + assert(false) { "${VCLErrorCode.MismatchedPresentationRequestInspectorDid.value} error code is expected" } + }, { error -> + assert(error.errorCode == VCLErrorCode.MismatchedPresentationRequestInspectorDid.value) + }) + } + } +} \ No newline at end of file diff --git a/VCL/src/test/java/io/velocitycareerlabs/utils/ProfileServiceTypeVerifierTest.kt b/VCL/src/test/java/io/velocitycareerlabs/verifiers/ProfileServiceTypeVerifierTest.kt similarity index 99% rename from VCL/src/test/java/io/velocitycareerlabs/utils/ProfileServiceTypeVerifierTest.kt rename to VCL/src/test/java/io/velocitycareerlabs/verifiers/ProfileServiceTypeVerifierTest.kt index 22057766..4e3febba 100644 --- a/VCL/src/test/java/io/velocitycareerlabs/utils/ProfileServiceTypeVerifierTest.kt +++ b/VCL/src/test/java/io/velocitycareerlabs/verifiers/ProfileServiceTypeVerifierTest.kt @@ -5,7 +5,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.velocitycareerlabs.utils +package io.velocitycareerlabs.verifiers import io.velocitycareerlabs.api.entities.* import io.velocitycareerlabs.api.entities.error.VCLStatusCode diff --git a/app/src/main/java/com/vcl/wallet/Utils.kt b/app/src/main/java/com/vcl/wallet/Utils.kt index 6f4a0be0..ffc2709b 100644 --- a/app/src/main/java/com/vcl/wallet/Utils.kt +++ b/app/src/main/java/com/vcl/wallet/Utils.kt @@ -14,10 +14,10 @@ object Utils { fun getApprovedRejectedOfferIdsMock(offers: VCLOffers): Pair, List> { val approvedOfferIds: List = listOfNotNull( - offers.all.optJSONObject(0)?.optString("id") + offers.all[0].id ) val rejectedOfferIds: List = listOfNotNull( - offers.all.optJSONObject(1)?.optString("id") + offers.all[1].id ) return Pair(approvedOfferIds, rejectedOfferIds) } diff --git a/build.gradle b/build.gradle index 44460111..07aca28b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,8 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = "1.7.21" + // Set "rc" suffix for VCL SDK release candidate + ext.rcSuffix = "rc" repositories { google() mavenCentral() @@ -16,6 +18,7 @@ buildscript { plugins { // Automation plugin + // https://github.com/gradle-nexus/publish-plugin id "io.github.gradle-nexus.publish-plugin" version "1.1.0" } @@ -35,4 +38,4 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir -} \ No newline at end of file +} diff --git a/publish-core.gradle b/publish-core.gradle index f2a295bc..2685c325 100644 --- a/publish-core.gradle +++ b/publish-core.gradle @@ -1,23 +1,32 @@ -Properties properties = new Properties() -properties.load(new FileInputStream(rootProject.file( - '/Volumes/Keybase/team/velocitycareers/mobile/android/maven/sonatype.properties' -))) +def propertiesFilePath = '/Volumes/Keybase/team/velocitycareers/mobile/android/maven/nexus.properties' +def propertiesFile = new File(propertiesFilePath) +def properties = new Properties() + +if (propertiesFile.exists()) { + try { + properties.load(new FileInputStream(propertiesFile)) + } catch (IOException e) { + e.printStackTrace() + } +} else { + println "Properties file not found: $propertiesFilePath" +} ext { - signingKeyId = System.getenv("SIGNING_KEY_ID") ?: properties["signingKeyId"] - signingPassword = System.getenv("SIGNING_PASSWORD") ?: properties["signingPassword"] - ossrhUsername = System.getenv("OSSRH_USERNAME") ?: properties["ossrhUsername"] - ossrhPassword = System.getenv("OSSRH_PASSWORD") ?: properties["ossrhPassword"] - sonatypeStagingProfileId = System.getenv("SONATYPE_STAGING_PROFILE_ID") ?: properties["sonatypeStagingProfileId"] - signingPrivateKey = System.getenv("SIGNING_KEY") ?: properties["signingPrivateKey"] + signingKeyId = System.getenv("ANDROID_NEXUS_SIGNING_KEY_ID") ?: properties["signingKeyId"] + signingPassword = System.getenv("ANDROID_NEXUS_SIGNING_PWD") ?: properties["signingPassword"] + ossrhUsername = System.getenv("ANDROID_NEXUS_OSSRH_USERNAME") ?: properties["ossrhUsername"] + ossrhPassword = System.getenv("ANDROID_NEXUS_OSSRH_PWD") ?: properties["ossrhPassword"] + sonatypeStagingProfileId = System.getenv("ANDROID_NEXUS_STAGING_PROFILE_ID") ?: properties["stagingProfileId"] + signingPrivateKey = System.getenv("ANDROID_NEXUS_PRIVATE_KEY") ?: properties["signingPrivateKey"] } -//println("signingKeyId: ${signingKeyId}") -//println("signingPassword: ${signingPassword}") -//println("ossrhUsername: ${ossrhUsername}") -//println("ossrhPassword: ${ossrhPassword}") -//println("sonatypeStagingProfileId: ${sonatypeStagingProfileId}") -//println("signingPrivateKey: ${signingPrivateKey}") +// println("signingKeyId: ${signingKeyId}") +// println("signingPassword: ${signingPassword}") +// println("ossrhUsername: ${ossrhUsername}") +// println("ossrhPassword: ${ossrhPassword}") +// println("sonatypeStagingProfileId: ${sonatypeStagingProfileId}") +// println("signingPrivateKey: ${signingPrivateKey}") nexusPublishing { repositories { @@ -29,4 +38,8 @@ nexusPublishing { snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) } } -} \ No newline at end of file +} + +// Create custom tasks for publishing +task publishRelease(dependsOn: [':VCL:publishReleasePublicationToSonatypeRepository']) +task publishRc(dependsOn: [':VCL:publishRcPublicationToSonatypeRepository']) \ No newline at end of file