diff --git a/Cargo.lock b/Cargo.lock index 4554beb4..d53a9b02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1900,7 +1900,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pg_doorman" -version = "3.11.1" +version = "3.11.2" dependencies = [ "ahash", "arc-swap", diff --git a/Cargo.toml b/Cargo.toml index 902c2db9..a2bbe6ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ edition = "2021" license = "MIT" name = "pg_doorman" rust-version = "1.87.0" -version = "3.11.1" +version = "3.11.2" [profile.release] codegen-units = 1 diff --git a/documentation/en/src/changelog.md b/documentation/en/src/changelog.md index 18f3db64..37bf8159 100644 --- a/documentation/en/src/changelog.md +++ b/documentation/en/src/changelog.md @@ -1,5 +1,19 @@ # Changelog +### 3.11.2 + +#### Talos routes `s2i|`-prefixed clients through a service pool + +For `user=talos`, the pool-user selection now also understands +`s2i|`-prefixed `clientId`s. In addition to `clientId`, `srv-` +and the max token role (`owner`, `read_write`, `read_only`), pg_doorman +checks a pool user named `srv-` where `service-name` is +the part of the `clientId` after the `|` separator. For a `clientId` of +`s2i|test-service` the pool user checked is `srv-test-service`. + +Selection order: `clientId`, `srv-`, `srv-` +(for `s2i|`-prefixed `clientId`), then the max token role. + ### 3.11.1 #### Pool-level `sync_server_parameters` override diff --git a/src/auth/talos.rs b/src/auth/talos.rs index 1cd7af32..1721fac4 100644 --- a/src/auth/talos.rs +++ b/src/auth/talos.rs @@ -117,10 +117,18 @@ pub fn resolve_talos_user( source: TalosUserSource::Personal, }; } - let service_name = format!("srv-{client_id}"); - if pool_exists(pool_name, &service_name) { + let service_name_by_client_id = format!("srv-{client_id}"); + if pool_exists(pool_name, &service_name_by_client_id) { return TalosResolution { - username: service_name, + username: service_name_by_client_id, + source: TalosUserSource::ServicePool, + }; + } + let parsed_service_name = client_id.split("|").nth(1).unwrap_or(client_id); + let service_account = format!("srv-{parsed_service_name}"); + if pool_exists(pool_name, &service_account) { + return TalosResolution { + username: service_account, source: TalosUserSource::ServicePool, }; } @@ -615,6 +623,38 @@ mod tests { assert_eq!(resolved.username, "srv-billing-api"); } + #[test] + fn resolve_service_pool_from_prefixed_client_id() { + let resolved = + resolve_talos_user("billing_db", "s2i|test-service", Role::Owner, |db, user| { + db == "billing_db" && user == "srv-test-service" + }); + assert_eq!(resolved.source, TalosUserSource::ServicePool); + assert_eq!(resolved.username, "srv-test-service"); + } + + #[test] + fn resolve_service_pool_from_prefixed_client_id_respects_max_role() { + let resolved = resolve_talos_user( + "billing_db", + "s2i|test-service", + Role::ReadWrite, + |db, user| db == "billing_db" && user == "srv-test-service", + ); + assert_eq!(resolved.source, TalosUserSource::ServicePool); + assert_eq!(resolved.username, "srv-test-service"); + } + + #[test] + fn resolve_falls_back_to_max_role_when_service_account_missing() { + let resolved = + resolve_talos_user("billing_db", "s2i|test-service", Role::ReadWrite, |_, _| { + false + }); + assert_eq!(resolved.source, TalosUserSource::MaxRole); + assert_eq!(resolved.username, "read_write"); + } + #[test] fn resolve_falls_through_to_max_role() { let resolved = resolve_talos_user("billing_db", "billing-api", Role::Owner, |_, _| false);