Skip to content

Commit

Permalink
feat: list credential issuers
Browse files Browse the repository at this point in the history
  • Loading branch information
patlo-iog committed May 7, 2024
1 parent 9eeb3f0 commit b0e9f59
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ object CredentialIssuerEndpoints {
.name("createCredentialIssuer")
.summary("Create a new credential issuer")

val getCredentialIssuersEndpoint: Endpoint[
(ApiKeyCredentials, JwtCredentials),
RequestContext,
ErrorResponse,
CredentialIssuerPage,
Any
] = baseIssuerPrivateEndpoint.get
.errorOut(EndpointOutputs.basicFailuresAndForbidden)
.out(statusCode(StatusCode.Ok).description("List the credential issuers"))
.out(jsonBody[CredentialIssuerPage])
.name("getCredentialIssuers")
.summary("List all credential issuers")

val createCredentialConfigurationEndpoint: Endpoint[
(ApiKeyCredentials, JwtCredentials),
(RequestContext, UUID, CreateCredentialConfigurationRequest),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ case class CredentialIssuerServerEndpoints(
}
}

val getCredentialIssuersServerEndpoint: ZServerEndpoint[Any, Any] =
CredentialIssuerEndpoints.getCredentialIssuersEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
.serverLogic { wac =>
{ case rc =>
credentialIssuerController
.getCredentialIssuers(rc)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
}

val createCredentialConfigurationServerEndpoint: ZServerEndpoint[Any, Any] =
CredentialIssuerEndpoints.createCredentialConfigurationEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
Expand All @@ -91,6 +103,7 @@ case class CredentialIssuerServerEndpoints(
createCredentialOfferServerEndpoint,
nonceServerEndpoint,
createCredentialIssuerServerEndpoint,
getCredentialIssuersServerEndpoint,
createCredentialConfigurationServerEndpoint,
issuerMetadataServerEndpoint
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.hyperledger.identus.agent.server.config.AppConfig
import org.hyperledger.identus.api.http.ErrorResponse.badRequest
import org.hyperledger.identus.api.http.ErrorResponse.internalServerError
import org.hyperledger.identus.api.http.{ErrorResponse, RequestContext}
import org.hyperledger.identus.api.util.PaginationUtils
import org.hyperledger.identus.castor.core.model.did.{CanonicalPrismDID, PrismDID}
import org.hyperledger.identus.oidc4vc.CredentialIssuerEndpoints.ExtendedErrorResponse
import org.hyperledger.identus.oidc4vc.http.*
Expand Down Expand Up @@ -42,6 +43,10 @@ trait CredentialIssuerController {
request: CreateCredentialIssuerRequest
): ZIO[WalletAccessContext, ErrorResponse, CredentialIssuer]

def getCredentialIssuers(
ctx: RequestContext
): ZIO[WalletAccessContext, ErrorResponse, CredentialIssuerPage]

def createCredentialConfiguration(
ctx: RequestContext,
issuerId: UUID,
Expand Down Expand Up @@ -222,6 +227,18 @@ case class CredentialIssuerControllerImpl(
} yield issuer
}

override def getCredentialIssuers(
ctx: RequestContext
): ZIO[WalletAccessContext, ErrorResponse, CredentialIssuerPage] =
val uri = ctx.request.uri
for {
issuers <- issuerMetadataService.getCredentialIssuers
} yield CredentialIssuerPage(
self = uri.toString(),
pageOf = PaginationUtils.composePageOfUri(uri).toString,
contents = issuers.map(i => i)
)

override def createCredentialConfiguration(
ctx: RequestContext,
issuerId: UUID,
Expand All @@ -240,7 +257,7 @@ case class CredentialIssuerControllerImpl(
override def getIssuerMetadata(ctx: RequestContext, issuerId: UUID): IO[ErrorResponse, IssuerMetadata] = {
for
credentialIssuer <- issuerMetadataService.getCredentialIssuer(issuerId)
credentialConfigurations <- issuerMetadataService.listCredentialConfiguration(issuerId)
credentialConfigurations <- issuerMetadataService.getCredentialConfigurations(issuerId)
yield IssuerMetadata.fromIssuer(agentBaseUrl, credentialIssuer, credentialConfigurations)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,18 @@ object CredentialIssuer {
given Conversion[PolluxCredentialIssuer, CredentialIssuer] = domain =>
CredentialIssuer(domain.id, domain.authorizationServer.toString)
}

case class CredentialIssuerPage(
self: String,
kind: String = "CredentialIssuerPage",
pageOf: String,
next: Option[String] = None,
previous: Option[String] = None,
contents: Seq[CredentialIssuer]
)

object CredentialIssuerPage {
given schema: Schema[CredentialIssuerPage] = Schema.derived
given encoder: JsonEncoder[CredentialIssuerPage] = DeriveJsonEncoder.gen
given decoder: JsonDecoder[CredentialIssuerPage] = DeriveJsonDecoder.gen
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,74 @@ package org.hyperledger.identus.pollux.core.repository
import org.hyperledger.identus.pollux.core.model.oidc4vc.CredentialConfiguration
import org.hyperledger.identus.pollux.core.model.oidc4vc.CredentialIssuer
import org.hyperledger.identus.shared.models.WalletAccessContext
import org.hyperledger.identus.shared.models.WalletId
import zio.*

import java.util.UUID

trait OIDC4VCIssuerMetadataRepository {
def createIssuer(issuer: CredentialIssuer): URIO[WalletAccessContext, Unit]
def findAllIssuerForWallet: URIO[WalletAccessContext, Seq[CredentialIssuer]]
def findWalletIssuers: URIO[WalletAccessContext, Seq[CredentialIssuer]]
def findIssuer(issuerId: UUID): UIO[Option[CredentialIssuer]]

def createCredentialConfiguration(issuerId: UUID, config: CredentialConfiguration): URIO[WalletAccessContext, Unit]
def findAllCredentialConfigurations(issuerId: UUID): UIO[Seq[CredentialConfiguration]]
}

class InMemoryOIDC4VCIssuerMetadataRepository(
issuerStore: Ref[Map[UUID, CredentialIssuer]],
credentialConfigStore: Ref[Map[(UUID, String), CredentialConfiguration]]
issuerStore: Ref[Map[WalletId, Seq[CredentialIssuer]]],
credentialConfigStore: Ref[Map[(WalletId, UUID), Seq[CredentialConfiguration]]]
) extends OIDC4VCIssuerMetadataRepository {

override def createIssuer(issuer: CredentialIssuer): URIO[WalletAccessContext, Unit] =
issuerStore.modify(m => () -> m.updated(issuer.id, issuer))
for {
walletId <- ZIO.serviceWith[WalletAccessContext](_.walletId)
_ <- issuerStore.modify(m => () -> m.updated(walletId, m.getOrElse(walletId, Nil) :+ issuer))
} yield ()

override def findAllIssuerForWallet: URIO[WalletAccessContext, Seq[CredentialIssuer]] =
issuerStore.get.map(_.values.toSeq)
override def findWalletIssuers: URIO[WalletAccessContext, Seq[CredentialIssuer]] =
for {
walletId <- ZIO.serviceWith[WalletAccessContext](_.walletId)
store <- issuerStore.get
} yield store.getOrElse(walletId, Nil)

override def findIssuer(issuerId: UUID): UIO[Option[CredentialIssuer]] =
issuerStore.get.map(_.get(issuerId))
issuerStore.get.map(m => m.values.flatten.find(_.id == issuerId))

override def createCredentialConfiguration(
issuerId: UUID,
config: CredentialConfiguration
): URIO[WalletAccessContext, Unit] = {
for {
issuerExists <- issuerStore.get.map(_.contains(issuerId))
configExists <- credentialConfigStore.get.map(_.contains((issuerId, config.configurationId)))
walletId <- ZIO.serviceWith[WalletAccessContext](_.walletId)
issuerExists <- issuerStore.get.map(_.getOrElse(walletId, Nil).exists(_.id == issuerId))
configExists <- credentialConfigStore.get
.map(m => m.getOrElse((walletId, issuerId), Nil).exists(_.configurationId == config.configurationId))
_ <- ZIO
.cond(issuerExists, (), s"Issuer with id $issuerId does not exist")
.orDieWith(Exception(_))
_ <- ZIO
.cond(!configExists, (), s"Configuration with id ${config.configurationId} already exists")
.orDieWith(Exception(_))
_ <- credentialConfigStore.update(_.updated((issuerId, config.configurationId), config))
_ <- credentialConfigStore
.update(m => m.updated((walletId, issuerId), m.getOrElse((walletId, issuerId), Nil) :+ config))
} yield ()
}

override def findAllCredentialConfigurations(
issuerId: UUID
): UIO[Seq[CredentialConfiguration]] =
credentialConfigStore.get.map(_.filter(_._1._1 == issuerId).values.toSeq)
credentialConfigStore.get.map { m =>
m.collect { case ((_, iss), configs) if iss == issuerId => configs }.flatten.toSeq
}

}

object InMemoryOIDC4VCIssuerMetadataRepository {
def layer: ULayer[OIDC4VCIssuerMetadataRepository] =
ZLayer.fromZIO(
for {
issuerStore <- Ref.make(Map.empty[UUID, CredentialIssuer])
credentialConfigStore <- Ref.make(Map.empty[(UUID, String), CredentialConfiguration])
issuerStore <- Ref.make(Map.empty[WalletId, Seq[CredentialIssuer]])
credentialConfigStore <- Ref.make(Map.empty[(WalletId, UUID), Seq[CredentialConfiguration]])
} yield InMemoryOIDC4VCIssuerMetadataRepository(issuerStore, credentialConfigStore)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,15 @@ object OIDC4VCIssuerMetadataServiceError {

trait OIDC4VCIssuerMetadataService {
def createCredentialIssuer(authorizationServer: URL): URIO[WalletAccessContext, CredentialIssuer]
def getCredentialIssuers: URIO[WalletAccessContext, Seq[CredentialIssuer]]
def getCredentialIssuer(issuerId: UUID): IO[IssuerIdNotFound, CredentialIssuer]
def createCredentialConfiguration(
issuerId: UUID,
format: CredentialFormat,
configurationId: String,
schemaId: String
): ZIO[WalletAccessContext, InvalidSchemaId | UnsupportedCredentialFormat, CredentialConfiguration]
def listCredentialConfiguration(
def getCredentialConfigurations(
issuerId: UUID
): IO[IssuerIdNotFound, Seq[CredentialConfiguration]]
}
Expand All @@ -65,6 +66,9 @@ class OIDC4VCIssuerMetadataServiceImpl(repository: OIDC4VCIssuerMetadataReposito
repository.createIssuer(issuer).as(issuer)
}

override def getCredentialIssuers: URIO[WalletAccessContext, Seq[CredentialIssuer]] =
repository.findWalletIssuers

override def getCredentialIssuer(issuerId: UUID): IO[IssuerIdNotFound, CredentialIssuer] =
repository
.findIssuer(issuerId)
Expand Down Expand Up @@ -98,7 +102,7 @@ class OIDC4VCIssuerMetadataServiceImpl(repository: OIDC4VCIssuerMetadataReposito
} yield config
}

override def listCredentialConfiguration(
override def getCredentialConfigurations(
issuerId: UUID
): IO[IssuerIdNotFound, Seq[CredentialConfiguration]] =
repository
Expand Down

0 comments on commit b0e9f59

Please sign in to comment.