From a82d687b2fd7c1f6e03052d52aba288fcee4da63 Mon Sep 17 00:00:00 2001 From: Dotan Simha Date: Tue, 30 Jan 2024 12:00:23 +0200 Subject: [PATCH] wasm bug fixes, and added smoke tests and ci workflow (#389) --- .github/workflows/ci.yaml | 72 +- Cargo.lock | 15 + Cargo.toml | 2 +- README.md | 16 +- bin/cloudflare_worker/.cargo/config | 4 + bin/cloudflare_worker/package.json | 3 +- bin/conductor/docker/Dockerfile | 1 + libs/common/src/graphql.rs | 2 + libs/config/conductor.schema.json | 11 +- libs/config/src/lib.rs | 4 +- libs/engine/src/gateway.rs | 7 + libs/smoke_tests/Cargo.toml | 20 + libs/smoke_tests/README.md | 22 + libs/smoke_tests/docker-compose.yaml | 26 + libs/smoke_tests/src/http_get.rs | 39 + libs/smoke_tests/src/jwt.rs | 186 ++ libs/smoke_tests/src/lib.rs | 3 + ...s__http_get__smoke_http_get__http_get.snap | 9 + ...ke_tests__jwt__smoke_jwt__empty_token.snap | 11 + ...sts__jwt__smoke_jwt__invalid_audience.snap | 11 + ...tests__jwt__smoke_jwt__invalid_issuer.snap | 11 + ..._tests__jwt__smoke_jwt__invalid_token.snap | 11 + ...s__jwt__smoke_jwt__invalid_token_type.snap | 11 + ...t__smoke_jwt__nonsecure_missing_token.snap | 9 + ..._jwt__smoke_jwt__token_header_missing.snap | 11 + ...ke_tests__jwt__smoke_jwt__valid_token.snap | 9 + ...ry__smoke_telemetry__telemetry_jaeger.snap | 9 + ...etry__smoke_telemetry__telemetry_otlp.snap | 9 + libs/smoke_tests/src/telemetry.rs | 177 ++ libs/smoke_tests/test_gw.yaml | 90 + libs/smoke_tests/volumes/keycloak/realm.json | 2241 +++++++++++++++++ plugins/http_get/Cargo.toml | 1 + plugins/http_get/src/plugin.rs | 34 +- plugins/jwt_auth/Cargo.toml | 1 + plugins/jwt_auth/src/config.rs | 1 + plugins/jwt_auth/src/jwks_provider.rs | 22 +- plugins/telemetry/src/config.rs | 2 +- 37 files changed, 3076 insertions(+), 37 deletions(-) create mode 100644 bin/cloudflare_worker/.cargo/config create mode 100644 libs/smoke_tests/Cargo.toml create mode 100644 libs/smoke_tests/README.md create mode 100644 libs/smoke_tests/docker-compose.yaml create mode 100644 libs/smoke_tests/src/http_get.rs create mode 100644 libs/smoke_tests/src/jwt.rs create mode 100644 libs/smoke_tests/src/lib.rs create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__http_get__smoke_http_get__http_get.snap create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__empty_token.snap create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_audience.snap create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_issuer.snap create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_token.snap create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_token_type.snap create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__nonsecure_missing_token.snap create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__token_header_missing.snap create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__valid_token.snap create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__telemetry__smoke_telemetry__telemetry_jaeger.snap create mode 100644 libs/smoke_tests/src/snapshots/smoke_tests__telemetry__smoke_telemetry__telemetry_otlp.snap create mode 100644 libs/smoke_tests/src/telemetry.rs create mode 100644 libs/smoke_tests/test_gw.yaml create mode 100644 libs/smoke_tests/volumes/keycloak/realm.json diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d56e73a5..4cef384d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -30,7 +30,7 @@ jobs: run: cargo install -q worker-build && worker-build - name: test - run: cargo test -- --nocapture + run: cargo test --workspace --exclude smoke_tests -- --nocapture env: RUST_BACKTRACE: full @@ -47,6 +47,74 @@ jobs: working-directory: libs/napi run: yarn build + smoke-test: + name: smoke test + needs: + - build + runs-on: ubuntu-22.04 + steps: + - name: checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + + - name: setup environment + uses: ./.github/actions/setup + + - name: run containers + working-directory: libs/smoke_tests/ + run: docker compose -f docker-compose.yaml up -d + + - uses: JarvusInnovations/background-action@v1 + name: run test server in background + with: + run: cargo run + working-directory: tests/test-server + wait-on: http-get://127.0.0.1:4000 + tail: true + wait-for: 10m + log-output-if: failure + log-output: true + + - uses: JarvusInnovations/background-action@v1 + name: run gateway in background (binary) + with: + run: cargo run --bin conductor -- ./libs/smoke_tests/test_gw.yaml + wait-on: http-get://127.0.0.1:9000/graphql + tail: true + wait-for: 15m + log-output-if: failure + log-output: true + + - name: run tests (binary) + working-directory: libs/smoke_tests/ + run: cargo test --features binary -- --nocapture + env: + CONDUCTOR_URL: http://127.0.0.1:9000 + + - uses: the-guild-org/shared-config/setup@main + name: setup env (wasm) + with: + nodeVersion: 20 + packageManager: pnpm + workingDirectory: bin/cloudflare_worker + packageManagerVersion: 8 + + - uses: JarvusInnovations/background-action@v1 + name: run gateway in background (wasm) + with: + working-directory: bin/cloudflare_worker + run: pnpm start:smoke + wait-on: http-get://127.0.0.1:8787/graphql + tail: true + wait-for: 15m + log-output-if: failure + log-output: true + + - name: run tests (wasm) + working-directory: libs/smoke_tests/ + run: cargo test --features wasm -- --nocapture + env: + CONDUCTOR_URL: http://127.0.0.1:8787 + graphql-over-http: runs-on: ubuntu-22.04 steps: @@ -151,7 +219,7 @@ jobs: - name: run panic free analyzer run: cargo panic-analyzer > ./panic-audit.md env: - IGNORED_CRATES: e2e_tests,benches,server + IGNORED_CRATES: smoke_tests,e2e_tests,benches,server IGNORED_FILES: ./plugins/jwt_auth/src/test.rs - name: comment on pull request diff --git a/Cargo.lock b/Cargo.lock index 3742a4ff..ca16d91b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2319,6 +2319,7 @@ dependencies = [ "schemars", "serde", "tracing", + "urlencoding", ] [[package]] @@ -2629,6 +2630,7 @@ dependencies = [ "thiserror", "tracing", "wasm_polyfills", + "web-time", ] [[package]] @@ -4462,6 +4464,19 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +[[package]] +name = "smoke_tests" +version = "0.0.0" +dependencies = [ + "conductor_common", + "insta", + "lazy_static", + "reqwest", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "snafu" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index ca51943e..1c688a79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" members = ["bin/*", "libs/*", "plugins/*"] -exclude = ["bin/npm", "tests/test-server", "tools/panic_free_analyzer"] +exclude = ["bin/npm", "tests/test-server"] [workspace.dependencies] tokio = "1.35.1" diff --git a/README.md b/README.md index 2c7476bb..a7e57638 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ > [!IMPORTANT] > Conductor gateway is still under development, and currently available as alpha. > -> Please use it with caution. Feedback and Contributions are always welcome! +> Please use it with caution. Feedback and Contributions are always welcome! # Conductor: MIT open-source GraphQL Gateway @@ -53,6 +53,12 @@ Conductor's configuration can be defined in both YAML and JSON formats. The conf ### Configuration File Example ```yaml +server: + port: 9000 + +logger: + filter: error + sources: - type: graphql id: my-source @@ -63,12 +69,10 @@ endpoints: - path: /graphql from: my-source plugins: + - type: cors + config: + allowed_origin: "*" - type: graphiql - -plugins: - - type: cors - config: - allowed_origin: "*" ``` ## Running Conductor diff --git a/bin/cloudflare_worker/.cargo/config b/bin/cloudflare_worker/.cargo/config new file mode 100644 index 00000000..7817b679 --- /dev/null +++ b/bin/cloudflare_worker/.cargo/config @@ -0,0 +1,4 @@ +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "link-args=-z stack-size=3000000", +] diff --git a/bin/cloudflare_worker/package.json b/bin/cloudflare_worker/package.json index 51cd39cd..accf8c25 100644 --- a/bin/cloudflare_worker/package.json +++ b/bin/cloudflare_worker/package.json @@ -4,7 +4,8 @@ "private": true, "scripts": { "deploy": "wrangler deploy", - "dev": "wrangler dev --var \"CONDUCTOR_CONFIG:$(cat ../../test_config/worker.yaml)\"" + "dev": "wrangler dev --var \"CONDUCTOR_CONFIG:$(cat ../../test_config/worker.yaml)\"", + "start:smoke": "wrangler dev --var \"ENABLE_WASM_FEATURES:false\" --var \"CONDUCTOR_CONFIG:$(cat ../../libs/smoke_tests/test_gw.yaml)\"" }, "devDependencies": { "wrangler": "3.25.0" diff --git a/bin/conductor/docker/Dockerfile b/bin/conductor/docker/Dockerfile index 2ed5f2c4..6e4dfa35 100644 --- a/bin/conductor/docker/Dockerfile +++ b/bin/conductor/docker/Dockerfile @@ -9,6 +9,7 @@ COPY plugins plugins COPY bin bin RUN rm -rf lib/benches RUN rm -rf lib/e2e_tests +RUN rm -rf lib/smoke_tests RUN echo 'fn main() { println!(""); }' > ./bin/conductor/src/main.rs RUN echo 'fn main() { println!(""); }' > ./bin/conductor/src/lib.rs # We are only building the dependencies here, with a dummy file, this compiles all dependencies code only. diff --git a/libs/common/src/graphql.rs b/libs/common/src/graphql.rs index c457e232..3df26fd8 100644 --- a/libs/common/src/graphql.rs +++ b/libs/common/src/graphql.rs @@ -64,6 +64,8 @@ impl Display for GraphQLRequest { #[derive(thiserror::Error, Debug)] pub enum ExtractGraphQLOperationError { + #[error("invalid url query parameter")] + InvalidQueryParameterEncoding, #[error("missing query parameter")] MissingQueryParameter, #[error("invalid content-type header")] diff --git a/libs/config/conductor.schema.json b/libs/config/conductor.schema.json index 4667127f..d8066dcf 100644 --- a/libs/config/conductor.schema.json +++ b/libs/config/conductor.schema.json @@ -162,7 +162,7 @@ "type": { "type": "string", "enum": [ - "mocl" + "mock" ] }, "id": { @@ -1706,14 +1706,7 @@ "cache_duration": { "description": "Duration after which the cached JWKS should be expired. If not specified, the default value will be used.", "default": "10m", - "anyOf": [ - { - "$ref": "#/definitions/Duration" - }, - { - "type": "null" - } - ] + "type": "string" }, "prefetch": { "description": "If set to `true`, the JWKS will be fetched on startup and cached. In case of invalid JWKS, the error will be ignored and the plugin will try to fetch again when server receives the first request. If set to `false`, the JWKS will be fetched on-demand, when the first request comes in.", diff --git a/libs/config/src/lib.rs b/libs/config/src/lib.rs index cf5f88ac..37db88a5 100644 --- a/libs/config/src/lib.rs +++ b/libs/config/src/lib.rs @@ -403,7 +403,7 @@ pub enum SourceDefinition { /// The configuration for the GraphQL source. config: GraphQLSourceConfig, }, - #[serde(rename = "mocl")] + #[serde(rename = "mock")] /// A simple, single GraphQL endpoint Mock { /// The identifier of the source. This is used to reference the source in the `from` field of an endpoint definition. @@ -601,7 +601,7 @@ pub fn parse_config_contents( } Err(errors) => { for error in errors { - println!("error: {}", error); + println!("error: {:?}", error); } // @expected: 👇 diff --git a/libs/engine/src/gateway.rs b/libs/engine/src/gateway.rs index dfa14511..b99baf2f 100644 --- a/libs/engine/src/gateway.rs +++ b/libs/engine/src/gateway.rs @@ -57,6 +57,13 @@ pub enum GatewayError { impl ConductorGateway { pub fn match_route(&self, route: &Url) -> Result<&ConductorGatewayRouteData, GatewayError> { + // TODO: This function should probably use a more sophisticated matching algorithm. + for conductor_route in &self.routes { + if route.path() == conductor_route.base_path { + return Ok(&conductor_route.route_data); + } + } + for conductor_route in &self.routes { if route.path().starts_with(&conductor_route.base_path) { return Ok(&conductor_route.route_data); diff --git a/libs/smoke_tests/Cargo.toml b/libs/smoke_tests/Cargo.toml new file mode 100644 index 00000000..bf1859da --- /dev/null +++ b/libs/smoke_tests/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "smoke_tests" +version = "0.0.0" +edition = "2021" + +[lib] +path = "src/lib.rs" + +[dependencies] +tokio = { workspace = true, features = ["full"] } +reqwest = { workspace = true, features = ["json"] } +conductor_common = { path = "../common", features = ["test_utils"] } +serde = { workspace = true } +serde_json = { workspace = true } +insta = "1.34.0" +lazy_static = "1.4.0" + +[features] +binary = [] +wasm = [] diff --git a/libs/smoke_tests/README.md b/libs/smoke_tests/README.md new file mode 100644 index 00000000..25e8cd50 --- /dev/null +++ b/libs/smoke_tests/README.md @@ -0,0 +1,22 @@ +## Smoke Tests + +The purpose of these tests is to execute different flows that are connected to real data sources and other third-party dependencies. + +Failure in one of these tests during a PR, might indicate that one of the crucial flows is broken. + +## CI Setup + +Check the `.github/workflows/ci.yaml` (job: `smoke-test`). We are running the same tests against WASM and binary builds. + +## Running Locally + +To run locally, following these instructions: + +1. Make sure you have Docker engine installed and running. +2. Start the third-pary dependencies by running: `docker compose -f docker-compose.yaml up -d --remove-orphans --wait --force-recreate` inside the `libs/smoke_tests` directory. +3. Start a real GraphQL server by running: `cargo run` inside `tests/test-server` directory. + +Now, run Conductor with one of the configurations: + +- For binary runtime, use: `cargo run --bin conductor -- ./libs/smoke_tests/test_gw.yaml` in the root workspace dir. +- For WASM runtime, use: `pnpm start:smoke` inside `bin/cloudflare_worker`. diff --git a/libs/smoke_tests/docker-compose.yaml b/libs/smoke_tests/docker-compose.yaml new file mode 100644 index 00000000..3bbf56c2 --- /dev/null +++ b/libs/smoke_tests/docker-compose.yaml @@ -0,0 +1,26 @@ +version: "3.8" +name: "conductor-smoke-test" +services: + # Keycloak (JWT/JWKS Provider) + keycloak: + image: quay.io/keycloak/keycloak:23.0.4 + environment: + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + command: ["start-dev", "--import-realm"] + volumes: + - ./volumes/keycloak:/opt/keycloak/data/import + ports: + - 4001:8080 + + # Jaeger (Telmetry) + jaeger: + image: jaegertracing/all-in-one:1.53 + environment: + COLLECTOR_OTLP_ENABLED: true + ports: + - 4317:4317 # OTLP over gRPC + - 4318:4318 # OTLP over HTTP + - 6831:6831/udp # Jaeger Thrift over compact thrift protocol, UDP + - 6832:6832/udp # Jaeger Thrift over binary thrift protocol, UDP + - 16686:16686 # Jaeger UI / API diff --git a/libs/smoke_tests/src/http_get.rs b/libs/smoke_tests/src/http_get.rs new file mode 100644 index 00000000..f6b339fb --- /dev/null +++ b/libs/smoke_tests/src/http_get.rs @@ -0,0 +1,39 @@ +#[cfg(test)] +mod smoke_http_get { + use insta::assert_debug_snapshot; + use lazy_static::lazy_static; + use reqwest::header::{HeaderMap, CONTENT_TYPE}; + use serde_json::Value; + use std::env::var; + + lazy_static! { + static ref CONDUCTOR_URL: String = var("CONDUCTOR_URL").expect("CONDUCTOR_URL env var not set"); + } + + #[tokio::test] + async fn http_get() { + let mut headers = HeaderMap::default(); + headers.append( + CONTENT_TYPE, + "application/x-www-form-urlencoded".parse().unwrap(), + ); + + let req = reqwest::Client::new() + .request( + reqwest::Method::GET, + format!( + "{}/http-get?query=query%20%7B%20__typename%20%7D", + CONDUCTOR_URL.as_str() + ), + ) + .headers(headers); + + let gql_response = req + .send() + .await + .expect("failed to run http req to conductor"); + assert_eq!(gql_response.status(), 200); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + } +} diff --git a/libs/smoke_tests/src/jwt.rs b/libs/smoke_tests/src/jwt.rs new file mode 100644 index 00000000..0ec62fdd --- /dev/null +++ b/libs/smoke_tests/src/jwt.rs @@ -0,0 +1,186 @@ +#[cfg(test)] +mod smoke_jwt { + use conductor_common::http::ConductorHttpRequest; + use insta::assert_debug_snapshot; + use lazy_static::lazy_static; + use reqwest::header::CONTENT_TYPE; + use reqwest::Response; + use serde_json::Value; + use std::collections::HashMap; + use std::env::var; + + lazy_static! { + static ref CONDUCTOR_URL: String = var("CONDUCTOR_URL").expect("CONDUCTOR_URL env var not set"); + } + + static KEYCLOAK_URL: &str = "http://localhost:4001"; + + #[derive(serde::Deserialize)] + struct JwtMock { + access_token: String, + } + + async fn create_token() -> String { + let mut form_params = HashMap::<&str, &str>::new(); + form_params.insert("client_id", "conductor"); + form_params.insert("grant_type", "password"); + // Should match /libs/smoke_tests/volumes/keycloak/realm.json client secret + form_params.insert("client_secret", "dlApaPkSI9xPL4gG3HLnIU8MnB66eJOz"); + form_params.insert("scope", "openid"); + form_params.insert("username", "test"); + form_params.insert("password", "test"); + + let response = reqwest::Client::new() + .post(format!( + "{KEYCLOAK_URL}/realms/test/protocol/openid-connect/token" + )) + .form(&form_params) + .header(CONTENT_TYPE, "application/x-www-form-urlencoded") + .send() + .await + .expect("failed to create jwt token"); + + if !response.status().is_success() { + panic!( + "failed to create jwt token, status: {}, body: {:?}", + response.status(), + response.text().await + ); + } + + response.json::().await.unwrap().access_token + } + + async fn make_graphql_request(req: ConductorHttpRequest) -> Response { + let req_builder = reqwest::Client::new() + .request(req.method, req.uri) + .headers(req.headers) + .body(req.body); + + req_builder + .send() + .await + .expect("failed to run http req to conductor") + } + + #[tokio::test] + async fn valid_token() { + let token = create_token().await; + let mut req = ConductorHttpRequest::default(); + req.method = reqwest::Method::POST; + req.uri = format!("{}/jwt", CONDUCTOR_URL.as_str()).parse().unwrap(); + req.headers.append( + "authorization", + format!("Bearer {}", token).parse().unwrap(), + ); + let gql_response = make_graphql_request(req).await; + assert_eq!(gql_response.status(), 200); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + } + + #[tokio::test] + async fn invalid_token_type() { + let token = create_token().await; + let mut req = ConductorHttpRequest::default(); + req.method = reqwest::Method::POST; + req.uri = format!("{}/jwt", CONDUCTOR_URL.as_str()).parse().unwrap(); + req.headers.append( + "authorization", + format!("TokenType {}", token).parse().unwrap(), + ); + let gql_response = make_graphql_request(req).await; + assert_eq!(gql_response.status(), 400); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + } + + #[tokio::test] + async fn token_header_missing() { + let mut req = ConductorHttpRequest::default(); + req.method = reqwest::Method::POST; + req.uri = format!("{}/jwt", CONDUCTOR_URL.as_str()).parse().unwrap(); + req + .headers + .append("authorization", format!("Bearer ").parse().unwrap()); + let gql_response = make_graphql_request(req).await; + assert_eq!(gql_response.status(), 400); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + } + + #[tokio::test] + async fn empty_token() { + let mut req = ConductorHttpRequest::default(); + req.method = reqwest::Method::POST; + req.uri = format!("{}/jwt", CONDUCTOR_URL.as_str()).parse().unwrap(); + let gql_response = make_graphql_request(req).await; + assert_eq!(gql_response.status(), 400); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + } + + #[tokio::test] + async fn invalid_token() { + let token = "bad jwt"; + let mut req = ConductorHttpRequest::default(); + req.method = reqwest::Method::POST; + req.uri = format!("{}/jwt", CONDUCTOR_URL.as_str()).parse().unwrap(); + req.headers.append( + "authorization", + format!("Bearer {}", token).parse().unwrap(), + ); + let gql_response = make_graphql_request(req).await; + assert_eq!(gql_response.status(), 400); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + } + + #[tokio::test] + async fn invalid_audience() { + // Same as valid token, but has audience set to "bad_aud" + let token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImYwN0FUa0tNUUZrWWVkQ2pXaFdCRG1yWG5EaFJGVDV0bmxwOUh5Z0FpMUEifQ.eyJleHAiOjE4MDY0NTUwMTMsImlhdCI6MTcwNjQ1NDk1MywianRpIjoiYTMzYzZjMjktOWMxYS00MTRmLTgxOTUtNDk2YjBhZGNkYzZjIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo0MDAxL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOiJiYWRfYXVkIiwic3ViIjoiZGRlZGMwNjItOTc0NC00ZGVhLThhOGQtNjk3YWIxMmNkNTBjIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiY29uZHVjdG9yIiwic2Vzc2lvbl9zdGF0ZSI6ImQ3YzYyYjE2LWI0NmEtNDdjMC1iZDYwLWJkYTk5OWM4MjY2MiIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtbWFzdGVyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJzaWQiOiJkN2M2MmIxNi1iNDZhLTQ3YzAtYmQ2MC1iZGE5OTljODI2NjIiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6InRlc3QgdGVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6InRlc3QiLCJnaXZlbl9uYW1lIjoidGVzdCIsImZhbWlseV9uYW1lIjoidGVzdCIsImVtYWlsIjoidGVzdEB0ZXN0LmNvbSJ9.g3BXU0esmeB4g47UdxtXdNFdmPuv2YR-GxJY_uiyS8WrEB727JQc5tHpHpAhWZHY36lkJ8e7Mly13eeR1AVoEqfmr3RHWlCppNQH4q3iE5wS3FFSlv44uPpzTawvmdITMTXZGY86-RTUp2TlJ92IBztu-BeCj5XEUi60E3TJ6TBj6kS-HbJeDCv4neRcg_-dJ2GPPsO1ob0mdk_I5MHRwDMBog4XQpgiqiNC6rUiXnyqIVCjIgRBogswv7pbS44P4m5-N2_DyF15FuKsPWLApByI32ona5SZNemTrJHgA5jatBsBz2weFZurJSniyn6YKJWHcsV7D6cOComxVjtmJA"; + let mut req = ConductorHttpRequest::default(); + req.method = reqwest::Method::POST; + req.uri = format!("{}/jwt", CONDUCTOR_URL.as_str()).parse().unwrap(); + req.headers.append( + "authorization", + format!("Bearer {}", token).parse().unwrap(), + ); + let gql_response = make_graphql_request(req).await; + assert_eq!(gql_response.status(), 400); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + } + + #[tokio::test] + async fn invalid_issuer() { + // Same as valid token, but has audience set to "bad_aud" + let token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImYwN0FUa0tNUUZrWWVkQ2pXaFdCRG1yWG5EaFJGVDV0bmxwOUh5Z0FpMUEifQ.eyJleHAiOjE4MDY0NTUwMTMsImlhdCI6MTcwNjQ1NDk1MywianRpIjoiYTMzYzZjMjktOWMxYS00MTRmLTgxOTUtNDk2YjBhZGNkYzZjIiwiaXNzIjoiaHR0cDovL290aGVyLmNvbS9yZWFsbXMvbWFzdGVyIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImRkZWRjMDYyLTk3NDQtNGRlYS04YThkLTY5N2FiMTJjZDUwYyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImNvbmR1Y3RvciIsInNlc3Npb25fc3RhdGUiOiJkN2M2MmIxNi1iNDZhLTQ3YzAtYmQ2MC1iZGE5OTljODI2NjIiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbIi8qIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLW1hc3RlciIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIiwic2lkIjoiZDdjNjJiMTYtYjQ2YS00N2MwLWJkNjAtYmRhOTk5YzgyNjYyIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsIm5hbWUiOiJ0ZXN0IHRlc3QiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0IiwiZ2l2ZW5fbmFtZSI6InRlc3QiLCJmYW1pbHlfbmFtZSI6InRlc3QiLCJlbWFpbCI6InRlc3RAdGVzdC5jb20ifQ.Pu6z9WW-MoE2C_xXNotwIe6ggD8FTkHmEBt51s9IjCLGImtvP8mfEhUUddHFbfWc5HBE8uD5LXYv8k6-KIukbnnADd5gLuumxbeF0FQVLvZbaFSH_U4gMp3OJhmGf7S7XKgUmSIO8v9Ax2AHBqmzgfcqmoPcRGCh_aRw8ue9Xye0tJ6f3VQYGZrc1T6rSuf2T1STxh-m_QAPrT0C2NcgQKHOQG4ZY60U9xjNpF1Kq5139v7HKs89qwHv99vj6eBhRGr6dL6DUrmkE93qfNFm4IykkLk8ELoRm3mGAmykPygEYhRb-gxRFG2--2-zqRGN51qmX9TGY6ZQWHvD-SQgNg"; + let mut req = ConductorHttpRequest::default(); + req.method = reqwest::Method::POST; + req.uri = format!("{}/jwt", CONDUCTOR_URL.as_str()).parse().unwrap(); + req.headers.append( + "authorization", + format!("Bearer {}", token).parse().unwrap(), + ); + let gql_response = make_graphql_request(req).await; + assert_eq!(gql_response.status(), 400); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + } + + #[tokio::test] + async fn nonsecure_missing_token() { + // This one checks against jwt-nonsecure and it allows access without token + let mut req = ConductorHttpRequest::default(); + req.method = reqwest::Method::POST; + req.uri = format!("{}/jwt-nonsecure", CONDUCTOR_URL.as_str()) + .parse() + .unwrap(); + let gql_response = make_graphql_request(req).await; + assert_eq!(gql_response.status(), 200); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + } +} diff --git a/libs/smoke_tests/src/lib.rs b/libs/smoke_tests/src/lib.rs new file mode 100644 index 00000000..b026314e --- /dev/null +++ b/libs/smoke_tests/src/lib.rs @@ -0,0 +1,3 @@ +mod http_get; +mod jwt; +mod telemetry; diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__http_get__smoke_http_get__http_get.snap b/libs/smoke_tests/src/snapshots/smoke_tests__http_get__smoke_http_get__http_get.snap new file mode 100644 index 00000000..093b6c4a --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__http_get__smoke_http_get__http_get.snap @@ -0,0 +1,9 @@ +--- +source: libs/smoke_tests/src/http_get.rs +expression: json_body +--- +Object { + "data": Object { + "__typename": String("Query"), + }, +} diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__empty_token.snap b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__empty_token.snap new file mode 100644 index 00000000..84cd0e3e --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__empty_token.snap @@ -0,0 +1,11 @@ +--- +source: libs/smoke_tests/src/jwt.rs +expression: json_body +--- +Object { + "errors": Array [ + Object { + "message": String("unauthenticated request"), + }, + ], +} diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_audience.snap b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_audience.snap new file mode 100644 index 00000000..84cd0e3e --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_audience.snap @@ -0,0 +1,11 @@ +--- +source: libs/smoke_tests/src/jwt.rs +expression: json_body +--- +Object { + "errors": Array [ + Object { + "message": String("unauthenticated request"), + }, + ], +} diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_issuer.snap b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_issuer.snap new file mode 100644 index 00000000..84cd0e3e --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_issuer.snap @@ -0,0 +1,11 @@ +--- +source: libs/smoke_tests/src/jwt.rs +expression: json_body +--- +Object { + "errors": Array [ + Object { + "message": String("unauthenticated request"), + }, + ], +} diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_token.snap b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_token.snap new file mode 100644 index 00000000..84cd0e3e --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_token.snap @@ -0,0 +1,11 @@ +--- +source: libs/smoke_tests/src/jwt.rs +expression: json_body +--- +Object { + "errors": Array [ + Object { + "message": String("unauthenticated request"), + }, + ], +} diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_token_type.snap b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_token_type.snap new file mode 100644 index 00000000..84cd0e3e --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__invalid_token_type.snap @@ -0,0 +1,11 @@ +--- +source: libs/smoke_tests/src/jwt.rs +expression: json_body +--- +Object { + "errors": Array [ + Object { + "message": String("unauthenticated request"), + }, + ], +} diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__nonsecure_missing_token.snap b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__nonsecure_missing_token.snap new file mode 100644 index 00000000..4335f70d --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__nonsecure_missing_token.snap @@ -0,0 +1,9 @@ +--- +source: libs/smoke_tests/src/jwt.rs +expression: json_body +--- +Object { + "data": Object { + "__typename": String("Query"), + }, +} diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__token_header_missing.snap b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__token_header_missing.snap new file mode 100644 index 00000000..84cd0e3e --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__token_header_missing.snap @@ -0,0 +1,11 @@ +--- +source: libs/smoke_tests/src/jwt.rs +expression: json_body +--- +Object { + "errors": Array [ + Object { + "message": String("unauthenticated request"), + }, + ], +} diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__valid_token.snap b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__valid_token.snap new file mode 100644 index 00000000..4335f70d --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__jwt__smoke_jwt__valid_token.snap @@ -0,0 +1,9 @@ +--- +source: libs/smoke_tests/src/jwt.rs +expression: json_body +--- +Object { + "data": Object { + "__typename": String("Query"), + }, +} diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__telemetry__smoke_telemetry__telemetry_jaeger.snap b/libs/smoke_tests/src/snapshots/smoke_tests__telemetry__smoke_telemetry__telemetry_jaeger.snap new file mode 100644 index 00000000..3c447443 --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__telemetry__smoke_telemetry__telemetry_jaeger.snap @@ -0,0 +1,9 @@ +--- +source: libs/smoke_tests/src/telemetry.rs +expression: json_body +--- +Object { + "data": Object { + "__typename": String("Query"), + }, +} diff --git a/libs/smoke_tests/src/snapshots/smoke_tests__telemetry__smoke_telemetry__telemetry_otlp.snap b/libs/smoke_tests/src/snapshots/smoke_tests__telemetry__smoke_telemetry__telemetry_otlp.snap new file mode 100644 index 00000000..3c447443 --- /dev/null +++ b/libs/smoke_tests/src/snapshots/smoke_tests__telemetry__smoke_telemetry__telemetry_otlp.snap @@ -0,0 +1,9 @@ +--- +source: libs/smoke_tests/src/telemetry.rs +expression: json_body +--- +Object { + "data": Object { + "__typename": String("Query"), + }, +} diff --git a/libs/smoke_tests/src/telemetry.rs b/libs/smoke_tests/src/telemetry.rs new file mode 100644 index 00000000..e037acbd --- /dev/null +++ b/libs/smoke_tests/src/telemetry.rs @@ -0,0 +1,177 @@ +#[cfg(test)] +#[cfg(not(feature = "wasm"))] // TODO: Remove this when Telemtry will be fully functional on WASM runtime +mod smoke_telemetry { + use conductor_common::http::ConductorHttpRequest; + use insta::assert_debug_snapshot; + use lazy_static::lazy_static; + use reqwest::Response; + use serde_json::Value; + use std::env::var; + use std::time::{Duration, SystemTime, UNIX_EPOCH}; + use tokio::time::sleep; + + lazy_static! { + static ref CONDUCTOR_URL: String = var("CONDUCTOR_URL").expect("CONDUCTOR_URL env var not set"); + } + + static JAEGER_API: &str = "localhost:16686"; + + async fn make_graphql_request(req: ConductorHttpRequest) -> Response { + let req_builder = reqwest::Client::new() + .request(req.method, req.uri) + .headers(req.headers) + .body(req.body); + + req_builder + .send() + .await + .expect("failed to run http req to conductor") + } + + #[derive(Clone, Debug, serde::Deserialize)] + struct JaegerTracesResponse { + pub data: Vec, + } + + #[derive(Clone, Debug, serde::Deserialize)] + struct JaegerTrace { + pub spans: Vec, + } + + #[derive(Clone, Debug, serde::Deserialize)] + struct JaegerSpan { + #[serde(rename = "operationName")] + pub operation_name: String, + } + + async fn fetch_traces(service: &str, start: u128, end: u128) -> Vec { + let url = format!("http://{JAEGER_API}/api/traces?end={end}&limit=20&lookback=1h&maxDuration&minDuration&service={service}&start={start}"); + + let response = reqwest::Client::new() + .get(url) + .send() + .await + .expect("failed to fetch jaeger traces") + .json::() + .await + .expect("failed to get jaeger response"); + + assert_eq!(response.data.len(), 1); + + response.data[0].spans.clone() + } + + #[tokio::test] + async fn telemetry_jaeger() { + let mut req = ConductorHttpRequest::default(); + req.method = reqwest::Method::POST; + req.uri = format!("{}/telemetry-jaeger-udp", CONDUCTOR_URL.as_str()) + .parse() + .unwrap(); + let start_timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); + let gql_response: Response = make_graphql_request(req).await; + let end_timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); + assert_eq!(gql_response.status(), 200); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + + sleep(Duration::from_secs(5)).await; // Jaeger needs some processing time... + let traces = fetch_traces("conductor-jaeger-test", start_timestamp, end_timestamp).await; + + assert!(traces + .iter() + .find(|v| v.operation_name == "transform_request") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "transform_response") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "query") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "execute") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "POST /") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "upstream_call") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "graphql_parse") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "HTTP POST /telemetry-jaeger-udp") + .is_some()); + } + + #[tokio::test] + async fn telemetry_otlp() { + let mut req = ConductorHttpRequest::default(); + req.method = reqwest::Method::POST; + req.uri = format!("{}/telemetry-jaeger-otlp", CONDUCTOR_URL.as_str()) + .parse() + .unwrap(); + let start_timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); + let gql_response: Response = make_graphql_request(req).await; + let end_timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); + assert_eq!(gql_response.status(), 200); + let json_body = gql_response.json::().await.unwrap(); + assert_debug_snapshot!(json_body); + + sleep(Duration::from_secs(5)).await; // Jaeger needs some processing time... + let traces = fetch_traces("conductor-otlp-test", start_timestamp, end_timestamp).await; + + assert!(traces + .iter() + .find(|v| v.operation_name == "transform_request") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "transform_response") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "query") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "execute") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "POST /") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "upstream_call") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "graphql_parse") + .is_some()); + assert!(traces + .iter() + .find(|v| v.operation_name == "HTTP POST /telemetry-jaeger-otlp") + .is_some()); + } +} diff --git a/libs/smoke_tests/test_gw.yaml b/libs/smoke_tests/test_gw.yaml new file mode 100644 index 00000000..55703b0f --- /dev/null +++ b/libs/smoke_tests/test_gw.yaml @@ -0,0 +1,90 @@ +server: + port: 9000 + host: "127.0.0.1" + +logger: + format: pretty + filter: info + +sources: + - type: graphql + id: upstream + config: + endpoint: http://localhost:4000/ + +endpoints: + # Just passthrough + - from: upstream + path: /graphql + plugins: + - type: graphiql + + # The following endpoint is used to test the JWT plugin + # We are running Keycloak with some pre-baked configuration (see `docker-compose.yaml`) + # This one also sets the reject_unauthenticated_requests to true so it should not allow to access without a token + - path: /jwt + from: upstream + plugins: + - type: jwt_auth + config: + reject_unauthenticated_requests: true + issuers: + - http://localhost:4001/realms/test + audiences: + - account + jwks_providers: + - source: remote + url: http://localhost:4001/realms/test/protocol/openid-connect/certs + prefetch: true + cache_duration: 10m + + # This one also sets the reject_unauthenticated_requests to false so it should allow access without a token + - path: /jwt-nonsecure + from: upstream + plugins: + - type: jwt_auth + config: + reject_unauthenticated_requests: false + issuers: + - http://localhost:4001/realms/test + audiences: + - account + jwks_providers: + - source: remote + url: http://localhost:4001/realms/test/protocol/openid-connect/certs + prefetch: true + cache_duration: 10m + + # Jaeger with UDP endpoint + - path: /telemetry-jaeger-udp + from: upstream + plugins: + - type: telemetry + enabled: ${ENABLE_WASM_FEATURES:true} + config: + service_name: conductor-jaeger-test + targets: + - type: jaeger + config: + endpoint: "127.0.0.1:6831" + + # Jaeger with OTLP endpoint + - path: /telemetry-jaeger-otlp + from: upstream + plugins: + - type: telemetry + enabled: ${ENABLE_WASM_FEATURES:true} + config: + service_name: conductor-otlp-test + targets: + - type: otlp + endpoint: http://localhost:4317/v1/traces + protocol: grpc + + # HTTP GET + - path: /http-get + from: upstream + plugins: + - type: http_get + config: + mutations: false diff --git a/libs/smoke_tests/volumes/keycloak/realm.json b/libs/smoke_tests/volumes/keycloak/realm.json new file mode 100644 index 00000000..7b249d0e --- /dev/null +++ b/libs/smoke_tests/volumes/keycloak/realm.json @@ -0,0 +1,2241 @@ +{ + "id": "332ba276-391f-4093-97da-54c821c77ab4", + "realm": "test", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "2e251540-84fc-4350-b505-5a44746bec44", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "332ba276-391f-4093-97da-54c821c77ab4", + "attributes": {} + }, + { + "id": "9ec940cc-2883-46a0-a83c-beb3b5f2c179", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "332ba276-391f-4093-97da-54c821c77ab4", + "attributes": {} + }, + { + "id": "36e79287-eaaf-4cb3-95fa-e7ede85b45c0", + "name": "default-roles-test", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": ["offline_access", "uma_authorization"], + "client": { + "account": ["view-profile", "manage-account"] + } + }, + "clientRole": false, + "containerId": "332ba276-391f-4093-97da-54c821c77ab4", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "c76f5349-ff4f-44b8-b82f-88f98fe732dd", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "b90fd18f-cc86-42e1-a878-39e394ec785b", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "55ce90d7-9b1d-4eaf-a813-102b7edf8ec1", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "e942d6ad-d9e6-4c1a-9e2c-15c06b130130", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "5c8442f4-9a01-4973-9900-6be389f288b4", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "4b1fbed5-7447-42bc-9448-67e0976a1be1", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "c4aff7b1-6226-4590-be5c-73d4b680d193", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "ecb9b042-85db-41bb-a1ec-08c54f290732", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "322a5e88-2a21-4bbd-a3d7-fcd9dc227372", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "a9f7c17f-f1f9-4d5c-bbea-81b3a91abb1a", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "f176fd91-efd7-4756-b2e1-a8b06391ab99", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "81ccadd0-34a7-4f69-83d3-9d0d33409931", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "1fd87d95-c9f4-4f03-947f-493e8fe91f40", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "839ad851-1d15-42f2-9429-4fcd5f243264", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-events", + "query-clients", + "impersonation", + "manage-users", + "view-identity-providers", + "manage-clients", + "view-authorization", + "query-users", + "manage-realm", + "view-events", + "query-groups", + "query-realms", + "manage-authorization", + "create-client", + "view-realm", + "view-clients", + "manage-identity-providers", + "view-users" + ] + } + }, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "e0209256-97ed-4b76-9aef-1908cd424ed5", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "fb1b6ffb-03c5-47f1-8108-36925ac6e553", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-clients"] + } + }, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "184d7f15-7c3d-4e9e-a8b0-eb16bef34342", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "e75d9acf-7354-473a-b80a-8c44a10cdcdc", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + }, + { + "id": "feda2ec3-84d0-4c50-86d2-8a0d2faf3e2e", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-users", "query-groups"] + } + }, + "clientRole": true, + "containerId": "21af38e1-630a-4856-885f-1640c05664f7", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "9f0259ba-20e5-484f-91dd-b4745a789ebc", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "4726bc3a-25ec-4827-a982-3c90c6fcb7ee", + "attributes": {} + } + ], + "conductor": [ + { + "id": "204f1efa-cc76-4714-bb97-df25d0e87a2f", + "name": "uma_protection", + "composite": false, + "clientRole": true, + "containerId": "dd677863-5369-4088-8d31-8b0b35675f98", + "attributes": {} + } + ], + "account": [ + { + "id": "76e84c46-8ae0-4954-8248-4788425bf4e6", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": ["view-consent"] + } + }, + "clientRole": true, + "containerId": "580e5a79-de2e-4343-b39c-f4853805a4f0", + "attributes": {} + }, + { + "id": "b22cd89d-8161-4735-b21a-6a4e3aab8d88", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "580e5a79-de2e-4343-b39c-f4853805a4f0", + "attributes": {} + }, + { + "id": "1809b3b0-a560-40eb-896f-b615ee32217d", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "580e5a79-de2e-4343-b39c-f4853805a4f0", + "attributes": {} + }, + { + "id": "2e52269f-1ddd-45d9-815d-90fda4d76fc3", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "580e5a79-de2e-4343-b39c-f4853805a4f0", + "attributes": {} + }, + { + "id": "966e1809-6b49-4f33-939d-a81cd668bd84", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "580e5a79-de2e-4343-b39c-f4853805a4f0", + "attributes": {} + }, + { + "id": "ddb74bfe-ef9e-4908-9278-36d054defe9f", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": ["manage-account-links"] + } + }, + "clientRole": true, + "containerId": "580e5a79-de2e-4343-b39c-f4853805a4f0", + "attributes": {} + }, + { + "id": "1f5e4859-3004-4682-9b24-f77fecf85d1a", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "580e5a79-de2e-4343-b39c-f4853805a4f0", + "attributes": {} + }, + { + "id": "8fd01db4-7611-427a-8fb0-d839c7847aed", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "580e5a79-de2e-4343-b39c-f4853805a4f0", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRole": { + "id": "36e79287-eaaf-4cb3-95fa-e7ede85b45c0", + "name": "default-roles-test", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "332ba276-391f-4093-97da-54c821c77ab4" + }, + "requiredCredentials": ["password"], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppFreeOTPName", + "totpAppGoogleName", + "totpAppMicrosoftAuthenticatorName" + ], + "localizationTexts": {}, + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": ["ES256"], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyExtraOrigins": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "webAuthnPolicyPasswordlessExtraOrigins": [], + "users": [ + { + "id": "e4b97ce6-a635-479b-ab6f-8a28b2a04d41", + "createdTimestamp": 1706465688353, + "username": "service-account-conductor", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "conductor", + "credentials": [], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": ["default-roles-test"], + "clientRoles": { + "conductor": ["uma_protection"] + }, + "notBefore": 0, + "groups": [] + }, + { + "id": "85a0d88c-6aed-42ca-b0c8-1dfd159b514c", + "createdTimestamp": 1706465715384, + "username": "test", + "enabled": true, + "totp": false, + "emailVerified": true, + "firstName": "test", + "lastName": "test", + "email": "test@test.com", + "credentials": [ + { + "id": "4be2cd58-c1fd-4a8e-bc89-f4fba258865d", + "type": "password", + "userLabel": "My password", + "createdDate": 1706465721261, + "secretData": "{\"value\":\"N8G79qVAFj6Y8SkOI4plGZJ1WAsw/fRvpvTCToaee5I=\",\"salt\":\"ne2yyZZmjPjCFudVXI8J9g==\",\"additionalParameters\":{}}", + "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": ["default-roles-test"], + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": ["offline_access"] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": ["manage-account", "view-groups"] + } + ] + }, + "clients": [ + { + "id": "580e5a79-de2e-4343-b39c-f4853805a4f0", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/test/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/realms/test/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "0438e56b-e00c-4afe-bd51-6fdd489588dd", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/test/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/realms/test/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "6aaa1ce4-5591-447c-92dc-3f995a788dca", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "3d4d8ef1-4b92-4e0a-88ab-b24decf438b1", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "4726bc3a-25ec-4827-a982-3c90c6fcb7ee", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "dd677863-5369-4088-8d31-8b0b35675f98", + "clientId": "conductor", + "name": "conductor", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "dlApaPkSI9xPL4gG3HLnIU8MnB66eJOz", + "redirectUris": ["/*"], + "webOrigins": ["/*"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "authorizationServicesEnabled": true, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "oauth2.device.authorization.grant.enabled": "false", + "client.secret.creation.time": "1706465688", + "backchannel.logout.session.required": "true", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "b03793db-7e94-4b91-af4a-5e9486c6dc81", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "introspection.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "dd5b351d-3575-43c7-b117-4da372a79311", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "client_id", + "introspection.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "client_id", + "jsonType.label": "String" + } + }, + { + "id": "a931f07e-b26a-4dc8-b479-36723999889b", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "introspection.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "21af38e1-630a-4856-885f-1640c05664f7", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "703c5d66-61a7-4f21-9ebd-035a316ab822", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/test/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/admin/test/console/*"], + "webOrigins": ["+"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "75e82837-c408-4cc2-92fd-e271275e2a28", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "33e04ad0-8496-4852-8a52-b89264ece352", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "07b91b75-38f5-47f8-83b8-19687e5ae65a", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "f040b600-e65e-4d50-a0bd-711ada76a9be", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "18ab043e-8dcb-45e1-b37b-f6907172386a", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "db62b67c-1677-4e22-8fd5-868f516af09b", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "598fad27-603a-4478-8c2a-38f1bbb78b81", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "477ca9e2-acdf-4aa3-9272-8e81dab40f9a", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "e931b4b4-c444-424b-826f-78a22f8f7e6f", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "00e8f4b2-b2cd-4c1a-a288-1f77cd0a3e92", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "e4536cea-c3ce-438a-b4ca-52504fdd328a", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "d9af8012-a189-4907-93a4-854be51045cd", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "beb8b0ae-ddad-4520-b128-7db980ec0752", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "b54fef34-971f-42cd-9ec0-78c126ff6106", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "5b49d828-cac6-49ca-a307-0ffafd90669d", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "edccd58b-d077-481d-8ff0-64f86b847928", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "175011a7-7515-4bf2-86c1-3336303f0fd2", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "c3e4525e-db98-4c5b-80e0-d2e5e2118b71", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "5779487a-aaeb-4080-abf8-2043f74a7dfe", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "22ec5bfa-8588-40a6-aef1-61be20fe6fb4", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "14c105ff-28b4-448b-b555-d4c1dc944b15", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + }, + { + "id": "09e2fa3f-7299-4d76-8cac-64933bf5ef1e", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "fa11c705-5d4e-44d7-9cf1-e6a875459293", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "c74b7692-43e1-44e8-b57b-16e9a96a2008", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "06dc46e2-49de-42f1-8cf9-7b8a74361af2", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "e7f25879-7ea6-4488-852c-22d9595ebf0a", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "109dd2e1-b79e-4fb0-8478-606e4187be39", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "6ab75ebf-68fb-44dd-a65b-80001491923a", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "introspection.token.claim": "true", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "01f663de-38fa-47b6-95c8-06608d480c2d", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "de18c4e5-7913-41d4-8839-449306d3093c", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "91ae3b14-e0d7-45bf-8a31-b50ceaffa3d5", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "1d9b3093-06a2-4156-a3b8-064b0d3e4d7a", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "6c834fc4-82e1-493f-9ac6-138f31e14e53", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String" + } + }, + { + "id": "b239fd62-1365-4021-9973-e7e3085ea4f0", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String" + } + }, + { + "id": "96db35f3-8ce0-4590-82e6-9bfddb5e0990", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "5b90420a-cab9-4a97-a188-ceb1a557a4b0", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "dc970c2a-c4b0-4bdd-b23d-f70fe13c268f", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "id": "f8f159ec-dd6b-4161-be01-99b2d93976ad", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins", + "acr" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "referrerPolicy": "no-referrer", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": ["jboss-logging"], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "951d0522-e036-42a9-9fca-0ac93fb08756", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-property-mapper", + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-role-list-mapper", + "saml-user-attribute-mapper", + "oidc-address-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper" + ] + } + }, + { + "id": "e864e083-fa5e-4f61-952b-905bb6240676", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": ["200"] + } + }, + { + "id": "e3ac9150-f362-42ca-be77-0dcefb77a3c3", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper", + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper", + "saml-user-attribute-mapper", + "saml-role-list-mapper" + ] + } + }, + { + "id": "b3ded76a-4aa0-41fb-a77f-103a9b7024bc", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "adba2948-e09f-4811-8c01-f749408119fc", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "c19a1b1c-32ef-42dc-88c9-f21c642c824e", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "a0c4ea3c-5997-4a6e-b55e-50ee62b2cd2c", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "b5f790d3-c47d-412b-b18c-c0ee7c80c41e", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": ["true"], + "client-uris-must-match": ["true"] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "d811e10a-dd84-4aa0-a1b9-c70dba2b2cfb", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "kid": ["6bc2bc46-2fbf-4c23-ac62-6db02b8360b4"], + "secret": ["Gh4_gAWbPullj8FBwomrtA"], + "priority": ["100"] + } + }, + { + "id": "0eb5122d-02da-4699-b9da-639a5b420016", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEowIBAAKCAQEAySQA9ReEWHNsgKW/nDZgT5qPcEnYBuQaWBVDVHUqy9AlUmg8fzS5OxHnsiPakxSzFjWrlWAh5ewInwQogHVqDrZ0YDwhUszta+Zdc7ZLMDxFZbIm9GJVLnlELFMo2CCsNB3DAyyoemgfqD4yt0rc+Lk1LINKoAY3VbPg1Vm/lF4xS1Puart/sFQHLygNzlxK5cNfZB0Rdb5ZhJhKPewSuKUMEWAsHyAbUuwy3RprawEe+gwr0jo/VKiwP+JGW3su1HUR2CXYoj8pQWV5BhKoHZJHkoQX7UsEIQmMyQd3aZNJgeC5eyrYWpIFbpZ7YvsTSyjlDnTGzgmPRIqDpvMgEQIDAQABAoIBAAJiSZueC3zD43OT41U95L7UyWHukh+kyPYlzF4+JEN1pFeZciwcyxw63ljjaWYIXwYCnVXAJa60sTZ3RWaeXaMugOsaE1gIo0C94noqXTJFaJontiP73WAXYlo3IxsKqhfVCXhqaVXIEaX8FLxM6BkPkuSD6tjCMhozL1VAAtAGl2PA/bN+907AFJIQV4R55768sR/lQwplAyHV4QGcrF41ssIF6EN1FHC3ussqAmwi6Bn3ecD0nNHSrTqyenjF3N/9EqkD7VoCzlKX/gxA4HmH3h40ARqKIExmxN1JSbhcY5PadrZ2Q88WBBQJMZNFgUWOolzaEUEqf2PRrpWpnpUCgYEA6kRV6HQXpWdK7SHfx16tRfyk9YRiWTNYiDPCXGXm2gFzexNsqhb1TUmd0KfmucD6N+Z12XKsfDjV2JrPXJ57leCUCPbu7wueszMk6BhmgJ+alw8IpG+JxbHx69QDitQHX9slzJ32rEzEoeprza9cryFv1FodMdDrwJgi9M7+iiUCgYEA28zxg56WZ5plXD/mxomUvqncS9p+WHcc471tllgAnTFyAnVziX318PS74d8/M2M5VQhbLBUc9KYqVAUT2JmKd01gczjagnNkc0mODYKqrakxuorbgSd+jsJ0Efv3+Y2u3kiKIDhsB1rmIqeW03Mo4eBsN66D4A5A+Kw3TnZVPH0CgYA94DrIoRE10OiLZa0TncAqgiaX6pWHdfZmUzV87jVhU4zK70B+VH4KIe2bFR3t5jiw1yA9hPpl/SUOhyVKE1oVJjIzg/VsEieisVmILBm203EjgQcJV6SOIQgDAAbHfmVMPjfnqbljnQs6sQ40T6MtZqmA7yRS6hm6zvQ8sQeuOQKBgAIxg19636ldAxHumVD14oQckdQ38/bSCXnEVCmh6y+mZKrdnS+MED49UvqoDatnnBTbJn3EICvY1KdN/aq1rYoPPZ0ovhV7LU9xNnewDB/7hkGETTh2um6WRi4/w3O8FDinpKk5pupT3+QMpBasGYZXhPzxQuOjgMZ6gXrI94TJAoGBAMSTa6r+WrIANkaejX/iKQLWEAIZAsSxwOi3WT6GRZs9gGREwXnOJ8qZrxjCw3M1VYMtGsvVNqa7o2S8q5RfExkfC5ZkgHkFopVBocBDzR4Hy0KeOeU19XbPv5/l2vWsYGj2XTm/mJDGbc9b/ZyH8mfOgDuJWTXInRIlI2JTleyW" + ], + "keyUse": ["ENC"], + "certificate": [ + "MIIClzCCAX8CBgGNUUfGLTANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTI0MDEyODE4MTI1NFoXDTM0MDEyODE4MTQzNFowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMkkAPUXhFhzbIClv5w2YE+aj3BJ2AbkGlgVQ1R1KsvQJVJoPH80uTsR57Ij2pMUsxY1q5VgIeXsCJ8EKIB1ag62dGA8IVLM7WvmXXO2SzA8RWWyJvRiVS55RCxTKNggrDQdwwMsqHpoH6g+MrdK3Pi5NSyDSqAGN1Wz4NVZv5ReMUtT7mq7f7BUBy8oDc5cSuXDX2QdEXW+WYSYSj3sErilDBFgLB8gG1LsMt0aa2sBHvoMK9I6P1SosD/iRlt7LtR1Edgl2KI/KUFleQYSqB2SR5KEF+1LBCEJjMkHd2mTSYHguXsq2FqSBW6We2L7E0so5Q50xs4Jj0SKg6bzIBECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZi9jJ57xn7Vj+WPy6p770WIko+TYDnjnlfp5tglwApbVWYNmFn12+gzA1aEpOZad358UlC7fsA8I31tG7sTchtgIZTxt5EcNCm07luL4AzxU9JirT3GtPWcAPydMhb/FNpvnKfEyVO7oceOR/fT4H5ZEDinCelNuVFvS9Bulp8EPbo1mKBI42Lu0UqoC++2AkPPtRk1SKcLwni/YO+sky41h2aVEPxzBwQHUn9UvrOu5u2i330SehwmfO4UtytnGb9lYkaSM2eLbu061kb0OxLUrH/7iqWsiLaapG0T1QgnOlzsAv+R7hWapWq+dnIzxKfEU2icYW8NDGIOrYIAoOg==" + ], + "priority": ["100"], + "algorithm": ["RSA-OAEP"] + } + }, + { + "id": "379e6b02-1875-4d03-b26d-f9767fbf0878", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEowIBAAKCAQEAlfnWdtZnfwhTPNbetIW/6DIjixFeXYOI0sAzyICa0fRxX9P6SCJVmm1wM+Ix8PoH6VLaafZQ4LVgm3jHokvwpglhcezjVmsADSSGBnZXd6k5sbmnMrO0YsjYh0yY5hef6mTJAcLdN5Z269Z7JJ29Q5D2XILPKBxm9vIHR440xcdofuahHJFzoU5EjrI9nt039keSyL/9a8Z3Q6A2pgzW4jgwkdVMmXPKICDQECSounGMEp1w1hm9YBhKR+TJyaDyUwaT2CAumQGaJtC0ylXTrc40ZNMa1402S8S+x8JW49kHsRofpIf2eQki5cqeZ+GbIM4tWVBfrK20DHSWSNaP6wIDAQABAoIBAA9lsKSktcgZ9Qm9pEGeVanOCU4ha1xoa6dPakGUdSXxqvjD94xo/KbibhmK3cZitRCO8V4y6/IV96QejfMAnuU8+KR3ReO9D9VJlvpE3g1VR1doEF1T4RofNUkMDnUuSbxeZTCUGUgNX4ouNphMxX3mp78EWFDtkY3wIoUweVnbSyl05VLU7ZNhxIUIrX1ikDR3enq7i5fKg2qHZVWRETtOH6DeY8kXuJoavKrXQXVGe8utV54UING6rvSvi7q9h8FRMoYlQQiLqD96KyyScvD5cYkzn/R0bh8Fj7pJjiIfEVye2iblFXywwSzhAoRh7srnQ85JWW6tBISPna6+DkECgYEAzI+0auXHLwiM/eQbJP0MUc0ksiEIgisqIXZv2LeNfYmjpoY8ruY040BBwKW8yCIBRhF5d7Eap6lKkmppP0wH93eX10sDpbytgP+wKVprQ9VAiOz/Jeauami6G73xRJ09Y3eUNfTpNGi1rP6LrVFEwBAGoGnlFc5S+fYI+R2WcksCgYEAu7BJrt/xM3NadhTwkLjYgvfgRShK9/yJ5jr2+GU+Ku0HvRSp4Kv40PcoEgga7srke3F0AJ+8h6xluDGMuRAv1ZNsDciQS5xk6SGOZxX1qkKxXWTv9EdDRalY78Y7igm8cicTB2/A/J2aGHVi6E9GRuzio53DFrskkIEgnKki1OECgYAW2q9Tihx42sG10/hcZ5EqynqhFCO5N01bs7nHQqqiLfCEuFarS7j48sLl3R58VaBCzcz+XGEX/kgAl7buhXYWZdwnB5B+wde4o9hwplN5nqe5JJFVELLH3K9+TfhYrCChrkh2xsgPPOufSkkcsgm2U5QJ9ArOJuEKYDycaHvYhQKBgQC4FriupUfWxxW7K96olCUIkEeQjWy6vyAiiUMjb2oCgGpOsq3Mh+CMVV8gEIMaJDbmPOkik4o/KIC8oRz9hzrjqK0a99VP1B3F6/vYcFl8nd2JVS9l0V/SO7/Xrf9H7TTAYkLv0FThcVNrGffhE6BCgKXrBRoMeZHrijuKbPqVAQKBgGHh2AVzq3obkQdswyGW1tF5ZODQTMRpUdckgeABnRshN2lP+WJmOHdqlsPtGv9GRuTc0kTMegT7RQpQY7Sq356cesMtiNDExpQJ7A1UdPt7KAA2kJeWjTFBYLOw1KeyemA48fBnO63rb3gik9+pFJGMLdxvveEsswc3FD8cB4IA" + ], + "keyUse": ["SIG"], + "certificate": [ + "MIIClzCCAX8CBgGNUUfF0TANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTI0MDEyODE4MTI1NFoXDTM0MDEyODE4MTQzNFowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJX51nbWZ38IUzzW3rSFv+gyI4sRXl2DiNLAM8iAmtH0cV/T+kgiVZptcDPiMfD6B+lS2mn2UOC1YJt4x6JL8KYJYXHs41ZrAA0khgZ2V3epObG5pzKztGLI2IdMmOYXn+pkyQHC3TeWduvWeySdvUOQ9lyCzygcZvbyB0eONMXHaH7moRyRc6FORI6yPZ7dN/ZHksi//WvGd0OgNqYM1uI4MJHVTJlzyiAg0BAkqLpxjBKdcNYZvWAYSkfkycmg8lMGk9ggLpkBmibQtMpV063ONGTTGteNNkvEvsfCVuPZB7EaH6SH9nkJIuXKnmfhmyDOLVlQX6yttAx0lkjWj+sCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAJyWjj4PxsVYeNh332WCXqZt0cCTDN6VYgK4E/hWbocJpcAeveXS86+y0C4pr4q6d0vJsrscoSKnSzumb3acDUtPTwOEmopvcrEjKI3zzANI3rOtEMud2fGSRlgY3zZYBLO9m0GNxGOq7KJQoZI2TTFPCFtKiXKTgPbGRep4yS8epVg3QjXIBrxEdNDByUmkhUdb9/VsR+SFXbXbUeyW6PeE7r1oo1WCM4sr69InrjqRBSE63Dtq1VvHCi6P9Gh84nALSpDf+P4QjC2T3a7P8kHPInxx2cBXDyO1UVb/4OAIlC29gjAeYmA3w55XfUnL23SujlHtA/8OyD/XCtlskeQ==" + ], + "priority": ["100"] + } + }, + { + "id": "cce22945-2ed8-438d-b352-1e1d82253ff3", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "kid": ["2d921760-18c5-49b9-88ba-f451c0bede83"], + "secret": [ + "y7QepQM2M2PmZhrBztSDkNkEd8bqpVCrdvm4FhSz_zJFwDIw7Aqk9pU2Nnac12D9MYhPN8MQmCrBYzaag5rJAw" + ], + "priority": ["100"], + "algorithm": ["HS256"] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "f8f0a449-8ba7-4421-8a43-d89dd4b7ce15", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "ac41705e-a3fa-4bce-9d54-b9405c88d376", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "3a93b3d7-4105-401e-8e43-16f69800ae5b", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "050f142a-d7e7-4218-9c4e-4dd40ba61aa4", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "8eefdb78-46d4-47b8-827a-d06987390ad3", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "e3a5685e-3f0c-4306-8fcd-ab871d80769b", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "7a3893a2-07a1-4bb2-ae77-05ae631cd89b", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "b9a39f14-251f-4f09-b93a-557d35d04521", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "63885a84-74dc-4bd4-bbaf-c616ae5e9659", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "127e318a-dcc9-4837-a8f9-ec502878a531", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b89a2c05-d681-4a1d-833b-a7085b0350d6", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "4ab24967-97e2-43a2-93ae-4e8ed8591937", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "33393e12-1a44-479d-af7f-ae69080cfa93", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "818cb609-9f1d-441a-9a55-e4d523a3b8bc", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "1318f429-4b06-4deb-b68d-070bace5bed2", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "4f6bc6e3-7b19-43ca-ae79-2814ace9610f", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "937aecb9-cdf6-46af-8025-a99d13fa6c0c", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "2d5f0643-e7de-496c-a6db-fba88ccdafe0", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "717d07e7-ec5b-4729-8138-e6104ede0b68", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "c47cafae-8394-4e29-8b70-e7836f561bb0", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaExpiresIn": "120", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DeviceCodeLifespan": "600", + "oauth2DevicePollingInterval": "5", + "parRequestUriLifespan": "60", + "cibaInterval": "5", + "realmReusableOtpCode": "false" + }, + "keycloakVersion": "23.0.4", + "userManagedAccessAllowed": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } +} diff --git a/plugins/http_get/Cargo.toml b/plugins/http_get/Cargo.toml index df403504..30d422a8 100644 --- a/plugins/http_get/Cargo.toml +++ b/plugins/http_get/Cargo.toml @@ -12,3 +12,4 @@ serde = { workspace = true } async-trait = { workspace = true } conductor_common = { path = "../../libs/common" } schemars = { workspace = true } +urlencoding = "2.1.3" diff --git a/plugins/http_get/src/plugin.rs b/plugins/http_get/src/plugin.rs index 4672ff42..5957d7b8 100644 --- a/plugins/http_get/src/plugin.rs +++ b/plugins/http_get/src/plugin.rs @@ -31,6 +31,7 @@ impl Plugin for HttpGetPlugin { if ctx.downstream_http_request.method == Method::GET { let (_, accept, result) = extract_graphql_from_get_request(&ctx.downstream_http_request); + println!("result: {:?}", result); match result { Ok(gql_request) => match ParsedGraphQLRequest::create_and_parse(gql_request) { Ok(parsed) => { @@ -117,16 +118,29 @@ pub fn extract_graphql_from_get_request( None => None, }; - return ( - content_type, - accept, - Ok(GraphQLRequest { - operation: operation.to_string(), - operation_name: operation_name.map(ToString::to_string), - variables, - extensions, - }), - ); + let query_param_decoded = urlencoding::decode(operation); + + match query_param_decoded { + Ok(query) => { + return ( + content_type, + accept, + Ok(GraphQLRequest { + operation: query.to_string(), + operation_name: operation_name.map(ToString::to_string), + variables, + extensions, + }), + ); + } + Err(_) => { + return ( + content_type, + accept, + Err(ExtractGraphQLOperationError::InvalidQueryParameterEncoding), + ) + } + } } None => { return ( diff --git a/plugins/jwt_auth/Cargo.toml b/plugins/jwt_auth/Cargo.toml index 1c5e0280..ab61011e 100644 --- a/plugins/jwt_auth/Cargo.toml +++ b/plugins/jwt_auth/Cargo.toml @@ -20,6 +20,7 @@ jsonwebtoken = "9.2.0" humantime-serde = "1.1.1" cookie = { version = "0.18.0", features = ["percent-encode"] } futures = { workspace = true } +web-time = "1.0.0" [dev-dependencies] lazy_static = { version = "1.4.0" } diff --git a/plugins/jwt_auth/src/config.rs b/plugins/jwt_auth/src/config.rs index 06ee8fbc..9caa47a3 100644 --- a/plugins/jwt_auth/src/config.rs +++ b/plugins/jwt_auth/src/config.rs @@ -122,6 +122,7 @@ pub enum JwksProviderSourceConfig { serialize_with = "humantime_serde::serialize", default = "default_polling_interval" )] + #[schemars(with = "String")] /// Duration after which the cached JWKS should be expired. If not specified, the default value will be used. cache_duration: Option, /// If set to `true`, the JWKS will be fetched on startup and cached. In case of invalid JWKS, the error will be ignored and the plugin will try to fetch again when server receives the first request. diff --git a/plugins/jwt_auth/src/jwks_provider.rs b/plugins/jwt_auth/src/jwks_provider.rs index 7df76cc2..5c26acc4 100644 --- a/plugins/jwt_auth/src/jwks_provider.rs +++ b/plugins/jwt_auth/src/jwks_provider.rs @@ -1,8 +1,10 @@ use std::{ sync::{Arc, RwLock}, - time::{Duration, SystemTime}, + time::Duration, }; +use web_time::SystemTime; + use jsonwebtoken::jwk::JwkSet; use crate::config::JwksProviderSourceConfig; @@ -45,6 +47,8 @@ impl JwksProvider { } => { // @expected: if initiating an http client fails, then we have to exit. let client = wasm_polyfills::create_http_client().build().unwrap(); + tracing::debug!("loading jwks for a remote source: {}", url); + let response_text = client .get(url) .send() @@ -83,11 +87,19 @@ impl JwksProvider { #[cfg(target_arch = "wasm32")] pub fn can_prefetch(&self) -> bool { - use tracing::error; - - error!("jwks prefetching is not supported on wasm32, ignoring"); + match &self.config { + JwksProviderSourceConfig::Remote { prefetch, .. } => match prefetch { + Some(prefetch) => { + if prefetch { + tracing::warn!("jwks prefetching is not supported on wasm32, ignoring"); + } - false + false + } + None => false, + }, + JwksProviderSourceConfig::Local { .. } => false, + } } #[cfg(not(target_arch = "wasm32"))] diff --git a/plugins/telemetry/src/config.rs b/plugins/telemetry/src/config.rs index 05d11d9b..bc8c6ef9 100644 --- a/plugins/telemetry/src/config.rs +++ b/plugins/telemetry/src/config.rs @@ -100,8 +100,8 @@ pub enum TelemetryTarget { #[serde(rename = "jaeger")] #[schemars(title = "Jaeger")] Jaeger { - /// The UDP endpoint of the Jaeger backend. The format is based on hostname and port only, e.g. `127.0.0.1:6831`. #[serde(default = "default_jaeger_endpoint")] + /// The UDP endpoint of the Jaeger backend. The format is based on hostname and port only, e.g. `127.0.0.1:6831`. endpoint: SocketAddr, }, }