Skip to content

Commit

Permalink
feat(prism-agent): add keycloak authorization support to endpoints (#753
Browse files Browse the repository at this point in the history
)

Signed-off-by: Pat Losoponkul <[email protected]>
  • Loading branch information
patlo-iog authored Oct 16, 2023
1 parent 9a8e216 commit 3e7534f
Show file tree
Hide file tree
Showing 64 changed files with 1,018 additions and 444 deletions.
27 changes: 18 additions & 9 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ lazy val V = new {

val vaultDriver = "6.1.0"
val micrometer = "1.11.2"

val nimbusJwt = "10.0.0"
val keycloak = "22.0.4"
}

/** Dependencies */
Expand All @@ -106,13 +109,16 @@ lazy val D = new {
val circeGeneric: ModuleID = "io.circe" %% "circe-generic" % V.circe
val circeParser: ModuleID = "io.circe" %% "circe-parser" % V.circe

val jwtCirce = "com.github.jwt-scala" %% "jwt-circe" % V.jwtCirceVersion

// https://mvnrepository.com/artifact/org.didcommx/didcomm/0.3.2
val didcommx: ModuleID = "org.didcommx" % "didcomm" % "0.3.1"
val peerDidcommx: ModuleID = "org.didcommx" % "peerdid" % "0.3.0"
val didScala: ModuleID = "app.fmgp" %% "did" % "0.0.0+113-61efa271-SNAPSHOT"

// Customized version of numbus jose jwt
// from https://github.com/goncalo-frade-iohk/Nimbus-JWT_Fork/commit/8a6665c25979e771afae29ce8c965c8b0312fefb
val jwk: ModuleID = "io.iohk.atala" % "nimbus-jose-jwt" % "10.0.0"
val nimbusJwt: ModuleID = "io.iohk.atala" % "nimbus-jose-jwt" % V.nimbusJwt

val typesafeConfig: ModuleID = "com.typesafe" % "config" % V.typesafeConfig
val scalaPbRuntime: ModuleID =
Expand Down Expand Up @@ -268,8 +274,6 @@ lazy val D_Pollux_VC_JWT = new {
.exclude("io.circe", "circe-generic_2.13")
.exclude("io.circe", "circe-parser_2.13")

val jwtCirce = "com.github.jwt-scala" %% "jwt-circe" % V.jwtCirceVersion

val zio = "dev.zio" %% "zio" % V.zio
val zioPrelude = "dev.zio" %% "zio-prelude" % V.zioPreludeVersion

Expand All @@ -285,7 +289,7 @@ lazy val D_Pollux_VC_JWT = new {
val zioDependencies: Seq[ModuleID] = Seq(zio, zioPrelude, zioTest, zioTestSbt, zioTestMagnolia)
val circeDependencies: Seq[ModuleID] = Seq(D.circeCore, D.circeGeneric, D.circeParser)
val baseDependencies: Seq[ModuleID] =
circeDependencies ++ zioDependencies :+ jwtCirce :+ circeJsonSchema :+ networkntJsonSchemaValidator :+ D.jwk :+ scalaTest
circeDependencies ++ zioDependencies :+ D.jwtCirce :+ circeJsonSchema :+ networkntJsonSchemaValidator :+ D.nimbusJwt :+ scalaTest

// Project Dependencies
lazy val polluxVcJwtDependencies: Seq[ModuleID] = baseDependencies
Expand Down Expand Up @@ -337,6 +341,7 @@ lazy val D_PrismAgent = new {
val flyway = "org.flywaydb" % "flyway-core" % V.flyway

val vaultDriver = "io.github.jopenlibs" % "vault-java-driver" % V.vaultDriver
val keycloakAuthz = "org.keycloak" % "keycloak-authz-client" % V.keycloak

// Dependency Modules
val baseDependencies: Seq[ModuleID] = Seq(
Expand Down Expand Up @@ -375,6 +380,8 @@ lazy val D_PrismAgent = new {
lazy val keyManagementDependencies: Seq[ModuleID] =
baseDependencies ++ bouncyDependencies ++ D.doobieDependencies ++ Seq(D.zioCatsInterop, D.zioMock, vaultDriver)

lazy val iamDependencies: Seq[ModuleID] = Seq(keycloakAuthz, D.jwtCirce)

lazy val serverDependencies: Seq[ModuleID] =
baseDependencies ++ tapirDependencies ++ postgresDependencies ++ Seq(D.zioMock, D.mockito)
}
Expand Down Expand Up @@ -417,7 +424,7 @@ lazy val models = project
), // TODO try to remove this from this module
// libraryDependencies += D.didScala
)
.settings(libraryDependencies += D.jwk) //FIXME just for the DidAgent
.settings(libraryDependencies += D.nimbusJwt) //FIXME just for the DidAgent

/* TODO move code from agentDidcommx to here
models implementation for didcommx () */
Expand Down Expand Up @@ -539,7 +546,7 @@ lazy val resolver = project // maybe merge into models
D.peerDidcommx,
D.munit,
D.munitZio,
D.jwk,
D.nimbusJwt,
),
testFrameworks += new TestFramework("munit.Framework")
)
Expand Down Expand Up @@ -758,9 +765,11 @@ lazy val prismAgentWalletAPI = project
.settings(prismAgentConnectCommonSettings)
.settings(
name := "prism-agent-wallet-api",
libraryDependencies ++= D_PrismAgent.keyManagementDependencies ++ D_PrismAgent.postgresDependencies ++ Seq(
D.zioMock
)
libraryDependencies ++=
D_PrismAgent.keyManagementDependencies ++
D_PrismAgent.iamDependencies ++
D_PrismAgent.postgresDependencies ++
Seq(D.zioMock)
)
.dependsOn(
agentDidcommx,
Expand Down
25 changes: 14 additions & 11 deletions infrastructure/shared/docker-compose-mt-keycloak.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,20 +148,23 @@ services:
- swagger-ui

keycloak:
image: bitnami/keycloak:22.0.3
image: quay.io/keycloak/keycloak:22.0.4
ports:
- "9980:8080"
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
KEYCLOAK_DATABASE_VENDOR: dev-mem
KEYCLOAK_EXTRA_ARGS: --health-enabled=true
KEYCLOAK_EXTRA_ARGS_PREPENDED: --verbose
healthcheck:
test: ["CMD", "curl", "-f", "http://keycloak:8080/health"]
interval: 5s
timeout: 5s
retries: 10
command: start-dev --health-enabled=true

keycloak-wait:
image: badouralix/curl-jq:ubuntu
command:
- /bin/bash
- -c
- until curl http://keycloak:8080/health; do sleep 1; done && echo "Keycloak is ready."
depends_on:
keycloak:
condition: service_started

keycloak-init:
image: badouralix/curl-jq:ubuntu
Expand All @@ -175,8 +178,8 @@ services:
volumes:
- ./keycloak/init-script.sh:/workspace/init-script.sh
depends_on:
keycloak:
condition: service_healthy
keycloak-wait:
condition: service_completed_successfully

volumes:
pg_data_db:
Expand Down
34 changes: 30 additions & 4 deletions infrastructure/shared/keycloak/init-script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,39 @@ function create_realm() {
}"
}

function create_prism_agent_client() {
function create_client() {
local access_token=$1
local client_id=$2
local client_secret=$3

curl --request POST "$KEYCLOAK_BASE_URL/admin/realms/$REALM_NAME/clients" \
--fail -s \
-H "Authorization: Bearer $access_token" \
-H "Content-Type: application/json" \
--data-raw "{
\"id\": \"prism-agent\",
\"id\": \"$client_id\",
\"directAccessGrantsEnabled\": true,
\"authorizationServicesEnabled\": true,
\"serviceAccountsEnabled\": true,
\"secret\": \"$PRISM_AGENT_CLIENT_SECRET\"
\"secret\": \"$client_secret\"
}"
}

function create_user() {
local access_token=$1
local username=$2
local password=$3

curl --request POST "$KEYCLOAK_BASE_URL/admin/realms/$REALM_NAME/users" \
--fail -s \
-H "Authorization: Bearer $access_token" \
-H "Content-Type: application/json" \
--data-raw "{
\"id\": \"$username\",
\"username\": \"$username\",
\"firstName\": \"$username\",
\"enabled\": true,
\"credentials\": [{\"value\": $password, \"temporary\": false}]
}"
}

