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

feat(prism-agent): add keycloak authorization support to endpoints #753

Merged
merged 34 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1149123
feat(prism-agent): support keycloak auth config
Oct 3, 2023
13f96a4
feat(prism-agent): update auth config validation
Oct 4, 2023
52b9693
chore: remove unused classes
Oct 4, 2023
786b099
revert: change config file back
Oct 4, 2023
4549676
feat(prism-agent): add keycloak auth placeholder interface
Oct 5, 2023
2c8e543
Revert "chore: remove unused classes"
Oct 5, 2023
1846d85
Revert "Revert "chore: remove unused classes""
Oct 5, 2023
bbabf8c
fix(prism-agent): make tests compile
Oct 5, 2023
4a02cca
fix: mulitple auth poc on tapir endpoint
Oct 5, 2023
0d5e25f
feat(prism-agent): apply jwt auth for did-registrar endpoints
Oct 5, 2023
68eafd6
wip: keycloak authz permissions check poc
Oct 6, 2023
f330ba7
build: create new user in keycloak init script
Oct 9, 2023
0256bed
feat(prism-agent): implement RPT permission logic
Oct 9, 2023
d455a6a
feat(prism-agent): support batch wallet get
Oct 9, 2023
e65f1ad
chore: resolve merge conflicts
Oct 10, 2023
cd54202
feat(prism-agent): authz interface
Oct 10, 2023
12f7e57
feat(prism-agent): add keycloak client wrapper
Oct 10, 2023
e0c604f
fix: token introspection
Oct 10, 2023
4f22945
feat: migrate connection endpoints to authz
Oct 10, 2023
f27d795
feat: migrate event endpoints to authz
Oct 10, 2023
14ccdba
feat: migrate wallet & entity endpoitns to authz
Oct 10, 2023
b611fc5
feat: migrate isscred endpoints to authz
Oct 10, 2023
606ab8f
feat: migrate creddef & schemareg endpoints to authz
Oct 11, 2023
94f686d
feat: migrate verpolicy endpoints to authz
Oct 11, 2023
9f4b0a2
feat: migrate presentproof endpoints to authz
Oct 11, 2023
1157662
chore: small refactor
Oct 11, 2023
baa0bb1
build: use official keycloak image
Oct 11, 2023
64d44e1
build: create manage client in local keycloak bootstrap
Oct 11, 2023
696a9ed
fix: make tests compile
Oct 11, 2023
29aefc6
chore: minor cleanup
Oct 11, 2023
f8cc424
fix: make keycloak not call service when disabled
Oct 11, 2023
992f08b
chore: pr cleanup
Oct 11, 2023
eaed8c8
chore: expose app config envs
Oct 16, 2023
589565e
chore: oas spec alignment with new security object
Oct 16, 2023
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
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"
40 changes: 27 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,20 @@ agent {
autoProvisioning = true
autoProvisioning = ${?API_KEY_AUTO_PROVISIONING}
}
# TODO: expose environent variable
keycloak {
enabled = false // TODO: change to false before merge !!!

keycloakUrl = "http://localhost:9980"
realmName = "atala-demo"
clientId = "prism-agent"
clientSecret = "prism-agent-demo-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 = true // TODO: change to false before merge
patlo-iog marked this conversation as resolved.
Show resolved Hide resolved
}
}
database {
host = "localhost"
Expand Down Expand Up @@ -164,13 +178,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 +199,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
Loading