Skip to content

Commit

Permalink
fix(prism-agent): introduce generic secret store for CD (#727)
Browse files Browse the repository at this point in the history
Signed-off-by: Pat Losoponkul <[email protected]>
  • Loading branch information
patlo-iog authored Sep 20, 2023
1 parent 2c5bc51 commit 3d4aacd
Show file tree
Hide file tree
Showing 27 changed files with 536 additions and 174 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.iohk.atala.pollux.core.service

import io.iohk.atala.agent.walletapi.storage
import io.iohk.atala.agent.walletapi.storage.{DIDSecret, DIDSecretStorage}
import io.iohk.atala.mercury.model.DidId
import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage
import io.iohk.atala.agent.walletapi.storage.CredentialDefinitionSecret
import io.iohk.atala.pollux.anoncreds.{AnoncredLib, SchemaDef}
import io.iohk.atala.pollux.core.model.error.CredentialSchemaError
import io.iohk.atala.pollux.core.model.error.CredentialSchemaError.URISyntaxError
Expand All @@ -27,11 +27,10 @@ import java.util.UUID
import scala.util.Try

class CredentialDefinitionServiceImpl(
didSecretStorage: DIDSecretStorage,
genericSecretStorage: GenericSecretStorage,
credentialDefinitionRepository: CredentialDefinitionRepository,
uriDereferencer: URIDereferencer
) extends CredentialDefinitionService {
private val KEY_ID = "anoncred-credential-definition-private-key"

override def create(in: CredentialDefinition.Input): Result[CredentialDefinition] = {
for {
Expand Down Expand Up @@ -80,11 +79,10 @@ class CredentialDefinitionServiceImpl(
proofKeyCredentialDefinitionJson
)
createdCredentialDefinition <- credentialDefinitionRepository.create(cd)
_ <-
didSecretStorage.insertKey(
DidId(in.author),
s"$KEY_ID/${createdCredentialDefinition.guid}",
DIDSecret(privateCredentialDefinitionJson, PrivateCredentialDefinitionSchemaSerDesV1.version)
_ <- genericSecretStorage
.set(
createdCredentialDefinition.guid,
CredentialDefinitionSecret(privateCredentialDefinitionJson)
)
} yield createdCredentialDefinition
}.mapError {
Expand Down Expand Up @@ -123,7 +121,7 @@ class CredentialDefinitionServiceImpl(

object CredentialDefinitionServiceImpl {
val layer: URLayer[
DIDSecretStorage & CredentialDefinitionRepository & URIDereferencer,
GenericSecretStorage & CredentialDefinitionRepository & URIDereferencer,
CredentialDefinitionService
] =
ZLayer.fromFunction(CredentialDefinitionServiceImpl(_, _, _))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.iohk.atala.pollux.core.service

import io.iohk.atala.agent.walletapi.memory.DIDSecretStorageInMemory
import io.iohk.atala.agent.walletapi.memory.GenericSecretStorageInMemory
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.schema.CredentialDefinition
import io.iohk.atala.pollux.core.repository.CredentialDefinitionRepositoryInMemory
Expand All @@ -15,7 +15,7 @@ trait CredentialDefinitionServiceSpecHelper {
protected val defaultWalletLayer = ZLayer.succeed(WalletAccessContext(WalletId.default))

protected val credentialDefinitionServiceLayer =
DIDSecretStorageInMemory.layer ++ CredentialDefinitionRepositoryInMemory.layer ++ ResourceURIDereferencerImpl.layer >>>
GenericSecretStorageInMemory.layer ++ CredentialDefinitionRepositoryInMemory.layer ++ ResourceURIDereferencerImpl.layer >>>
CredentialDefinitionServiceImpl.layer ++ defaultWalletLayer

val defaultDefinition =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import io.iohk.atala.agent.walletapi.service.{
WalletManagementServiceImpl
}
import io.iohk.atala.agent.walletapi.sql.{JdbcDIDNonSecretStorage, JdbcEntityRepository, JdbcWalletNonSecretStorage}
import io.iohk.atala.agent.walletapi.storage.DIDKeySecretStorageImpl
import io.iohk.atala.castor.controller.{DIDControllerImpl, DIDRegistrarControllerImpl}
import io.iohk.atala.castor.core.service.DIDServiceImpl
import io.iohk.atala.castor.core.util.DIDOperationValidator
Expand Down Expand Up @@ -170,7 +169,6 @@ object MainApp extends ZIOAppDefault {
GrpcModule.irisStubLayer,
GrpcModule.prismNodeStubLayer,
// storage
DIDKeySecretStorageImpl.layer,
RepoModule.agentContextAwareTransactorLayer ++ RepoModule.agentTransactorLayer >>> JdbcDIDNonSecretStorage.layer,
RepoModule.agentContextAwareTransactorLayer >>> JdbcWalletNonSecretStorage.layer,
RepoModule.allSecretStorageLayer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import doobie.util.transactor.Transactor
import io.grpc.ManagedChannelBuilder
import io.iohk.atala.agent.server.config.AppConfig
import io.iohk.atala.agent.walletapi.crypto.Apollo
import io.iohk.atala.agent.walletapi.memory.GenericSecretStorageInMemory
import io.iohk.atala.agent.walletapi.memory.{DIDSecretStorageInMemory, WalletSecretStorageInMemory}
import io.iohk.atala.agent.walletapi.sql.JdbcGenericSecretStorage
import io.iohk.atala.agent.walletapi.sql.{JdbcDIDSecretStorage, JdbcWalletSecretStorage}
import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage
import io.iohk.atala.agent.walletapi.storage.{DIDSecretStorage, WalletSecretStorage}
import io.iohk.atala.agent.walletapi.util.SeedResolver
import io.iohk.atala.agent.walletapi.vault.VaultGenericSecretStorage
import io.iohk.atala.agent.walletapi.vault.{
VaultDIDSecretStorage,
VaultKVClient,
Expand Down Expand Up @@ -139,7 +143,7 @@ object RepoModule {
SystemModule.configLayer >>> vaultClientConfig
}

val allSecretStorageLayer: TaskLayer[DIDSecretStorage & WalletSecretStorage] = {
val allSecretStorageLayer: TaskLayer[DIDSecretStorage & WalletSecretStorage & GenericSecretStorage] = {
ZLayer.fromZIO {
ZIO
.service[AppConfig]
Expand All @@ -148,25 +152,28 @@ object RepoModule {
.flatMap {
case "vault" =>
ZIO.succeed(
ZLayer.make[DIDSecretStorage & WalletSecretStorage](
ZLayer.make[DIDSecretStorage & WalletSecretStorage & GenericSecretStorage](
VaultDIDSecretStorage.layer,
VaultWalletSecretStorage.layer,
VaultGenericSecretStorage.layer,
vaultClientLayer,
)
)
case "postgres" =>
ZIO.succeed(
ZLayer.make[DIDSecretStorage & WalletSecretStorage](
ZLayer.make[DIDSecretStorage & WalletSecretStorage & GenericSecretStorage](
JdbcDIDSecretStorage.layer,
JdbcWalletSecretStorage.layer,
JdbcGenericSecretStorage.layer,
agentContextAwareTransactorLayer,
)
)
case "memory" =>
ZIO.succeed(
ZLayer.make[DIDSecretStorage & WalletSecretStorage](
ZLayer.make[DIDSecretStorage & WalletSecretStorage & GenericSecretStorage](
DIDSecretStorageInMemory.layer,
WalletSecretStorageInMemory.layer,
GenericSecretStorageInMemory.layer
)
)
case backend =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.iohk.atala.shared.models.WalletId

import java.util.UUID
import scala.util.Try
import scala.language.implicitConversions

case class ApiKeyAuthenticatorImpl(
apiKeyConfig: ApiKeyConfig,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package io.iohk.atala.pollux.credentialdefinition

import io.iohk.atala.agent.walletapi.model.Entity
import io.iohk.atala.agent.walletapi.storage.DIDSecretStorage
import io.iohk.atala.agent.walletapi.storage.CredentialDefinitionSecret
import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage
import io.iohk.atala.api.http.ErrorResponse
import io.iohk.atala.container.util.MigrationAspects.*
import io.iohk.atala.iam.authentication.Authenticator
import io.iohk.atala.mercury.model.DidId
import io.iohk.atala.pollux.core.service.serdes.{
PrivateCredentialDefinitionSchemaSerDesV1,
ProofKeyCredentialDefinitionSchemaSerDesV1,
Expand Down Expand Up @@ -116,23 +116,17 @@ object CredentialDefinitionBasicSpec extends ZIOSpecDefault with CredentialDefin
fetchedCredentialDefinition.keyCorrectnessProof.toString()
)
assertValidKeyCorrectnessProof = assert(maybeValidKeyCorrectnessProof)(Assertion.isTrue)
svc <- ZIO.service[DIDSecretStorage]
maybeDidSecret <- svc
.getKey(
DidId(credentialDefinitionInput.author),
s"anoncred-credential-definition-private-key/${fetchedCredentialDefinition.guid}",
PrivateCredentialDefinitionSchemaSerDesV1.version
)
storage <- ZIO.service[GenericSecretStorage]
maybeDidSecret <- storage
.get(fetchedCredentialDefinition.guid)
.provideSomeLayer(Entity.Default.wacLayer)
(assertCorrectPrivateDefinitionSchema, maybeValidPrivateDefinitionZIO) = maybeDidSecret match {
maybeValidPrivateDefinitionZIO = maybeDidSecret match {
case Some(didSecret) =>
val schemaAssertion =
assert(didSecret.schemaId)(equalTo(PrivateCredentialDefinitionSchemaSerDesV1.version))
val validPrivateDefinition =
PrivateCredentialDefinitionSchemaSerDesV1.schemaSerDes.validate(didSecret.json.toString())
(schemaAssertion, validPrivateDefinition)
validPrivateDefinition
case None =>
(assert(false)(Assertion.isTrue), ZIO.succeed(false))
ZIO.succeed(false)
}
maybeValidPrivateDefinition <- maybeValidPrivateDefinitionZIO
assertValidPrivateDefinition = assert(maybeValidPrivateDefinition)(Assertion.isTrue)
Expand All @@ -141,7 +135,6 @@ object CredentialDefinitionBasicSpec extends ZIOSpecDefault with CredentialDefin
credentialDefinitionIsFetched &&
assertValidPublicDefinition &&
assertValidKeyCorrectnessProof &&
assertCorrectPrivateDefinitionSchema &&
assertValidPrivateDefinition
},
test("get the credential definition by the wrong id") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package io.iohk.atala.pollux.credentialdefinition

import com.dimafeng.testcontainers.PostgreSQLContainer
import io.iohk.atala.agent.walletapi.memory.DIDSecretStorageInMemory
import io.iohk.atala.agent.walletapi.memory.GenericSecretStorageInMemory
import io.iohk.atala.agent.walletapi.model.{ManagedDIDState, PublicationState}
import io.iohk.atala.agent.walletapi.service.{ManagedDIDService, MockManagedDIDService}
import io.iohk.atala.agent.walletapi.storage.DIDSecretStorage
import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage
import io.iohk.atala.api.http.ErrorResponse
import io.iohk.atala.castor.core.model.did.PrismDIDOperation
import io.iohk.atala.iam.authentication.{Authenticator, DefaultEntityAuthenticator}
Expand Down Expand Up @@ -53,7 +53,7 @@ trait CredentialDefinitionTestTools extends PostgresTestContainerSupport {
]

private val controllerLayer =
DIDSecretStorageInMemory.layer >+>
GenericSecretStorageInMemory.layer >+>
systemTransactorLayer >+> contextAwareTransactorLayer >+> JdbcCredentialDefinitionRepository.layer >+>
ResourceURIDereferencerImpl.layer >+>
CredentialDefinitionServiceImpl.layer >+>
Expand All @@ -78,7 +78,7 @@ trait CredentialDefinitionTestTools extends PostgresTestContainerSupport {
lazy val testEnvironmentLayer = ZLayer.makeSome[
ManagedDIDService,
CredentialDefinitionController & CredentialDefinitionRepository & CredentialDefinitionService &
PostgreSQLContainer & Authenticator & DIDSecretStorage
PostgreSQLContainer & Authenticator & GenericSecretStorage
](
controllerLayer,
pgContainerLayer,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ALTER TABLE public.peer_did_rand_key DROP COLUMN "schema_id";

CREATE TABLE public.generic_secret (
"key" TEXT NOT NULL,
"payload" TEXT NOT NULL,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"wallet_id" UUID REFERENCES public.wallet ("wallet_id") NOT NULL,
CONSTRAINT unique_key_wallet UNIQUE ("key", "wallet_id")
);

ALTER TABLE public.generic_secret ENABLE ROW LEVEL SECURITY;

CREATE POLICY generic_secret_wallet_isolation
ON public.generic_secret
USING (wallet_id = current_setting('app.current_wallet_id')::UUID);
Original file line number Diff line number Diff line change
@@ -1,46 +1,47 @@
package io.iohk.atala.agent.walletapi.memory

import io.iohk.atala.agent.walletapi.storage.DIDSecret
import com.nimbusds.jose.jwk.OctetKeyPair
import io.iohk.atala.agent.walletapi.storage.DIDSecretStorage
import io.iohk.atala.mercury.model.DidId
import io.iohk.atala.shared.models.WalletAccessContext
import io.iohk.atala.shared.models.WalletId
import zio.*

class DIDSecretStorageInMemory(walletRefs: Ref[Map[WalletId, Ref[Map[String, DIDSecret]]]]) extends DIDSecretStorage {
class DIDSecretStorageInMemory(walletRefs: Ref[Map[WalletId, Ref[Map[String, OctetKeyPair]]]])
extends DIDSecretStorage {

private def walletStoreRef: URIO[WalletAccessContext, Ref[Map[String, DIDSecret]]] =
private def walletStoreRef: URIO[WalletAccessContext, Ref[Map[String, OctetKeyPair]]] =
for {
walletId <- ZIO.serviceWith[WalletAccessContext](_.walletId)
refs <- walletRefs.get
maybeWalletRef = refs.get(walletId)
walletRef <- maybeWalletRef
.fold {
for {
ref <- Ref.make(Map.empty[String, DIDSecret])
ref <- Ref.make(Map.empty[String, OctetKeyPair])
_ <- walletRefs.set(refs.updated(walletId, ref))
} yield ref
}(ZIO.succeed)
} yield walletRef

private def constructKey(did: DidId, keyId: String, schemaId: String): String = s"${did}_${keyId}_${schemaId}"
private def constructKey(did: DidId, keyId: String): String = s"${did}_${keyId}"

override def getKey(did: DidId, keyId: String, schemaId: String): RIO[WalletAccessContext, Option[DIDSecret]] =
override def getKey(did: DidId, keyId: String): RIO[WalletAccessContext, Option[OctetKeyPair]] =
walletStoreRef.flatMap { storeRef =>
storeRef.get.map(_.get(constructKey(did, keyId, schemaId)))
storeRef.get.map(_.get(constructKey(did, keyId)))
}

override def insertKey(did: DidId, keyId: String, didSecret: DIDSecret): RIO[WalletAccessContext, Int] =
override def insertKey(did: DidId, keyId: String, keyPair: OctetKeyPair): RIO[WalletAccessContext, Int] =
walletStoreRef.flatMap(storeRef =>
storeRef.modify { store =>
val key = constructKey(did, keyId, didSecret.schemaId)
val key = constructKey(did, keyId)
if (store.contains(key)) {
(
ZIO.fail(Exception(s"Unique constraint violation, key $key already exist.")),
store
) // Already exists, so we're effectively updating it
} else {
(ZIO.succeed(1), store.updated(key, didSecret))
(ZIO.succeed(1), store.updated(key, keyPair))
}
}.flatten
)
Expand All @@ -50,7 +51,7 @@ object DIDSecretStorageInMemory {
val layer: ULayer[DIDSecretStorage] =
ZLayer.fromZIO(
Ref
.make(Map.empty[WalletId, Ref[Map[String, DIDSecret]]])
.make(Map.empty[WalletId, Ref[Map[String, OctetKeyPair]]])
.map(DIDSecretStorageInMemory(_))
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.iohk.atala.agent.walletapi.memory

import io.iohk.atala.agent.walletapi.storage.GenericSecret
import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage
import io.iohk.atala.shared.models.WalletAccessContext
import io.iohk.atala.shared.models.WalletId
import zio.*
import zio.json.ast.Json

class GenericSecretStorageInMemory(walletRefs: Ref[Map[WalletId, Ref[Map[String, Json]]]])
extends GenericSecretStorage {

private def walletStoreRef: URIO[WalletAccessContext, Ref[Map[String, Json]]] =
for {
walletId <- ZIO.serviceWith[WalletAccessContext](_.walletId)
refs <- walletRefs.get
maybeWalletRef = refs.get(walletId)
walletRef <- maybeWalletRef
.fold {
for {
ref <- Ref.make(Map.empty[String, Json])
_ <- walletRefs.set(refs.updated(walletId, ref))
} yield ref
}(ZIO.succeed)
} yield walletRef

override def set[K, V](key: K, secret: V)(implicit ev: GenericSecret[K, V]): RIO[WalletAccessContext, Unit] = {
for {
storeRef <- walletStoreRef
_ <- storeRef.modify { store =>
val keyPath = ev.keyPath(key)
if (store.contains(keyPath))
(
ZIO.fail(Exception(s"Unique constaint violation, key $key already exists.")),
store
)
else (ZIO.unit, store.updated(keyPath, ev.encodeValue(secret)))
}.flatten
} yield ()
}

override def get[K, V](key: K)(implicit ev: GenericSecret[K, V]): RIO[WalletAccessContext, Option[V]] = {
val keyPath = ev.keyPath(key)
for {
storeRef <- walletStoreRef
json <- storeRef.get.map(_.get(keyPath))
result <- json.fold(ZIO.none)(json => ZIO.fromTry(ev.decodeValue(json)).asSome)
} yield result
}

}

object GenericSecretStorageInMemory {
val layer: ULayer[GenericSecretStorage] =
ZLayer.fromZIO(
Ref
.make(Map.empty[WalletId, Ref[Map[String, Json]]])
.map(GenericSecretStorageInMemory(_))
)
}
Loading

0 comments on commit 3d4aacd

Please sign in to comment.