From f6bc6ad14e0fc46b5ff9ee8ae06cce6d94326dcf Mon Sep 17 00:00:00 2001 From: Pat Losoponkul Date: Mon, 13 Nov 2023 20:44:46 +0700 Subject: [PATCH 1/4] test: add entity permission tests Signed-off-by: Pat Losoponkul --- .../core/EntityPermissionManagementSpec.scala | 134 ++++++++++++++++++ .../service/WalletManagementServiceSpec.scala | 11 ++ 2 files changed, 145 insertions(+) create mode 100644 prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala new file mode 100644 index 0000000000..5908b526fd --- /dev/null +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala @@ -0,0 +1,134 @@ +package io.iohk.atala.iam.authorization.core + +import io.iohk.atala.agent.walletapi.crypto.ApolloSpecHelper +import io.iohk.atala.agent.walletapi.model.Entity +import io.iohk.atala.agent.walletapi.model.Wallet +import io.iohk.atala.agent.walletapi.service.EntityService +import io.iohk.atala.agent.walletapi.service.EntityServiceImpl +import io.iohk.atala.agent.walletapi.service.WalletManagementService +import io.iohk.atala.agent.walletapi.service.WalletManagementServiceImpl +import io.iohk.atala.agent.walletapi.sql.JdbcEntityRepository +import io.iohk.atala.agent.walletapi.sql.JdbcWalletNonSecretStorage +import io.iohk.atala.agent.walletapi.sql.JdbcWalletSecretStorage +import io.iohk.atala.iam.authorization.core.PermissionManagement.Error.ServiceError +import io.iohk.atala.iam.authorization.core.PermissionManagement.Error.WalletNotFoundById +import io.iohk.atala.shared.models.WalletAdministrationContext +import io.iohk.atala.shared.models.WalletId +import io.iohk.atala.sharedtest.containers.PostgresTestContainerSupport +import io.iohk.atala.test.container.DBTestUtils +import zio.* +import zio.test.* +import zio.test.Assertion.* + +object EntityPermissionManagementSpec extends ZIOSpecDefault, PostgresTestContainerSupport, ApolloSpecHelper { + + override def spec = { + val s = suite("EntityPermissionManagementSpec")( + successfulCasesSuite, + failureCasesSuite, + multitenantSuite + ) @@ TestAspect.before(DBTestUtils.runMigrationAgentDB) + + s.provide( + EntityPermissionManagementService.layer, + EntityServiceImpl.layer, + WalletManagementServiceImpl.layer, + JdbcEntityRepository.layer, + JdbcWalletNonSecretStorage.layer, + JdbcWalletSecretStorage.layer, + contextAwareTransactorLayer, + systemTransactorLayer, + pgContainerLayer, + apolloLayer + ) + }.provide(Runtime.removeDefaultLoggers) + + private val successfulCasesSuite = suite("Successful cases")( + test("grant wallet access to the user") { + for { + entityService <- ZIO.service[EntityService] + permissionService <- ZIO.service[PermissionManagement.Service[Entity]] + walletService <- ZIO.service[WalletManagementService] + wallet1 <- walletService + .createWallet(Wallet("test")) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + wallet2 <- walletService + .createWallet(Wallet("test2")) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + entity <- entityService + .create(Entity("alice", wallet1.id.toUUID)) + _ <- permissionService + .grantWalletToUser(wallet2.id, entity) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + entity <- entityService.getById(entity.id) + permissions <- permissionService + .listWalletPermissions(entity) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + } yield assert(permissions.head)(equalTo(wallet2.id)) + }, + ) + + private val failureCasesSuite = suite("Failure Cases")( + test("revoke wallet is not support") { + for { + entityService <- ZIO.service[EntityService] + permissionService <- ZIO.service[PermissionManagement.Service[Entity]] + walletService <- ZIO.service[WalletManagementService] + wallet1 <- walletService + .createWallet(Wallet("test")) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + entity <- entityService + .create(Entity("alice", wallet1.id.toUUID)) + exit <- permissionService + .revokeWalletFromUser(wallet1.id, entity) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + .debug("revokeWallet") + .exit + } yield assert(exit)(fails(isSubtype[ServiceError](anything))) + } + ) + + private val multitenantSuite = suite("multi-tenant cases")( + test("grant wallet acces to the user by self-service") { + val walletId1 = WalletId.random + val walletId2 = WalletId.random + for { + entityService <- ZIO.service[EntityService] + permissionService <- ZIO.service[PermissionManagement.Service[Entity]] + walletService <- ZIO.service[WalletManagementService] + wallet1 <- walletService + .createWallet(Wallet("test", walletId1)) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + wallet2 <- walletService + .createWallet(Wallet("test2", walletId2)) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + entity <- entityService.create(Entity("alice", wallet1.id.toUUID)) + _ <- permissionService + .grantWalletToUser(wallet2.id, entity) + .provide(ZLayer.succeed(WalletAdministrationContext.SelfService(Seq(walletId2)))) + entity <- entityService.getById(entity.id) + permissions <- permissionService + .listWalletPermissions(entity) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + } yield assert(permissions.head)(equalTo(wallet2.id)) + }, + test("grant wallet access to non-permitted wallet by self-service is not allowed") { + val walletId1 = WalletId.random + val walletId2 = WalletId.random + for { + entityService <- ZIO.service[EntityService] + permissionService <- ZIO.service[PermissionManagement.Service[Entity]] + walletService <- ZIO.service[WalletManagementService] + wallet1 <- walletService + .createWallet(Wallet("test", walletId1)) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + entity <- entityService.create(Entity("alice", wallet1.id.toUUID)) + exit <- permissionService + .grantWalletToUser(walletId2, entity) + .provide(ZLayer.succeed(WalletAdministrationContext.SelfService(Seq(walletId1)))) + .exit + } yield assert(exit)(fails(isSubtype[WalletNotFoundById](anything))) + }, + ) + +} diff --git a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/WalletManagementServiceSpec.scala b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/WalletManagementServiceSpec.scala index 0d1bda9ebc..6814e2c249 100644 --- a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/WalletManagementServiceSpec.scala +++ b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/WalletManagementServiceSpec.scala @@ -4,6 +4,7 @@ import io.iohk.atala.agent.walletapi.crypto.ApolloSpecHelper import io.iohk.atala.agent.walletapi.model.Wallet import io.iohk.atala.agent.walletapi.model.WalletSeed import io.iohk.atala.agent.walletapi.service.WalletManagementServiceError.DuplicatedWalletSeed +import io.iohk.atala.agent.walletapi.service.WalletManagementServiceError.TooManyPermittedWallet import io.iohk.atala.agent.walletapi.sql.JdbcWalletNonSecretStorage import io.iohk.atala.agent.walletapi.sql.JdbcWalletSecretStorage import io.iohk.atala.agent.walletapi.storage.WalletSecretStorage @@ -133,6 +134,16 @@ object WalletManagementServiceSpec _ <- svc.createWallet(Wallet("wallet-1"), Some(seed)) exit <- svc.createWallet(Wallet("wallet-2"), Some(seed)).exit } yield assert(exit)(fails(isSubtype[DuplicatedWalletSeed](anything))) + }, + test("cannot create new wallet for self-service context if already have permitted wallet") { + val walletId = WalletId.random + for { + svc <- ZIO.service[WalletManagementService] + exit <- svc + .createWallet(Wallet("wallet-1")) + .provide(ZLayer.succeed(WalletAdministrationContext.SelfService(Seq(walletId)))) + .exit + } yield assert(exit)(fails(isSubtype[TooManyPermittedWallet](anything))) } ) From 051e22c56c78835796b0b1637e1cb2975256f944 Mon Sep 17 00:00:00 2001 From: Pat Losoponkul Date: Mon, 13 Nov 2023 21:01:35 +0700 Subject: [PATCH 2/4] test: refactor stub Signed-off-by: Pat Losoponkul --- ...cloakPermissionManagementServiceSpec.scala | 109 +++++++----------- 1 file changed, 39 insertions(+), 70 deletions(-) diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala index beb91ce81e..3308da7313 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala @@ -24,19 +24,46 @@ import zio.test.Assertion.* import zio.test.TestAspect.* import java.util.UUID +import io.iohk.atala.sharedtest.containers.PostgresTestContainerSupport +import io.iohk.atala.agent.walletapi.crypto.ApolloSpecHelper +import io.iohk.atala.agent.walletapi.service.WalletManagementServiceImpl +import io.iohk.atala.agent.walletapi.sql.JdbcEntityRepository +import io.iohk.atala.agent.walletapi.sql.JdbcWalletNonSecretStorage +import io.iohk.atala.agent.walletapi.sql.JdbcWalletSecretStorage +import io.iohk.atala.test.container.DBTestUtils object KeycloakPermissionManagementServiceSpec extends ZIOSpecDefault with KeycloakTestContainerSupport - with KeycloakConfigUtils { - - override def spec = suite("KeycloakPermissionManagementServiceSpec")( - successfulCasesSuite, - failureCasesSuite - ) - .provide(Runtime.removeDefaultLoggers) + with KeycloakConfigUtils + with PostgresTestContainerSupport + with ApolloSpecHelper { + + override def spec = { + val s = suite("KeycloakPermissionManagementServiceSpec")( + successfulCasesSuite, + failureCasesSuite + ) @@ sequential @@ TestAspect.before(DBTestUtils.runMigrationAgentDB) + + s.provide( + Client.default, + keycloakContainerLayer, + keycloakAdminConfigLayer, + KeycloakAdmin.layer, + KeycloakPermissionManagementService.layer, + KeycloakAuthenticatorImpl.layer, + ZLayer.fromZIO(initializeClient) >>> KeycloakClientImpl.authzClientLayer >+> KeycloakClientImpl.layer, + keycloakConfigLayer(), + WalletManagementServiceImpl.layer, + JdbcWalletNonSecretStorage.layer, + JdbcWalletSecretStorage.layer, + contextAwareTransactorLayer, + pgContainerLayer, + apolloLayer + ).provide(Runtime.removeDefaultLoggers) + } - val successfulCasesSuite = suite("Successful Cases")( + private val successfulCasesSuite = suite("Successful Cases")( test("grant wallet access to the user") { for { client <- ZIO.service[KeycloakClient] @@ -90,20 +117,9 @@ object KeycloakPermissionManagementServiceSpec } yield assert(permittedWallet2)(fails(isSubtype[ResourceNotPermitted](anything))) } - ).provide( - Client.default, - keycloakContainerLayer, - keycloakAdminConfigLayer, - KeycloakAdmin.layer, - KeycloakPermissionManagementService.layer, - WalletManagementServiceStub.layer, - KeycloakAuthenticatorImpl.layer, - ZLayer.fromZIO(initializeClient) >>> KeycloakClientImpl.authzClientLayer >+> KeycloakClientImpl.layer, - keycloakConfigLayer(), - ZLayer.succeed(WalletAdministrationContext.Admin()) - ) @@ sequential - - val failureCasesSuite = suite("Failure Cases Suite")( + ).provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.Admin())) + + private val failureCasesSuite = suite("Failure Cases Suite")( test("grant wallet access to the user with invalid wallet id") { for { permissionService <- ZIO.service[PermissionManagement.Service[KeycloakEntity]] @@ -111,52 +127,5 @@ object KeycloakPermissionManagementServiceSpec exit <- permissionService.grantWalletToUser(WalletId.random, entity).exit } yield assert(exit)(fails(isSubtype[WalletNotFoundById](anything))) } - ).provide( - Client.default, - keycloakContainerLayer, - keycloakAdminConfigLayer, - KeycloakAdmin.layer, - KeycloakPermissionManagementService.layer, - WalletManagementServiceStub.layer, - ZLayer.fromZIO(initializeClient) >>> KeycloakClientImpl.authzClientLayer >+> KeycloakClientImpl.layer, - keycloakConfigLayer(), - ZLayer.succeed(WalletAdministrationContext.Admin()) - ) @@ sequential -} - -class WalletManagementServiceStub extends WalletManagementService { - private var wallets: Map[WalletId, Wallet] = Map.empty - override def createWallet(wallet: Wallet, seed: Option[WalletSeed]): IO[WalletManagementServiceError, Wallet] = { - val wallet = Wallet(name = "test") - wallets = wallets + (wallet.id -> wallet) - ZIO.succeed(wallet) - } - - override def getWallet(walletId: WalletId): IO[WalletManagementServiceError, Option[Wallet]] = { - ZIO.succeed(wallets.get(walletId)) - } - - override def getWallets(walletIds: Seq[WalletId]): IO[WalletManagementServiceError, Seq[Wallet]] = { - ZIO.succeed(wallets.filter(w => walletIds.contains(w._1)).values.toSeq) - } - - override def listWallets( - offset: Option[RuntimeFlags], - limit: Option[RuntimeFlags] - ): IO[WalletManagementServiceError, (Seq[Wallet], RuntimeFlags)] = ??? - - override def listWalletNotifications - : ZIO[WalletAccessContext, WalletManagementServiceError, Seq[EventNotificationConfig]] = ??? - - override def createWalletNotification( - config: EventNotificationConfig - ): ZIO[WalletAccessContext, WalletManagementServiceError, EventNotificationConfig] = ??? - - override def deleteWalletNotification( - id: _root_.java.util.UUID - ): ZIO[WalletAccessContext, WalletManagementServiceError, Unit] = ??? -} - -object WalletManagementServiceStub { - val layer = ZLayer.succeed(new WalletManagementServiceStub) + ).provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.Admin())) } From 4b195a427018ad9e84794f909308ebb9efb0d51b Mon Sep 17 00:00:00 2001 From: Pat Losoponkul Date: Tue, 14 Nov 2023 14:24:11 +0700 Subject: [PATCH 3/4] test: add test for multi-tenant keycloak-permission-service Signed-off-by: Pat Losoponkul --- .../core/EntityPermissionManagementSpec.scala | 2 +- ...cloakPermissionManagementServiceSpec.scala | 74 ++++++++++++++++--- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala index 5908b526fd..aae3e29b5f 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala @@ -89,7 +89,7 @@ object EntityPermissionManagementSpec extends ZIOSpecDefault, PostgresTestContai ) private val multitenantSuite = suite("multi-tenant cases")( - test("grant wallet acces to the user by self-service") { + test("grant wallet access to the user by self-service") { val walletId1 = WalletId.random val walletId2 = WalletId.random for { diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala index 3308da7313..f851699759 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala @@ -1,8 +1,11 @@ package io.iohk.atala.iam.authorization.keycloak.admin -import io.iohk.atala.agent.walletapi.model.{Wallet, WalletSeed} +import io.iohk.atala.agent.walletapi.crypto.ApolloSpecHelper +import io.iohk.atala.agent.walletapi.model.Wallet +import io.iohk.atala.agent.walletapi.service.WalletManagementServiceImpl import io.iohk.atala.agent.walletapi.service.{WalletManagementService, WalletManagementServiceError} -import io.iohk.atala.event.notification.EventNotificationConfig +import io.iohk.atala.agent.walletapi.sql.JdbcWalletNonSecretStorage +import io.iohk.atala.agent.walletapi.sql.JdbcWalletSecretStorage import io.iohk.atala.iam.authentication.AuthenticationError.ResourceNotPermitted import io.iohk.atala.iam.authentication.oidc.{ KeycloakAuthenticator, @@ -15,7 +18,9 @@ import io.iohk.atala.iam.authorization.core.PermissionManagement import io.iohk.atala.iam.authorization.core.PermissionManagement.Error.WalletNotFoundById import io.iohk.atala.shared.models.WalletAdministrationContext import io.iohk.atala.shared.models.{WalletAccessContext, WalletId} +import io.iohk.atala.sharedtest.containers.PostgresTestContainerSupport import io.iohk.atala.sharedtest.containers.{KeycloakContainerCustom, KeycloakTestContainerSupport} +import io.iohk.atala.test.container.DBTestUtils import zio.* import zio.ZIO.* import zio.http.Client @@ -24,13 +29,6 @@ import zio.test.Assertion.* import zio.test.TestAspect.* import java.util.UUID -import io.iohk.atala.sharedtest.containers.PostgresTestContainerSupport -import io.iohk.atala.agent.walletapi.crypto.ApolloSpecHelper -import io.iohk.atala.agent.walletapi.service.WalletManagementServiceImpl -import io.iohk.atala.agent.walletapi.sql.JdbcEntityRepository -import io.iohk.atala.agent.walletapi.sql.JdbcWalletNonSecretStorage -import io.iohk.atala.agent.walletapi.sql.JdbcWalletSecretStorage -import io.iohk.atala.test.container.DBTestUtils object KeycloakPermissionManagementServiceSpec extends ZIOSpecDefault @@ -42,7 +40,8 @@ object KeycloakPermissionManagementServiceSpec override def spec = { val s = suite("KeycloakPermissionManagementServiceSpec")( successfulCasesSuite, - failureCasesSuite + failureCasesSuite, + multitenantSuite ) @@ sequential @@ TestAspect.before(DBTestUtils.runMigrationAgentDB) s.provide( @@ -128,4 +127,59 @@ object KeycloakPermissionManagementServiceSpec } yield assert(exit)(fails(isSubtype[WalletNotFoundById](anything))) } ).provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.Admin())) + + private val multitenantSuite = suite("multi-tenant cases")( + test("grant wallet access to the user by self-service") { + val walletId = WalletId.random + for { + client <- ZIO.service[KeycloakClient] + authenticator <- ZIO.service[KeycloakAuthenticator] + walletService <- ZIO.service[WalletManagementService] + + wallet <- walletService + .createWallet(Wallet("test_1", walletId)) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + + randomId = UUID.randomUUID().toString + username = "user_" + randomId + password = randomId + user <- createUser(username = username, password = password) + entity = KeycloakEntity(id = UUID.fromString(user.getId)) + + permissionService <- ZIO.service[PermissionManagement.Service[KeycloakEntity]] + _ <- permissionService + .grantWalletToUser(wallet.id, entity) + .provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.SelfService(Seq(walletId)))) + + token <- client.getAccessToken(username, password).map(_.access_token) + + entity <- authenticator.authenticate(token) + permittedWallet <- authenticator.authorize(entity) + } yield assert(wallet.id)(equalTo(permittedWallet.walletId)) + }, + test("grant wallet access to non-permitted wallet by self-service is not allowed") { + val walletId = WalletId.random + for { + client <- ZIO.service[KeycloakClient] + authenticator <- ZIO.service[KeycloakAuthenticator] + walletService <- ZIO.service[WalletManagementService] + + wallet <- walletService + .createWallet(Wallet("test_1", walletId)) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + + randomId = UUID.randomUUID().toString + username = "user_" + randomId + password = randomId + user <- createUser(username = username, password = password) + entity = KeycloakEntity(id = UUID.fromString(user.getId)) + + permissionService <- ZIO.service[PermissionManagement.Service[KeycloakEntity]] + exit <- permissionService + .grantWalletToUser(WalletId.random, entity) + .provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.SelfService(Seq(walletId)))) + .exit + } yield assert(exit)(fails(isSubtype[WalletNotFoundById](anything))) + } + ) } From 4c3975392385ebe89fcb4a80cdd4e842614d03b0 Mon Sep 17 00:00:00 2001 From: Pat Losoponkul Date: Tue, 14 Nov 2023 17:17:25 +0700 Subject: [PATCH 4/4] test: add multi-tenant wallet admin test cases Signed-off-by: Pat Losoponkul --- .../core/EntityPermissionManagementSpec.scala | 1 - ...cloakPermissionManagementServiceSpec.scala | 80 +++++++++++++++++++ .../service/WalletManagementServiceSpec.scala | 58 ++++++++++++-- 3 files changed, 133 insertions(+), 6 deletions(-) diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala index aae3e29b5f..90c4658b6d 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/core/EntityPermissionManagementSpec.scala @@ -82,7 +82,6 @@ object EntityPermissionManagementSpec extends ZIOSpecDefault, PostgresTestContai exit <- permissionService .revokeWalletFromUser(wallet1.id, entity) .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) - .debug("revokeWallet") .exit } yield assert(exit)(fails(isSubtype[ServiceError](anything))) } diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala index f851699759..8b71fda85a 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/iam/authorization/keycloak/admin/KeycloakPermissionManagementServiceSpec.scala @@ -16,6 +16,7 @@ import io.iohk.atala.iam.authentication.oidc.{ } import io.iohk.atala.iam.authorization.core.PermissionManagement import io.iohk.atala.iam.authorization.core.PermissionManagement.Error.WalletNotFoundById +import io.iohk.atala.iam.authorization.core.PermissionManagement.Error.UnexpectedError import io.iohk.atala.shared.models.WalletAdministrationContext import io.iohk.atala.shared.models.{WalletAccessContext, WalletId} import io.iohk.atala.sharedtest.containers.PostgresTestContainerSupport @@ -125,6 +126,17 @@ object KeycloakPermissionManagementServiceSpec entity = KeycloakEntity(id = UUID.randomUUID()) exit <- permissionService.grantWalletToUser(WalletId.random, entity).exit } yield assert(exit)(fails(isSubtype[WalletNotFoundById](anything))) + }, + test("grant wallet access to the user with invalid user id") { + for { + client <- ZIO.service[KeycloakClient] + authenticator <- ZIO.service[KeycloakAuthenticator] + walletService <- ZIO.service[WalletManagementService] + wallet <- walletService.createWallet(Wallet("test_1")) + entity = KeycloakEntity(id = UUID.randomUUID()) + permissionService <- ZIO.service[PermissionManagement.Service[KeycloakEntity]] + exit <- permissionService.grantWalletToUser(wallet.id, entity).exit + } yield assert(exit)(fails(isSubtype[UnexpectedError](anything))) } ).provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.Admin())) @@ -157,6 +169,42 @@ object KeycloakPermissionManagementServiceSpec permittedWallet <- authenticator.authorize(entity) } yield assert(wallet.id)(equalTo(permittedWallet.walletId)) }, + test("revoke wallet access from the user by self-service") { + for { + client <- ZIO.service[KeycloakClient] + authenticator <- ZIO.service[KeycloakAuthenticator] + walletService <- ZIO.service[WalletManagementService] + + wallet <- walletService + .createWallet(Wallet("test_2")) + .provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.Admin())) + + randomId = UUID.randomUUID().toString + username = "user_" + randomId + password = randomId + user <- createUser(username = username, password = password) + entity = KeycloakEntity(id = UUID.fromString(user.getId)) + + permissionService <- ZIO.service[PermissionManagement.Service[KeycloakEntity]] + _ <- permissionService + .grantWalletToUser(wallet.id, entity) + .provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.Admin())) + + token <- client.getAccessToken(username, password).map(_.access_token) + + entity <- authenticator.authenticate(token) + permittedWallet <- authenticator.authorize(entity) + + _ <- permissionService + .revokeWalletFromUser(wallet.id, entity) + .provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.SelfService(Seq(wallet.id)))) + + token2 <- client.getAccessToken(username, password).map(_.access_token) + entity2 <- authenticator.authenticate(token) + permittedWallet2 <- authenticator.authorize(entity).exit + + } yield assert(permittedWallet2)(fails(isSubtype[ResourceNotPermitted](anything))) + }, test("grant wallet access to non-permitted wallet by self-service is not allowed") { val walletId = WalletId.random for { @@ -180,6 +228,38 @@ object KeycloakPermissionManagementServiceSpec .provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.SelfService(Seq(walletId)))) .exit } yield assert(exit)(fails(isSubtype[WalletNotFoundById](anything))) + }, + test("revoke wallet access from non-permitted wallet by self-service is not allowed") { + for { + client <- ZIO.service[KeycloakClient] + authenticator <- ZIO.service[KeycloakAuthenticator] + walletService <- ZIO.service[WalletManagementService] + + wallet <- walletService + .createWallet(Wallet("test_2")) + .provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.Admin())) + + randomId = UUID.randomUUID().toString + username = "user_" + randomId + password = randomId + user <- createUser(username = username, password = password) + entity = KeycloakEntity(id = UUID.fromString(user.getId)) + + permissionService <- ZIO.service[PermissionManagement.Service[KeycloakEntity]] + _ <- permissionService + .grantWalletToUser(wallet.id, entity) + .provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.Admin())) + + token <- client.getAccessToken(username, password).map(_.access_token) + + entity <- authenticator.authenticate(token) + permittedWallet <- authenticator.authorize(entity) + + exit <- permissionService + .revokeWalletFromUser(wallet.id, entity) + .provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.SelfService(Seq(WalletId.random)))) + .exit + } yield assert(exit)(fails(isSubtype[WalletNotFoundById](anything))) } ) } diff --git a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/WalletManagementServiceSpec.scala b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/WalletManagementServiceSpec.scala index 6814e2c249..c7c08e3c06 100644 --- a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/WalletManagementServiceSpec.scala +++ b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/WalletManagementServiceSpec.scala @@ -28,8 +28,9 @@ object WalletManagementServiceSpec override def spec = { def testSuite(name: String) = suite(name)( - createWalletSpec, - getWalletSpec, + createWalletSpec.provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.Admin())), + getWalletSpec.provideSomeLayer(ZLayer.succeed(WalletAdministrationContext.Admin())), + multitenantSpec ) @@ TestAspect.before(DBTestUtils.runMigrationAgentDB) @@ TestAspect.sequential val suite1 = testSuite("jdbc as secret storage") @@ -40,7 +41,6 @@ object WalletManagementServiceSpec contextAwareTransactorLayer, pgContainerLayer, apolloLayer, - ZLayer.succeed(WalletAdministrationContext.Admin()) ) val suite2 = testSuite("vault as secret storage") @@ -52,7 +52,6 @@ object WalletManagementServiceSpec pgContainerLayer, apolloLayer, vaultKvClientLayer, - ZLayer.succeed(WalletAdministrationContext.Admin()) ) suite("WalletManagementService")(suite1, suite2) @@ -135,7 +134,7 @@ object WalletManagementServiceSpec exit <- svc.createWallet(Wallet("wallet-2"), Some(seed)).exit } yield assert(exit)(fails(isSubtype[DuplicatedWalletSeed](anything))) }, - test("cannot create new wallet for self-service context if already have permitted wallet") { + test("cannot create new wallet for self-service if already have permitted wallet") { val walletId = WalletId.random for { svc <- ZIO.service[WalletManagementService] @@ -147,4 +146,53 @@ object WalletManagementServiceSpec } ) + private def multitenantSpec = suite("multitenant spec")( + test("get all wallets for admin") { + for { + svc <- ZIO.service[WalletManagementService] + wallet1 <- svc.createWallet(Wallet("wallet-1")).provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + wallet2 <- svc.createWallet(Wallet("wallet-2")).provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + wallet3 <- svc.createWallet(Wallet("wallet-3")).provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + walletIds = Seq(wallet1, wallet2, wallet3).map(_.id) + wallets1 <- svc.getWallets(walletIds).provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + wallets2 <- svc + .listWallets() + .map(_._1) + .provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + } yield assert(wallets1.map(_.id))(equalTo(walletIds)) && + assert(wallets2.map(_.id))(equalTo(walletIds)) + }, + test("get only permitted wallet for self-service") { + for { + svc <- ZIO.service[WalletManagementService] + wallet1 <- svc.createWallet(Wallet("wallet-1")).provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + wallet2 <- svc.createWallet(Wallet("wallet-2")).provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + wallet3 <- svc.createWallet(Wallet("wallet-3")).provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + walletIds = Seq(wallet1, wallet2, wallet3).map(_.id) + permittedWalletIds = Seq(wallet1, wallet2).map(_.id) + wallets1 <- svc + .getWallets(walletIds) + .provide(ZLayer.succeed(WalletAdministrationContext.SelfService(permittedWalletIds))) + wallets2 <- svc + .listWallets() + .map(_._1) + .provide(ZLayer.succeed(WalletAdministrationContext.SelfService(permittedWalletIds))) + } yield assert(wallets1.map(_.id))(equalTo(permittedWalletIds)) && + assert(wallets2.map(_.id))(equalTo(permittedWalletIds)) + }, + test("cannot get wallet by self-service that is not permitted") { + for { + svc <- ZIO.service[WalletManagementService] + wallet1 <- svc.createWallet(Wallet("wallet-1")).provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + wallet2 <- svc.createWallet(Wallet("wallet-2")).provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + wallet3 <- svc.createWallet(Wallet("wallet-3")).provide(ZLayer.succeed(WalletAdministrationContext.Admin())) + walletIds = Seq(wallet1, wallet2, wallet3).map(_.id) + permittedWalletIds = Seq(wallet1, wallet2).map(_.id) + maybeWallet3 <- svc + .getWallet(wallet3.id) + .provide(ZLayer.succeed(WalletAdministrationContext.SelfService(permittedWalletIds))) + } yield assert(maybeWallet3)(isNone) + } + ) + }