Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(prism-agent): introduce generic secret store for CD #727

Merged
merged 2 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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