From 862aef49a414ebf5873fc4c4e5c8727bc076997c Mon Sep 17 00:00:00 2001 From: Cristian G Date: Tue, 16 Apr 2024 12:02:54 -0400 Subject: [PATCH] feat: revocation notification --- .../walletsdk/domain/buildingblocks/Pollux.kt | 2 +- .../walletsdk/domain/models/Credential.kt | 1 + .../domain/models/StorableCredential.kt | 1 - .../walletsdk/pluto/CredentialRecovery.kt | 2 +- .../atala/prism/walletsdk/pluto/PlutoImpl.kt | 9 +++-- .../prism/walletsdk/pollux/PolluxImpl.kt | 20 +++++++---- .../walletsdk/pollux/models/AnonCredential.kt | 5 +-- .../walletsdk/pollux/models/JWTCredential.kt | 5 +-- .../walletsdk/pollux/models/W3CCredential.kt | 5 +-- .../walletsdk/prismagent/ConnectionManager.kt | 6 ++-- .../prism/walletsdk/prismagent/PrismAgent.kt | 2 +- .../revocation/RevocationNotification.kt | 8 +++-- .../prismagent/ConnectionManagerTest.kt | 34 ++++++++++++++++--- .../prism/walletsdk/prismagent/PolluxMock.kt | 2 +- .../ui/credentials/CredentialsAdapter.kt | 4 +++ .../res/layout/placeholder_credential.xml | 11 +++++- 16 files changed, 85 insertions(+), 32 deletions(-) diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/buildingblocks/Pollux.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/buildingblocks/Pollux.kt index d0c636fa4..e36ce789d 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/buildingblocks/Pollux.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/buildingblocks/Pollux.kt @@ -97,7 +97,7 @@ interface Pollux { * @param credentialData The byte array containing the credential data. * @return The restored credential. */ - fun restoreCredential(restorationIdentifier: String, credentialData: ByteArray): Credential + fun restoreCredential(restorationIdentifier: String, credentialData: ByteArray, revoked: Boolean): Credential /** * Converts a [Credential] object to a [StorableCredential] object of the specified [CredentialType]. diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/Credential.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/Credential.kt index f833789d8..7667da3d7 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/Credential.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/Credential.kt @@ -11,4 +11,5 @@ interface Credential { val subject: String? val claims: Array val properties: Map + var revoked: Boolean? } diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/StorableCredential.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/StorableCredential.kt index 9b3a05185..e2035c3bd 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/StorableCredential.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/domain/models/StorableCredential.kt @@ -10,7 +10,6 @@ interface StorableCredential : Credential { val credentialUpdated: String? val credentialSchema: String? val validUntil: String? - val revoked: Boolean? val availableClaims: Array /** diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pluto/CredentialRecovery.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pluto/CredentialRecovery.kt index 16f8d7ec7..6dcfea3a7 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pluto/CredentialRecovery.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pluto/CredentialRecovery.kt @@ -6,4 +6,4 @@ package io.iohk.atala.prism.walletsdk.pluto * @property restorationId The restoration ID associated with the credential recovery. * @property credentialData The credential data as a byte array. */ -class CredentialRecovery(val restorationId: String, val credentialData: ByteArray) +class CredentialRecovery(val restorationId: String, val credentialData: ByteArray, val revoked: Boolean) diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pluto/PlutoImpl.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pluto/PlutoImpl.kt index 506e10034..56357e26c 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pluto/PlutoImpl.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pluto/PlutoImpl.kt @@ -57,7 +57,11 @@ class PlutoImpl(private val connection: DbConnection) : Pluto { PrismPlutoDb.Schema.version, AfterVersion(1) { it.execute(null, "ALTER TABLE CredentialMetadata DROB COLUMN nonce;", 0) - it.execute(null, "ALTER TABLE CredentialMetadata DROB COLUMN linkSecretBlindingData;", 0) + it.execute( + null, + "ALTER TABLE CredentialMetadata DROB COLUMN linkSecretBlindingData;", + 0 + ) it.execute(null, "ALTER TABLE CredentialMetadata ADD COLUMN json TEXT;", 0) } ) @@ -926,7 +930,8 @@ class PlutoImpl(private val connection: DbConnection) : Pluto { it.executeAsList().map { credential -> CredentialRecovery( restorationId = credential.recoveryId, - credentialData = credential.credentialData + credentialData = credential.credentialData, + revoked = credential.revoked != 0 ) } } diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/PolluxImpl.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/PolluxImpl.kt index f1d622335..9c7cfc2c2 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/PolluxImpl.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/PolluxImpl.kt @@ -147,25 +147,29 @@ class PolluxImpl( */ override fun restoreCredential( restorationIdentifier: String, - credentialData: ByteArray + credentialData: ByteArray, + revoked: Boolean ): Credential { - return when (restorationIdentifier) { + val cred: Credential + when (restorationIdentifier) { "jwt+credential" -> { - JWTCredential(credentialData.decodeToString()) + cred = JWTCredential(credentialData.decodeToString()) } "anon+credential" -> { - AnonCredential.fromStorableData(credentialData) + cred = AnonCredential.fromStorableData(credentialData) } "w3c+credential" -> { - Json.decodeFromString(credentialData.decodeToString()) + cred = Json.decodeFromString(credentialData.decodeToString()) } else -> { throw PolluxError.InvalidCredentialError() } } + cred.revoked = revoked + return cred } /** @@ -258,8 +262,10 @@ class PolluxImpl( val presentationRequest = PresentationRequest(attachmentBase64.base64.base64UrlDecoded) val cred = anoncreds_wrapper.Credential(credential.id) - val requestedAttributes = presentationRequest.getRequestedAttributes().toListRequestedAttribute() - val requestedPredicate = presentationRequest.getRequestedPredicates().toListRequestedPredicate() + val requestedAttributes = + presentationRequest.getRequestedAttributes().toListRequestedAttribute() + val requestedPredicate = + presentationRequest.getRequestedPredicates().toListRequestedPredicate() val credentialRequests = CredentialRequests( credential = cred, diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/AnonCredential.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/AnonCredential.kt index d8ab731ff..5886ab112 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/AnonCredential.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/AnonCredential.kt @@ -78,6 +78,8 @@ data class AnonCredential( return properties.toMap() } + override var revoked: Boolean? = null + /** * Converts the current credential object into a storable credential object. * @@ -119,8 +121,7 @@ data class AnonCredential( override val validUntil: String? get() = null - override val revoked: Boolean? - get() = null + override var revoked: Boolean? = c.revoked override val availableClaims: Array get() = c.claims.map { it.key }.toTypedArray() diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/JWTCredential.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/JWTCredential.kt index 18b51af5e..5953f17e3 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/JWTCredential.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/JWTCredential.kt @@ -77,6 +77,8 @@ data class JWTCredential(val data: String) : Credential { return properties.toMap() } + override var revoked: Boolean? = null + /** * Converts the current instance of [JWTCredential] to a [StorableCredential]. * @@ -105,8 +107,7 @@ data class JWTCredential(val data: String) : Credential { get() = c.jwtPayload.verifiableCredential.credentialSchema?.type override val validUntil: String? get() = null - override val revoked: Boolean? - get() = null + override var revoked: Boolean? = c.revoked override val availableClaims: Array get() = c.claims.map { it.key }.toTypedArray() diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/W3CCredential.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/W3CCredential.kt index d0398c6d4..1defab055 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/W3CCredential.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/pollux/models/W3CCredential.kt @@ -82,6 +82,8 @@ data class W3CCredential @JvmOverloads constructor( return properties.toMap() } + override var revoked: Boolean? = null + /** * Converts the current W3CCredential object to a StorableCredential object that can be stored and retrieved from a storage system. * @@ -102,8 +104,7 @@ data class W3CCredential @JvmOverloads constructor( get() = c.credentialSchema?.type override val validUntil: String? get() = null - override val revoked: Boolean? - get() = null + override var revoked: Boolean? = c.revoked override val availableClaims: Array get() = claims.map { it.key }.toTypedArray() diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/ConnectionManager.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/ConnectionManager.kt index ca70a441e..2eca846eb 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/ConnectionManager.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/ConnectionManager.kt @@ -12,7 +12,6 @@ import io.iohk.atala.prism.walletsdk.domain.models.CredentialType import io.iohk.atala.prism.walletsdk.domain.models.DID import io.iohk.atala.prism.walletsdk.domain.models.DIDPair import io.iohk.atala.prism.walletsdk.domain.models.Message -import io.iohk.atala.prism.walletsdk.pollux.models.JWTCredential import io.iohk.atala.prism.walletsdk.prismagent.connectionsmanager.ConnectionsManager import io.iohk.atala.prism.walletsdk.prismagent.connectionsmanager.DIDCommConnection import io.iohk.atala.prism.walletsdk.prismagent.mediation.MediationHandler @@ -214,7 +213,7 @@ class ConnectionManager( internal fun processMessages(arrayMessages: Array>) { scope.launch { - val messagesIds = mutableListOf() + val messagesIds = mutableListOf() val messages = mutableListOf() arrayMessages.map { pair -> messagesIds.add(pair.first) @@ -233,7 +232,8 @@ class ConnectionManager( matchingMessages.forEach { message -> val issueMessage = IssueCredential.fromMessage(message) if (pollux.extractCredentialFormatFromMessage(issueMessage.attachments) == CredentialType.JWT) { - val attachment = issueMessage.attachments.firstOrNull()?.data as? AttachmentBase64 + val attachment = + issueMessage.attachments.firstOrNull()?.data as? AttachmentBase64 attachment?.let { val credentialId = it.base64.base64UrlDecoded pluto.revokeCredential(credentialId) diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PrismAgent.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PrismAgent.kt index 50ae8df4a..0f3d11e7a 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PrismAgent.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PrismAgent.kt @@ -881,7 +881,7 @@ class PrismAgent { return pluto.getAllCredentials() .map { list -> list.map { - pollux.restoreCredential(it.restorationId, it.credentialData) + pollux.restoreCredential(it.restorationId, it.credentialData, it.revoked) } } } diff --git a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/protocols/revocation/RevocationNotification.kt b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/protocols/revocation/RevocationNotification.kt index 6780c1aa0..6a7a0a402 100644 --- a/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/protocols/revocation/RevocationNotification.kt +++ b/atala-prism-sdk/src/commonMain/kotlin/io/iohk/atala/prism/walletsdk/prismagent/protocols/revocation/RevocationNotification.kt @@ -1,3 +1,5 @@ +@file:Suppress("ktlint:standard:import-ordering") + package io.iohk.atala.prism.walletsdk.prismagent.protocols.revocation import io.iohk.atala.prism.walletsdk.domain.models.DID @@ -39,8 +41,8 @@ class RevocationNotification( fun fromMessage(message: Message): RevocationNotification { require( message.piuri == ProtocolType.PrismRevocation.value && - message.from != null && - message.to != null + message.from != null && + message.to != null ) { throw PrismAgentError.InvalidMessageType( type = message.piuri, @@ -54,4 +56,4 @@ class RevocationNotification( ) } } -} \ No newline at end of file +} diff --git a/atala-prism-sdk/src/commonTest/kotlin/io/iohk/atala/prism/walletsdk/prismagent/ConnectionManagerTest.kt b/atala-prism-sdk/src/commonTest/kotlin/io/iohk/atala/prism/walletsdk/prismagent/ConnectionManagerTest.kt index 82b3d4f35..e8e52b5d1 100644 --- a/atala-prism-sdk/src/commonTest/kotlin/io/iohk/atala/prism/walletsdk/prismagent/ConnectionManagerTest.kt +++ b/atala-prism-sdk/src/commonTest/kotlin/io/iohk/atala/prism/walletsdk/prismagent/ConnectionManagerTest.kt @@ -29,11 +29,9 @@ import org.mockito.MockitoAnnotations import org.mockito.kotlin.any import java.util.UUID import kotlinx.coroutines.flow.Flow -import org.mockito.ArgumentCaptor import org.mockito.Mockito.anyList import org.mockito.kotlin.anyArray import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.mock import kotlin.test.assertNotNull import kotlin.test.BeforeTest import kotlin.test.Test @@ -179,6 +177,32 @@ class ConnectionManagerTest { ) } ) + val attachments: Array = + arrayOf( + AttachmentDescriptor( + mediaType = "application/json", + format = CredentialType.JWT.type, + data = AttachmentBase64(base64 = "asdfasdfasdfasdfasdfasdfasdfasdfasdf".base64UrlEncoded) + ) + ) + val listMessages = listOf( + Message( + piuri = ProtocolType.DidcommconnectionRequest.value, + body = "" + ), + Message( + piuri = ProtocolType.DidcommIssueCredential.value, + thid = UUID.randomUUID().toString(), + from = DID("did:peer:asdf897a6sdf"), + to = DID("did:peer:f706sg678ha"), + attachments = attachments, + body = """{}""" + ) + ) + val messageList: Flow> = flow { + emit(listMessages) + } + `when`(plutoMock.getAllMessages()).thenReturn(messageList) connectionManager.startFetchingMessages() assertNotNull(connectionManager.fetchingMessagesJob) @@ -219,16 +243,16 @@ class ConnectionManagerTest { val messages = arrayOf( Pair( - threadId, Message( + threadId, + Message( piuri = ProtocolType.PrismRevocation.value, from = DID("did:peer:0978aszdf7890asg"), to = DID("did:peer:asdf9068asdf"), - body = """{"threadId":"$threadId","comment":null}""" + body = """{"issueCredentialProtocolThreadId":"$threadId","comment":null}""" ) ) ) - connectionManager.processMessages(messages) val argumentCaptor = argumentCaptor() verify(plutoMock).revokeCredential(argumentCaptor.capture()) diff --git a/atala-prism-sdk/src/commonTest/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PolluxMock.kt b/atala-prism-sdk/src/commonTest/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PolluxMock.kt index c981e3581..95ab39ab1 100644 --- a/atala-prism-sdk/src/commonTest/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PolluxMock.kt +++ b/atala-prism-sdk/src/commonTest/kotlin/io/iohk/atala/prism/walletsdk/prismagent/PolluxMock.kt @@ -53,7 +53,7 @@ class PolluxMock : Pollux { TODO("Not yet implemented") } - override fun restoreCredential(restorationIdentifier: String, credentialData: ByteArray): Credential { + override fun restoreCredential(restorationIdentifier: String, credentialData: ByteArray, revoked: Boolean): Credential { TODO("Not yet implemented") } diff --git a/sampleapp/src/main/java/io/iohk/atala/prism/sampleapp/ui/credentials/CredentialsAdapter.kt b/sampleapp/src/main/java/io/iohk/atala/prism/sampleapp/ui/credentials/CredentialsAdapter.kt index c9e0bcc52..06623e9c1 100644 --- a/sampleapp/src/main/java/io/iohk/atala/prism/sampleapp/ui/credentials/CredentialsAdapter.kt +++ b/sampleapp/src/main/java/io/iohk/atala/prism/sampleapp/ui/credentials/CredentialsAdapter.kt @@ -62,6 +62,7 @@ class CredentialsAdapter(private var data: MutableList = mutableList private val type: TextView = itemView.findViewById(R.id.credential_id) private val issuanceDate: TextView = itemView.findViewById(R.id.credential_issuance_date) private val expDate: TextView = itemView.findViewById(R.id.credential_expiration_date) + private val revoked: TextView = itemView.findViewById(R.id.revoked) private val typeString: String = itemView.context.getString(R.string.credential_type) private val issuanceString: String = itemView.context.getString(R.string.credential_issuance) private val expirationString: String = itemView.context.getString(R.string.credential_expiration) @@ -70,6 +71,9 @@ class CredentialsAdapter(private var data: MutableList = mutableList when (cred::class) { JWTCredential::class -> { val jwt = cred as JWTCredential + if (jwt.revoked != null && jwt.revoked!!) { + revoked.visibility = View.VISIBLE + } type.text = String.format(typeString, "JWT") // TODO: Check what else to display jwt.jwtPayload.nbf?.let { diff --git a/sampleapp/src/main/res/layout/placeholder_credential.xml b/sampleapp/src/main/res/layout/placeholder_credential.xml index fb8ae700e..03ad4b9ff 100644 --- a/sampleapp/src/main/res/layout/placeholder_credential.xml +++ b/sampleapp/src/main/res/layout/placeholder_credential.xml @@ -8,7 +8,7 @@ @@ -30,6 +30,15 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" /> + + \ No newline at end of file