Expand All @@ -58,4 +78,10 @@ echo "Creating a new test realm ..."
create_realm $ADMIN_ACCESS_TOKEN

echo "Creating a new prism-agent client ..."
create_prism_agent_client $ADMIN_ACCESS_TOKEN
create_client $ADMIN_ACCESS_TOKEN "prism-agent" $PRISM_AGENT_CLIENT_SECRET

echo "Creating a new prism-manage client ..."
create_client $ADMIN_ACCESS_TOKEN "prism-manage" $PRISM_AGENT_CLIENT_SECRET

echo "Creating a new sample user ..."
create_user $ADMIN_ACCESS_TOKEN "alice" "1234"
48 changes: 35 additions & 13 deletions prism-agent/service/server/src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,28 @@ agent {
autoProvisioning = true
autoProvisioning = ${?API_KEY_AUTO_PROVISIONING}
}
keycloak {
enabled = false
enabled = ${?KEYCLOAK_ENABLED}

keycloakUrl = "http://localhost:9980"
keycloakUrl = ${?KEYCLOAK_URL}

realmName = "atala-demo"
realmName = ${?KEYCLOAK_REALM}

clientId = "prism-agent"
clientId = ${?KEYCLOAK_CLIENT_ID}

clientSecret = "prism-agent-demo-secret"
clientSecret = ${?KEYCLOAK_CLIENT_SECRET}

# autoUpgradeToRPT is used to enable the auto RPT (requesting party token) logic.
# if enabled, normal accessToken can be used to perform permission checks by obtaining RPT from accessToken.
# if disabled, accessToken must be RPT which already include the permission claims.
autoUpgradeToRPT = false
autoUpgradeToRPT = ${?KEYCLOAK_UMA_AUTO_UPGRADE_RPT}
}
}
database {
host = "localhost"
Expand Down Expand Up @@ -164,13 +186,13 @@ agent {
}
}
secretStorage {
// Supports the following backend: [vault, postgres]
// If 'postgres' is used as a backend, it uses the agent db configuration
// If any other backend is used, its corresponding configuration must be configured.
# Supports the following backend: [vault, postgres, memory]
# If 'postgres' is used as a backend, it uses the agent db configuration
# If any other backend is used, its corresponding configuration must be configured.
backend = "vault"
backend = ${?SECRET_STORAGE_BACKEND}

// Configuration for Vault as a secret storage
# Configuration for Vault as a secret storage
vault {
address = "http://localhost:8200"
address = ${?VAULT_ADDR}
Expand All @@ -185,20 +207,20 @@ agent {
}

defaultWallet {
// A configuration for initializing default wallet.
//
// Once the default wallet is initialized, the agent will use persisted configurations
// from its storage and may ignore these parameters.
# A configuration for initializing default wallet.
#
# Once the default wallet is initialized, the agent will use persisted configurations
# from its storage and may ignore these parameters.
enabled = true
enabled = ${?DEFAULT_WALLET_ENABLED}

// Wallet seed to be used for the default wallet. If not provided, it will be generated.
# Wallet seed to be used for the default wallet. If not provided, it will be generated.
seed = ${?DEFAULT_WALLET_SEED}

// Webhook url of the default wallet.
// If provided, webhook notification will be created when wallet is initialized.
// If not provided, webhook will not be created.
// If provided after the default wallet has been initialized, it will not have any effect.
# Webhook url of the default wallet.
# If provided, webhook notification will be created when wallet is initialized.
# If not provided, webhook will not be created.
# If provided after the default wallet has been initialized, it will not have any effect.
webhookUrl = ${?DEFAULT_WALLET_WEBHOOK_URL}
webhookApiKey = ${?DEFAULT_WALLET_WEBHOOK_API_KEY}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ import io.iohk.atala.connect.sql.repository.{JdbcConnectionRepository, Migration
import io.iohk.atala.event.controller.EventControllerImpl
import io.iohk.atala.event.notification.EventNotificationServiceImpl
import io.iohk.atala.iam.authentication.DefaultAuthenticator
import io.iohk.atala.iam.authentication.admin.{AdminApiKeyAuthenticatorImpl, AdminConfig}
import io.iohk.atala.iam.authentication.apikey.{ApiKeyAuthenticatorImpl, ApiKeyConfig, JdbcAuthenticationRepository}
import io.iohk.atala.iam.authentication.apikey.JdbcAuthenticationRepository
import io.iohk.atala.iam.entity.http.controller.{EntityController, EntityControllerImpl}
import io.iohk.atala.iam.wallet.http.controller.WalletManagementControllerImpl
import io.iohk.atala.issue.controller.IssueControllerImpl
Expand Down Expand Up @@ -113,8 +112,6 @@ object MainApp extends ZIOAppDefault {
DidCommX.liveLayer,
// infra
SystemModule.configLayer,
AdminConfig.layer,
ApiKeyConfig.layer,
ZioHttpClient.layer,
// observability
DefaultJvmMetrics.live.unit,
Expand All @@ -136,7 +133,7 @@ object MainApp extends ZIOAppDefault {
EventControllerImpl.layer,
// domain
AppModule.apolloLayer,
AppModule.didJwtResolverlayer,
AppModule.didJwtResolverLayer,
DIDOperationValidator.layer(),
DIDResolver.layer,
HttpURIDereferencerImpl.layer,
Expand All @@ -152,7 +149,9 @@ object MainApp extends ZIOAppDefault {
VerificationPolicyServiceImpl.layer,
WalletManagementServiceImpl.layer,
// authentication
AdminApiKeyAuthenticatorImpl.layer >+> ApiKeyAuthenticatorImpl.layer >+> DefaultAuthenticator.layer,
AppModule.builtInAuthenticatorLayer,
AppModule.keycloakAuthenticatorLayer,
DefaultAuthenticator.layer,
// grpc
GrpcModule.prismNodeStubLayer,
// storage
Expand Down
Loading

0 comments on commit 3e7534f

Please sign in to comment.