Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
waltkb committed Dec 14, 2023
2 parents 064c3c8 + 3ab0368 commit e75398c
Show file tree
Hide file tree
Showing 10 changed files with 315 additions and 99 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package id.walt.webwallet.db.models

import kotlinx.uuid.exposed.KotlinxUUIDTable
import kotlinx.uuid.exposed.kotlinxUUID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.javatime.timestamp

object Events : KotlinxUUIDTable("events") {
object Events : IntIdTable("events") {
val tenant = varchar("tenant", 128).default("global")
val originator = varchar("originator", 128).default("unknown")
val account = kotlinxUUID("accountId")
val wallet = reference("wallet", Wallets)
val account = kotlinxUUID("account")
val wallet = kotlinxUUID("wallet").nullable()
val timestamp = timestamp("timestamp")
val event = varchar("event", 48)
val action = varchar("action", 48)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import java.net.URLDecoder
import kotlin.math.abs
import kotlin.time.Duration.Companion.seconds


class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : WalletService(tenant, accountId, walletId) {
class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) :
WalletService(tenant, accountId, walletId) {

companion object {
init {
Expand All @@ -84,7 +84,7 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W
it.document
}

override suspend fun deleteCredential(id: String) = let{
override suspend fun deleteCredential(id: String) = let {
CredentialsService.get(walletId, id)?.run {
logEvent(EventType.Credential.Delete, "wallet", createCredentialEventData(this.parsedDocument, null))
}
Expand Down Expand Up @@ -192,25 +192,31 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W
) : IllegalArgumentException(message)




/**
* @return redirect uri
*/
override suspend fun usePresentationRequest(request: String, did: String, selectedCredentialIds: List<String>, disclosures: Map<String, List<String>>?): Result<String?> {
override suspend fun usePresentationRequest(
request: String,
did: String,
selectedCredentialIds: List<String>,
disclosures: Map<String, List<String>>?
): Result<String?> {
val credentialWallet = getCredentialWallet(did)

val authReq = AuthorizationRequest.fromHttpQueryString(Url(request).encodedQuery)
println("Auth req: $authReq")

println("USING PRESENTATION REQUEST, SELECTED CREDENTIALS: $selectedCredentialIds")

SessionAttributes.HACK_outsideMappedSelectedCredentialsPerSession[authReq.state + authReq.presentationDefinition] = selectedCredentialIds
SessionAttributes.HACK_outsideMappedSelectedCredentialsPerSession[authReq.state + authReq.presentationDefinition] =
selectedCredentialIds
if (disclosures != null) {
SessionAttributes.HACK_outsideMappedSelectedDisclosuresPerSession[authReq.state + authReq.presentationDefinition] = disclosures
SessionAttributes.HACK_outsideMappedSelectedDisclosuresPerSession[authReq.state + authReq.presentationDefinition] =
disclosures
}

val presentationSession = credentialWallet.initializeAuthorization(authReq, 60.seconds, selectedCredentialIds.toSet())
val presentationSession =
credentialWallet.initializeAuthorization(authReq, 60.seconds, selectedCredentialIds.toSet())
println("Initialized authorization (VPPresentationSession): $presentationSession")

println("Resolved presentation definition: ${presentationSession.authorizationRequest!!.presentationDefinition!!.toJSONString()}")
Expand Down Expand Up @@ -265,7 +271,8 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W
override suspend fun resolvePresentationRequest(request: String): String {
val credentialWallet = getAnyCredentialWallet()

return Url(request).protocolWithAuthority.plus("?").plus(credentialWallet.parsePresentationRequest(request).toHttpQueryString())
return Url(request).protocolWithAuthority.plus("?")
.plus(credentialWallet.parsePresentationRequest(request).toHttpQueryString())
}


Expand All @@ -279,7 +286,8 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W
)
}

private fun getAnyCredentialWallet() = credentialWallets.values.firstOrNull() ?: getCredentialWallet("did:test:test")
private fun getAnyCredentialWallet() =
credentialWallets.values.firstOrNull() ?: getCredentialWallet("did:test:test")

private val testCIClientConfig = OpenIDClientConfig("test-client", null, redirectUri = "http://blank")

Expand Down Expand Up @@ -361,7 +369,8 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W
}.body<JsonObject>().let { BatchCredentialResponse.fromJSON(it) }
println("credentialResponses: $credentialResponses")

credentialResponses.credentialResponses ?: throw IllegalArgumentException("No credential responses returned")
credentialResponses.credentialResponses
?: throw IllegalArgumentException("No credential responses returned")
}

credReqs.size == 1 -> {
Expand Down Expand Up @@ -390,51 +399,54 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W

val credentialJwt = credential.decodeJws(withSignature = true)

val credentialResultPair = when (val typ = credentialJwt.header["typ"]?.jsonPrimitive?.content?.lowercase()) {
"jwt" -> {
val credentialId = credentialJwt.payload["vc"]!!.jsonObject["id"]?.jsonPrimitive?.content?.takeIf { it.isNotBlank() }
?: randomUUID()

println("Got JWT credential: $credentialJwt")

Pair(
WalletCredential(
wallet = walletId,
id = credentialId,
document = credential,
disclosures = null,
addedOn = Clock.System.now()
), createCredentialEventData(credentialJwt.payload["vc"]?.jsonObject, typ)
)
}
val credentialResultPair =
when (val typ = credentialJwt.header["typ"]?.jsonPrimitive?.content?.lowercase()) {
"jwt" -> {
val credentialId =
credentialJwt.payload["vc"]!!.jsonObject["id"]?.jsonPrimitive?.content?.takeIf { it.isNotBlank() }
?: randomUUID()

println("Got JWT credential: $credentialJwt")

Pair(
WalletCredential(
wallet = walletId,
id = credentialId,
document = credential,
disclosures = null,
addedOn = Clock.System.now()
), createCredentialEventData(credentialJwt.payload["vc"]?.jsonObject, typ)
)
}

"vc+sd-jwt" -> {
val credentialId = credentialJwt.payload["id"]?.jsonPrimitive?.content?.takeIf { it.isNotBlank() }
?: randomUUID()
"vc+sd-jwt" -> {
val credentialId =
credentialJwt.payload["id"]?.jsonPrimitive?.content?.takeIf { it.isNotBlank() }
?: randomUUID()

println("Got SD-JWT credential: $credentialJwt")
println("Got SD-JWT credential: $credentialJwt")

val disclosures = credentialJwt.signature.split("~").drop(1)
println("Disclosures (${disclosures.size}): $disclosures")
val disclosures = credentialJwt.signature.split("~").drop(1)
println("Disclosures (${disclosures.size}): $disclosures")

val disclosuresString = disclosures.joinToString("~")
val disclosuresString = disclosures.joinToString("~")

val credentialWithoutDisclosures = credential.substringBefore("~")
val credentialWithoutDisclosures = credential.substringBefore("~")

Pair(
WalletCredential(
wallet = walletId,
id = credentialId,
document = credentialWithoutDisclosures,
disclosures = disclosuresString,
addedOn = Clock.System.now()
), createCredentialEventData(credentialJwt.payload["vc"]!!.jsonObject, typ)
)
}
Pair(
WalletCredential(
wallet = walletId,
id = credentialId,
document = credentialWithoutDisclosures,
disclosures = disclosuresString,
addedOn = Clock.System.now()
), createCredentialEventData(credentialJwt.payload["vc"]!!.jsonObject, typ)
)
}

null -> throw IllegalArgumentException("WalletCredential JWT does not have \"typ\"")
else -> throw IllegalArgumentException("Invalid credential \"typ\": $typ")
}
null -> throw IllegalArgumentException("WalletCredential JWT does not have \"typ\"")
else -> throw IllegalArgumentException("Invalid credential \"typ\": $typ")
}
logEvent(
EventType.Credential.Accept,
parsedOfferReq.credentialOffer!!.credentialIssuer,
Expand All @@ -454,7 +466,17 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W
/* DIDs */

override suspend fun createDid(method: String, args: Map<String, JsonPrimitive>): String {
val key = args["keyId"]?.content?.takeIf { it.isNotEmpty() }?.let { getKey(it) } ?: LocalKey.generate(KeyType.Ed25519)
// val key = args["keyId"]?.content?.takeIf { it.isNotEmpty() }?.let { getKey(it) }
// ?: getKey(generateKey(KeyType.Ed25519.name))//TODO: throws keyid unique constraint violation
val key = args["keyId"]?.content?.takeIf { it.isNotEmpty() }?.let { getKey(it) } ?: let {
LocalKey.generate(KeyType.Ed25519)
}.also {
logEvent(
EventType.Key.Create, "wallet", KeyEventData(
id = it.getKeyId(), algorithm = it.keyType.name, keyManagementService = "local"
)
)
}
val options = getDidOptions(method, args)
val result = DidService.registerByKey(method, key, options)

Expand Down Expand Up @@ -551,11 +573,13 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W

override suspend fun generateKey(type: String): String =
LocalKey.generate(KeyType.valueOf(type)).let { createdKey ->
logEvent(EventType.Key.Create, "wallet", KeyEventData(
id = createdKey.getKeyId(),
algorithm = createdKey.keyType.name,
keyManagementService = "local",
))
logEvent(
EventType.Key.Create, "wallet", KeyEventData(
id = createdKey.getKeyId(),
algorithm = createdKey.keyType.name,
keyManagementService = "local",
)
)
insertKey(createdKey)
createdKey.getKeyId()
}
Expand Down Expand Up @@ -590,11 +614,13 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W

val key = keyResult.getOrThrow()
val keyId = key.getKeyId()
logEvent(EventType.Key.Import, "wallet", KeyEventData(
id = keyId,
algorithm = key.keyType.name,
keyManagementService = EventDataNotAvailable
))
logEvent(
EventType.Key.Import, "wallet", KeyEventData(
id = keyId,
algorithm = key.keyType.name,
keyManagementService = EventDataNotAvailable
)
)

insertKey(key)

Expand Down Expand Up @@ -652,13 +678,13 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W
val pageSize = filter.limit
val count = EventService.count(walletId, dataFilter)
val offset = startingAfterItemIndex + 1
val events = EventService.get(walletId, filter.limit, offset, dataFilter)
val events = EventService.get(accountId, walletId, filter.limit, offset, dataFilter)
EventLogFilterDataResult(
items = events,
count = events.size,
currentStartingAfter = computeCurrentStartingAfter(startingAfterItemIndex, pageSize),
nextStartingAfter = computeNextStartingAfter(startingAfterItemIndex, pageSize, count)
)
)
}.fold(onSuccess = {
it
}, onFailure = {
Expand Down Expand Up @@ -713,6 +739,7 @@ class SSIKit2WalletService(tenant: String?, accountId: UUID, walletId: UUID) : W
)
)

//TODO: move to related entity
private fun createCredentialEventData(json: JsonObject?, type: String?) = CredentialEventData(
ecosystem = EventDataNotAvailable,
issuerId = json?.jsonObject?.get("issuer")?.jsonObject?.get("id")?.jsonPrimitive?.content
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package id.walt.webwallet.service.account

import id.walt.web.controllers.generateToken
import id.walt.webwallet.db.models.*
import id.walt.webwallet.db.models.todo.AccountIssuers
import id.walt.webwallet.db.models.todo.Issuers
import id.walt.webwallet.service.WalletServiceManager
import id.walt.web.controllers.generateToken
import id.walt.webwallet.service.events.AccountEventData
import id.walt.webwallet.service.events.Event
import id.walt.webwallet.service.events.EventService
import id.walt.webwallet.service.events.EventType
import id.walt.webwallet.web.model.AccountRequest
import id.walt.webwallet.web.model.AddressAccountRequest
import id.walt.webwallet.web.model.EmailAccountRequest
import id.walt.webwallet.db.models.*
import kotlinx.datetime.toKotlinInstant
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonPrimitive
Expand All @@ -31,6 +35,16 @@ object AccountsService {
}

val walletService = WalletServiceManager.getWalletService(tenant, registeredUserId, createdInitialWalletId)
EventService.add(
Event(
action = EventType.Account.Create,
tenant = tenant,
originator = "wallet",
account = registeredUserId,
wallet = createdInitialWalletId,
data = AccountEventData(accountId = request.name)
)
)

// Add default data:
val createdDid = walletService.createDid("key", mapOf("alias" to JsonPrimitive("Onboarding")))
Expand Down Expand Up @@ -61,6 +75,16 @@ object AccountsService {
is AddressAccountRequest -> Web3WalletAccountStrategy.authenticate(tenant, request)
}
}.fold(onSuccess = {
EventService.add(
Event(
action = EventType.Account.Login,
tenant = tenant,
originator = "wallet",
account = it.id,
wallet = null,
data = AccountEventData(accountId = it.username)
)
)
Result.success(
AuthenticationResult(
id = it.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ data class Event(
val tenant: String? = null,
val originator: String? = null,
val account: UUID,
val wallet: UUID,
val wallet: UUID? = null,
val data: JsonObject,
) {
constructor(
action: EventType.Action, tenant: String?, originator: String?, account: UUID, wallet: UUID, data: EventData
action: EventType.Action, tenant: String?, originator: String?, account: UUID, wallet: UUID?, data: EventData
) : this(
event = action.type,
action = action.toString(),
Expand All @@ -44,7 +44,7 @@ data class Event(
tenant = row[Events.tenant],
originator = row[Events.originator],
account = row[Events.account],
wallet = row[Events.wallet].value,
wallet = row[Events.wallet],
data = Json.parseToJsonElement(row[Events.data]).jsonObject,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import kotlinx.uuid.UUID
import org.jetbrains.exposed.sql.Query
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.or
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction

object EventService {
fun get(walletId: UUID, limit: Int, offset: Long, dataFilter: Map<String, String> = emptyMap()): List<Event> =
fun get(accountId: UUID, walletId: UUID, limit: Int, offset: Long, dataFilter: Map<String, String> = emptyMap()): List<Event> =
addWhereClause(
Events.select { Events.wallet eq walletId }, dataFilter
Events.select { Events.account eq accountId or (Events.wallet eq walletId) }, dataFilter
).orderBy(Events.timestamp).limit(n = limit, offset = offset).map {
Event(it)
}
Expand Down
Loading

0 comments on commit e75398c

Please sign in to comment.