Skip to content

Commit

Permalink
fix: Wallet Management Error Handling (#1248)
Browse files Browse the repository at this point in the history
Signed-off-by: Bassam Riman <[email protected]>
Signed-off-by: Shota Jolbordi <[email protected]>
  • Loading branch information
CryptoKnightIOG authored and Shota Jolbordi committed Jul 17, 2024
1 parent bbc9629 commit a80ffab
Show file tree
Hide file tree
Showing 23 changed files with 218 additions and 176 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import org.hyperledger.identus.iam.authentication.oidc.{
KeycloakConfig,
KeycloakEntity
}
import org.hyperledger.identus.iam.authorization.core.PermissionManagement
import org.hyperledger.identus.iam.authorization.core.PermissionManagementService
import org.hyperledger.identus.iam.authorization.keycloak.admin.KeycloakPermissionManagementService
import org.hyperledger.identus.pollux.vc.jwt.{DidResolver as JwtDidResolver, PrismDidResolver}
import org.hyperledger.identus.shared.crypto.Apollo
Expand Down Expand Up @@ -103,7 +103,7 @@ object AppModule {
)

val keycloakAuthenticatorLayer: RLayer[
AppConfig & WalletManagementService & Client & PermissionManagement.Service[KeycloakEntity],
AppConfig & WalletManagementService & Client & PermissionManagementService[KeycloakEntity],
KeycloakAuthenticator
] =
ZLayer.fromZIO {
Expand All @@ -113,7 +113,7 @@ object AppModule {
if (!isEnabled) KeycloakAuthenticatorImpl.disabled
else
ZLayer.makeSome[
AppConfig & WalletManagementService & Client & PermissionManagement.Service[KeycloakEntity],
AppConfig & WalletManagementService & Client & PermissionManagementService[KeycloakEntity],
KeycloakAuthenticator
](
KeycloakConfig.layer,
Expand All @@ -125,14 +125,14 @@ object AppModule {
}.flatten

val keycloakPermissionManagementLayer
: RLayer[AppConfig & WalletManagementService & Client, PermissionManagement.Service[KeycloakEntity]] = {
: RLayer[AppConfig & WalletManagementService & Client, PermissionManagementService[KeycloakEntity]] = {
ZLayer.fromZIO {
ZIO
.serviceWith[AppConfig](_.agent.authentication.keycloak.enabled)
.map { isEnabled =>
if (!isEnabled) KeycloakPermissionManagementService.disabled
else
ZLayer.makeSome[AppConfig & WalletManagementService & Client, PermissionManagement.Service[KeycloakEntity]](
ZLayer.makeSome[AppConfig & WalletManagementService & Client, PermissionManagementService[KeycloakEntity]](
KeycloakClientImpl.authzClientLayer,
KeycloakClientImpl.layer,
KeycloakConfig.layer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package org.hyperledger.identus.iam.authentication.oidc
import org.hyperledger.identus.agent.walletapi.model.EntityRole
import org.hyperledger.identus.iam.authentication.AuthenticationError
import org.hyperledger.identus.iam.authentication.AuthenticationError.AuthenticationMethodNotEnabled
import org.hyperledger.identus.iam.authorization.core.PermissionManagement
import org.hyperledger.identus.iam.authorization.core.PermissionManagement.Error.PermissionNotAvailable
import org.hyperledger.identus.iam.authorization.core.PermissionManagementService
import org.hyperledger.identus.iam.authorization.core.PermissionManagementServiceError.PermissionNotAvailable
import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletAdministrationContext}
import zio.*

Expand All @@ -13,7 +13,7 @@ import java.util.UUID
class KeycloakAuthenticatorImpl(
client: KeycloakClient,
keycloakConfig: KeycloakConfig,
keycloakPermissionService: PermissionManagement.Service[KeycloakEntity],
keycloakPermissionService: PermissionManagementService[KeycloakEntity],
) extends KeycloakAuthenticator {

override def isEnabled: Boolean = keycloakConfig.enabled
Expand Down Expand Up @@ -48,7 +48,7 @@ class KeycloakAuthenticatorImpl(
.listWalletPermissions(entity)
.mapError {
case PermissionNotAvailable(_, msg) => AuthenticationError.InvalidCredentials(msg)
case e => AuthenticationError.UnexpectedError(e.message)
case e => AuthenticationError.UnexpectedError(e.userFacingMessage)
}
.flatMap {
case head +: Nil => ZIO.succeed(head)
Expand All @@ -68,7 +68,7 @@ class KeycloakAuthenticatorImpl(
.listWalletPermissions(entity)
.provide(ZLayer.succeed(WalletAdministrationContext.Admin()))
.mapBoth(
e => AuthenticationError.UnexpectedError(e.message),
e => AuthenticationError.UnexpectedError(e.userFacingMessage),
wallets => WalletAdministrationContext.SelfService(wallets)
)

Expand All @@ -90,7 +90,7 @@ class KeycloakAuthenticatorImpl(

object KeycloakAuthenticatorImpl {
val layer: RLayer[
KeycloakClient & KeycloakConfig & PermissionManagement.Service[KeycloakEntity],
KeycloakClient & KeycloakConfig & PermissionManagementService[KeycloakEntity],
KeycloakAuthenticator
] =
ZLayer.fromFunction(KeycloakAuthenticatorImpl(_, _, _))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,38 @@ package org.hyperledger.identus.iam.authorization

import org.hyperledger.identus.agent.walletapi.model.{BaseEntity, Entity}
import org.hyperledger.identus.iam.authentication.oidc.KeycloakEntity
import org.hyperledger.identus.iam.authorization.core.PermissionManagement
import org.hyperledger.identus.iam.authorization.core.PermissionManagement.Error
import org.hyperledger.identus.iam.authorization.core.{PermissionManagementService, PermissionManagementServiceError}
import org.hyperledger.identus.shared.models.{WalletAdministrationContext, WalletId}
import zio.*

class DefaultPermissionManagementService(
entityPermission: PermissionManagement.Service[Entity],
keycloakPermission: PermissionManagement.Service[KeycloakEntity]
) extends PermissionManagement.Service[BaseEntity] {
entityPermission: PermissionManagementService[Entity],
keycloakPermission: PermissionManagementService[KeycloakEntity]
) extends PermissionManagementService[BaseEntity] {

def grantWalletToUser(walletId: WalletId, entity: BaseEntity): ZIO[WalletAdministrationContext, Error, Unit] = {
def grantWalletToUser(
walletId: WalletId,
entity: BaseEntity
): ZIO[WalletAdministrationContext, PermissionManagementServiceError, Unit] = {
entity match {
case entity: Entity => entityPermission.grantWalletToUser(walletId, entity)
case kcEntity: KeycloakEntity => keycloakPermission.grantWalletToUser(walletId, kcEntity)
}
}

def revokeWalletFromUser(walletId: WalletId, entity: BaseEntity): ZIO[WalletAdministrationContext, Error, Unit] = {
def revokeWalletFromUser(
walletId: WalletId,
entity: BaseEntity
): ZIO[WalletAdministrationContext, PermissionManagementServiceError, Unit] = {
entity match {
case entity: Entity => entityPermission.revokeWalletFromUser(walletId, entity)
case kcEntity: KeycloakEntity => keycloakPermission.revokeWalletFromUser(walletId, kcEntity)
}
}

def listWalletPermissions(entity: BaseEntity): ZIO[WalletAdministrationContext, Error, Seq[WalletId]] = {
def listWalletPermissions(
entity: BaseEntity
): ZIO[WalletAdministrationContext, PermissionManagementServiceError, Seq[WalletId]] = {
entity match {
case entity: Entity => entityPermission.listWalletPermissions(entity)
case kcEntity: KeycloakEntity => keycloakPermission.listWalletPermissions(kcEntity)
Expand All @@ -37,8 +44,8 @@ class DefaultPermissionManagementService(

object DefaultPermissionManagementService {
def layer: URLayer[
PermissionManagement.Service[KeycloakEntity] & PermissionManagement.Service[Entity],
PermissionManagement.Service[BaseEntity]
PermissionManagementService[KeycloakEntity] & PermissionManagementService[Entity],
PermissionManagementService[BaseEntity]
] =
ZLayer.fromFunction(DefaultPermissionManagementService(_, _))
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,45 @@ package org.hyperledger.identus.iam.authorization.core

import org.hyperledger.identus.agent.walletapi.model.Entity
import org.hyperledger.identus.agent.walletapi.service.EntityService
import org.hyperledger.identus.iam.authorization.core.PermissionManagement.Error
import org.hyperledger.identus.iam.authorization.core.PermissionManagement.Error.{ServiceError, WalletNotFoundById}
import org.hyperledger.identus.iam.authorization.core.PermissionManagementServiceError.*
import org.hyperledger.identus.shared.models.{WalletAdministrationContext, WalletId}
import zio.*

import scala.language.implicitConversions

class EntityPermissionManagementService(entityService: EntityService) extends PermissionManagement.Service[Entity] {
class EntityPermissionManagementService(entityService: EntityService) extends PermissionManagementService[Entity] {

override def grantWalletToUser(walletId: WalletId, entity: Entity): ZIO[WalletAdministrationContext, Error, Unit] = {
override def grantWalletToUser(
walletId: WalletId,
entity: Entity
): ZIO[WalletAdministrationContext, PermissionManagementServiceError, Unit] = {
for {
_ <- ZIO
.serviceWith[WalletAdministrationContext](_.isAuthorized(walletId))
.filterOrFail(identity)(Error.WalletNotFoundById(walletId))
.filterOrFail(identity)(WalletNotFoundById(walletId))
_ <- entityService.assignWallet(entity.id, walletId.toUUID).orDieAsUnmanagedFailure
} yield ()
}

override def revokeWalletFromUser(walletId: WalletId, entity: Entity): ZIO[WalletAdministrationContext, Error, Unit] =
ZIO.fail(Error.ServiceError(s"Revoking wallet permission for an Entity is not yet supported."))
override def revokeWalletFromUser(
walletId: WalletId,
entity: Entity
): ZIO[WalletAdministrationContext, PermissionManagementServiceError, Unit] =
ZIO.fail(ServiceError(s"Revoking wallet permission for an Entity is not yet supported."))

override def listWalletPermissions(entity: Entity): ZIO[WalletAdministrationContext, Error, Seq[WalletId]] = {
override def listWalletPermissions(
entity: Entity
): ZIO[WalletAdministrationContext, PermissionManagementServiceError, Seq[WalletId]] = {
val walletId = WalletId.fromUUID(entity.walletId)
ZIO
.serviceWith[WalletAdministrationContext](_.isAuthorized(walletId))
.filterOrFail(identity)(Error.WalletNotFoundById(walletId))
.filterOrFail(identity)(WalletNotFoundById(walletId))
.as(Seq(walletId))
}

}

object EntityPermissionManagementService {
val layer: URLayer[EntityService, PermissionManagement.Service[Entity]] =
val layer: URLayer[EntityService, PermissionManagementService[Entity]] =
ZLayer.fromFunction(EntityPermissionManagementService(_))
}
Original file line number Diff line number Diff line change
@@ -1,39 +1 @@
package org.hyperledger.identus.iam.authorization.core

import org.hyperledger.identus.agent.walletapi.model.BaseEntity
import org.hyperledger.identus.shared.models.{WalletAdministrationContext, WalletId}
import zio.*

import java.util.UUID

object PermissionManagement {
trait Service[E <: BaseEntity] {
def grantWalletToUser(walletId: WalletId, entity: E): ZIO[WalletAdministrationContext, Error, Unit]
def revokeWalletFromUser(walletId: WalletId, entity: E): ZIO[WalletAdministrationContext, Error, Unit]
def listWalletPermissions(entity: E): ZIO[WalletAdministrationContext, Error, Seq[WalletId]]
}

sealed trait Error(val message: String)

object Error {
case class UserNotFoundById(userId: UUID, cause: Option[Throwable] = None)
extends Error(s"User $userId is not found" + cause.map(t => s" Cause: ${t.getMessage}"))
case class WalletNotFoundByUserId(userId: UUID) extends Error(s"Wallet for user $userId is not found")

case class WalletNotFoundById(walletId: WalletId) extends Error(s"Wallet not found by ${walletId.toUUID}")

case class WalletResourceNotFoundById(walletId: WalletId)
extends Error(s"Wallet resource not found by ${walletId.toUUID}")

case class PermissionNotFoundById(userId: UUID, walletId: WalletId, walletResourceId: String)
extends Error(
s"Permission not found by userId: $userId, walletId: ${walletId.toUUID}, walletResourceId: $walletResourceId"
)

case class PermissionNotAvailable(userId: UUID, cause: String) extends Error(cause)

case class UnexpectedError(cause: Throwable) extends Error(cause.getMessage)

case class ServiceError(cause: String) extends Error(cause)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.hyperledger.identus.iam.authorization.core

import org.hyperledger.identus.agent.walletapi.model.BaseEntity
import org.hyperledger.identus.shared.models.{WalletAdministrationContext, WalletId}
import zio.*

trait PermissionManagementService[E <: BaseEntity] {
def grantWalletToUser(
walletId: WalletId,
entity: E
): ZIO[WalletAdministrationContext, PermissionManagementServiceError, Unit]
def revokeWalletFromUser(
walletId: WalletId,
entity: E
): ZIO[WalletAdministrationContext, PermissionManagementServiceError, Unit]
def listWalletPermissions(
entity: E
): ZIO[WalletAdministrationContext, PermissionManagementServiceError, Seq[WalletId]]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.hyperledger.identus.iam.authorization.core

import org.hyperledger.identus.shared.models.{Failure, StatusCode, WalletId}

import java.util.UUID

sealed trait PermissionManagementServiceError(
val statusCode: StatusCode,
val userFacingMessage: String
) extends Failure {
override val namespace: String = "PermissionManagementServiceError"
}

object PermissionManagementServiceError {

case class UserNotFoundById(userId: UUID, cause: Option[Throwable] = None)
extends PermissionManagementServiceError(
StatusCode.BadRequest,
s"User $userId is not found" + cause.map(t => s" Cause: ${t.getMessage}")
)

case class WalletNotFoundByUserId(userId: UUID)
extends PermissionManagementServiceError(
StatusCode.BadRequest,
s"Wallet for user $userId is not found"
)

case class WalletNotFoundById(walletId: WalletId)
extends PermissionManagementServiceError(
StatusCode.BadRequest,
s"Wallet not found by ${walletId.toUUID}"
)

case class WalletResourceNotFoundById(walletId: WalletId)
extends PermissionManagementServiceError(
StatusCode.BadRequest,
s"Wallet resource not found by ${walletId.toUUID}"
)

case class PermissionNotFoundById(userId: UUID, walletId: WalletId, walletResourceId: String)
extends PermissionManagementServiceError(
StatusCode.BadRequest,
s"Permission not found by userId: $userId, walletId: ${walletId.toUUID}, walletResourceId: $walletResourceId"
)

case class PermissionNotAvailable(userId: UUID, cause: String)
extends PermissionManagementServiceError(StatusCode.BadRequest, cause)

case class ServiceError(cause: String) extends PermissionManagementServiceError(StatusCode.InternalServerError, cause)
}
Loading

0 comments on commit a80ffab

Please sign in to comment.