Skip to content

Commit f3eb0af

Browse files
authored
fix: Construction of OIDC endpoint when rootPath has a trailing slash (#547)
* fix: Construction of OIDC endpoint when rootPath has a trailing slash * changelog * changelog
1 parent d07f9bd commit f3eb0af

File tree

6 files changed

+75
-38
lines changed

6 files changed

+75
-38
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
- Pass gitsync credentials through properly and use a fine-grained access token ([#489]).
2828
- Failing to parse one `AirflowCluster`/`AuthenticationClass` should no longer cause the whole operator to stop functioning ([#520]).
2929
- BREAKING: Use distinct ServiceAccounts for the Stacklets, so that multiple Stacklets can be deployed in one namespace. Existing Stacklets will use the newly created ServiceAccounts after restart ([#545]).
30+
- Fix OIDC endpoint construction in case the `rootPath` does not have a trailing slash ([#547]).
3031

3132
[#488]: https://github.com/stackabletech/airflow-operator/pull/488
3233
[#489]: https://github.com/stackabletech/airflow-operator/pull/489
@@ -37,6 +38,7 @@
3738
[#524]: https://github.com/stackabletech/airflow-operator/pull/524
3839
[#530]: https://github.com/stackabletech/airflow-operator/pull/530
3940
[#545]: https://github.com/stackabletech/airflow-operator/pull/545
41+
[#547]: https://github.com/stackabletech/airflow-operator/pull/547
4042

4143
## [24.7.0] - 2024-07-24
4244

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.nix

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ tracing = "0.1"
3030

3131
# [patch."https://github.com/stackabletech/operator-rs.git"]
3232
# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" }
33+
# stackable-operator = { path = "../operator-rs/crates/stackable-operator" }

rust/operator-binary/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ tracing.workspace = true
2626
indoc.workspace = true
2727

2828
[build-dependencies]
29-
3029
built.workspace = true
3130

3231
[dev-dependencies]
32+
rstest.workspace = true
3333
serde_yaml.workspace = true

rust/operator-binary/src/config.rs

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ pub enum Error {
2626
FailedToCreateLdapEndpointUrl {
2727
source: stackable_operator::commons::authentication::ldap::Error,
2828
},
29+
30+
#[snafu(display("invalid OIDC endpoint"))]
31+
InvalidOidcEndpoint {
32+
source: stackable_operator::commons::authentication::oidc::Error,
33+
},
34+
35+
#[snafu(display("invalid well-known OIDC configuration URL"))]
36+
InvalidWellKnownConfigUrl {
37+
source: stackable_operator::commons::authentication::oidc::Error,
38+
},
2939
}
3040

3141
pub fn add_airflow_config(
@@ -78,7 +88,7 @@ fn append_authentication_config(
7888
}
7989

8090
if !oidc_providers.is_empty() {
81-
append_oidc_config(config, &oidc_providers);
91+
append_oidc_config(config, &oidc_providers)?;
8292
}
8393

8494
config.insert(
@@ -192,7 +202,7 @@ fn append_oidc_config(
192202
&oidc::AuthenticationProvider,
193203
&oidc::ClientAuthenticationOptions<()>,
194204
)],
195-
) {
205+
) -> Result<(), Error> {
196206
// Debatable: AUTH_OAUTH or AUTH_OID
197207
// Additionally can be set via config
198208
config.insert(
@@ -217,6 +227,13 @@ fn append_oidc_config(
217227

218228
let oauth_providers_config_entry = match oidc_provider {
219229
oidc::IdentityProviderHint::Keycloak => {
230+
let endpoint_url = oidc.endpoint_url().context(InvalidOidcEndpointSnafu)?;
231+
let mut api_base_url = endpoint_url.as_str().trim_end_matches('/').to_owned();
232+
api_base_url.push_str("/protocol/");
233+
let well_known_config_url = oidc
234+
.well_known_config_url()
235+
.context(InvalidWellKnownConfigUrlSnafu)?;
236+
220237
formatdoc!(
221238
"
222239
{{ 'name': 'keycloak',
@@ -228,11 +245,10 @@ fn append_oidc_config(
228245
'client_kwargs': {{
229246
'scope': '{scopes}'
230247
}},
231-
'api_base_url': '{url}/protocol/',
232-
'server_metadata_url': '{url}/.well-known/openid-configuration',
248+
'api_base_url': '{api_base_url}',
249+
'server_metadata_url': '{well_known_config_url}',
233250
}},
234251
}}",
235-
url = oidc.endpoint_url().unwrap(),
236252
scopes = scopes.join(" "),
237253
)
238254
}
@@ -251,12 +267,15 @@ fn append_oidc_config(
251267
joined_oauth_providers_config = oauth_providers_config.join(",\n")
252268
),
253269
);
270+
271+
Ok(())
254272
}
255273

256274
#[cfg(test)]
257275
mod tests {
258276
use crate::config::add_airflow_config;
259-
use indoc::indoc;
277+
use indoc::formatdoc;
278+
use rstest::rstest;
260279
use stackable_airflow_crd::authentication::{
261280
default_sync_roles_at, default_user_registration, AirflowAuthenticationClassResolved,
262281
AirflowClientAuthenticationDetailsResolved, FlaskRolesSyncMoment,
@@ -339,12 +358,15 @@ mod tests {
339358
]), result);
340359
}
341360

342-
#[test]
343-
fn test_oidc_config() {
344-
let oidc_provider_yaml1 = r#"
345-
hostname: my.keycloak1.server
361+
#[rstest]
362+
#[case("/realms/sdp")]
363+
#[case("/realms/sdp/")]
364+
#[case("/realms/sdp/////")]
365+
fn test_oidc_config(#[case] root_path: &str) {
366+
let oidc_provider_yaml1 = formatdoc!(
367+
"hostname: my.keycloak1.server
346368
port: 12345
347-
rootPath: my-root-path
369+
rootPath: {root_path}
348370
tls:
349371
verification:
350372
server:
@@ -356,8 +378,9 @@ mod tests {
356378
- email
357379
- profile
358380
provider_hint: Keycloak
359-
"#;
360-
let deserializer = serde_yaml::Deserializer::from_str(oidc_provider_yaml1);
381+
"
382+
);
383+
let deserializer = serde_yaml::Deserializer::from_str(&oidc_provider_yaml1);
361384
let oidc_provider1: oidc::AuthenticationProvider =
362385
serde_yaml::with::singleton_map_recursive::deserialize(deserializer).unwrap();
363386

@@ -399,41 +422,47 @@ mod tests {
399422
let mut result = BTreeMap::new();
400423
add_airflow_config(&mut result, &authentication_config).expect("Ok");
401424

402-
assert_eq!(BTreeMap::from([
403-
("AUTH_ROLES_SYNC_AT_LOGIN".into(), "false".into()),
404-
("AUTH_TYPE".into(), "AUTH_OAUTH".into()),
405-
("AUTH_USER_REGISTRATION".into(), "true".into()),
406-
("AUTH_USER_REGISTRATION_ROLE".into(), "Admin".into()),
407-
("OAUTH_PROVIDERS".into(), indoc!("
425+
assert_eq!(
426+
BTreeMap::from([
427+
("AUTH_ROLES_SYNC_AT_LOGIN".into(), "false".into()),
428+
("AUTH_TYPE".into(), "AUTH_OAUTH".into()),
429+
("AUTH_USER_REGISTRATION".into(), "true".into()),
430+
("AUTH_USER_REGISTRATION_ROLE".into(), "Admin".into()),
431+
(
432+
"OAUTH_PROVIDERS".into(),
433+
formatdoc! {"
408434
[
409-
{ 'name': 'keycloak',
435+
{{ 'name': 'keycloak',
410436
'icon': 'fa-key',
411437
'token_key': 'access_token',
412-
'remote_app': {
438+
'remote_app': {{
413439
'client_id': os.environ.get('OIDC_A96BCC4FA49835D2_CLIENT_ID'),
414440
'client_secret': os.environ.get('OIDC_A96BCC4FA49835D2_CLIENT_SECRET'),
415-
'client_kwargs': {
441+
'client_kwargs': {{
416442
'scope': 'openid email profile roles'
417-
},
418-
'api_base_url': 'https://my.keycloak1.server:12345/my-root-path/protocol/',
419-
'server_metadata_url': 'https://my.keycloak1.server:12345/my-root-path/.well-known/openid-configuration',
420-
},
421-
},
422-
{ 'name': 'keycloak',
443+
}},
444+
'api_base_url': 'https://my.keycloak1.server:12345/realms/sdp/protocol/',
445+
'server_metadata_url': 'https://my.keycloak1.server:12345/realms/sdp/.well-known/openid-configuration',
446+
}},
447+
}},
448+
{{ 'name': 'keycloak',
423449
'icon': 'fa-key',
424450
'token_key': 'access_token',
425-
'remote_app': {
451+
'remote_app': {{
426452
'client_id': os.environ.get('OIDC_3A305E38C3B561F3_CLIENT_ID'),
427453
'client_secret': os.environ.get('OIDC_3A305E38C3B561F3_CLIENT_SECRET'),
428-
'client_kwargs': {
454+
'client_kwargs': {{
429455
'scope': 'openid'
430-
},
431-
'api_base_url': 'http://my.keycloak2.server//protocol/',
432-
'server_metadata_url': 'http://my.keycloak2.server//.well-known/openid-configuration',
433-
},
434-
}
456+
}},
457+
'api_base_url': 'http://my.keycloak2.server/protocol/',
458+
'server_metadata_url': 'http://my.keycloak2.server/.well-known/openid-configuration',
459+
}},
460+
}}
435461
]
436-
").into())
437-
]), result);
462+
"}
463+
)
464+
]),
465+
result
466+
);
438467
}
439468
}

0 commit comments

Comments
 (0)