From 3787c23cbd049ceb10da8049dcea5ef0eb8ae9c0 Mon Sep 17 00:00:00 2001 From: Matheus Zaniolo Date: Wed, 20 May 2026 17:02:26 +0200 Subject: [PATCH 1/3] fix: Correct the url for get_password_complexity_policy --- src/v2/management/mod.rs | 2 +- tests/e2e.rs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/v2/management/mod.rs b/src/v2/management/mod.rs index 8b141ce..e8f99c6 100644 --- a/src/v2/management/mod.rs +++ b/src/v2/management/mod.rs @@ -570,7 +570,7 @@ impl Zitadel { ) -> Result { let request = self .client - .get(self.make_url("/policies/password/complexity")?) + .get(self.make_url("management/v1/policies/password/complexity")?) .chain_opt(org_id, |req, org_id| req.header(HEADER_ZITADEL_ORGANIZATION_ID, org_id)) .build()?; Ok(self.send_request(request).await?) diff --git a/tests/e2e.rs b/tests/e2e.rs index 5b33845..40f1aa3 100644 --- a/tests/e2e.rs +++ b/tests/e2e.rs @@ -1469,3 +1469,20 @@ async fn test_e2e_organization_scoped_operations() -> Result<()> { Ok(()) } + +#[test(tokio::test)] +#[test_log(default_log_filter = "debug")] +async fn test_e2e_get_password_complexity_policy() -> Result<()> { + let zitadel = mk_zitadel_client().await?; + + let response = zitadel.get_password_complexity_policy(None).await?; + let policy = response.policy().expect("password complexity policy should be present"); + + assert!(policy.min_length().is_some()); + assert!(policy.has_uppercase().is_some()); + assert!(policy.has_lowercase().is_some()); + assert!(policy.has_number().is_some()); + assert!(policy.has_symbol().is_some()); + + Ok(()) +} From 654564dbab469ed5f285b172b703ce686b19dc01 Mon Sep 17 00:00:00 2001 From: Matheus Zaniolo Date: Fri, 22 May 2026 12:20:22 +0200 Subject: [PATCH 2/3] feat: Add create_machine_user --- src/v2/management/mod.rs | 17 +++++++++++++++ tests/e2e.rs | 46 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/v2/management/mod.rs b/src/v2/management/mod.rs index e8f99c6..a02c15e 100644 --- a/src/v2/management/mod.rs +++ b/src/v2/management/mod.rs @@ -29,6 +29,23 @@ impl Zitadel { Ok(self.send_request(request).await?) } + /// Create a new user with the type machine for your API, service or device. + /// [Docs](https://zitadel.com/docs/apis/resources/mgmt/management-service-add-machine-user) + pub async fn create_machine_user( + &self, + org_id: Option, + body: V1AddMachineUserRequest, + ) -> Result { + let request = self + .client + .post(self.make_url("management/v1/users/machine")?) + .chain_opt(org_id, |req, org_id| req.header(HEADER_ZITADEL_ORGANIZATION_ID, org_id)) + .json(&body) + .build()?; + + Ok(self.send_request(request).await?) + } + /// Create actions. [Docs](https://zitadel.com/docs/apis/resources/mgmt/management-service-create-action) pub async fn create_action( &self, diff --git a/tests/e2e.rs b/tests/e2e.rs index 40f1aa3..d7ac4a3 100644 --- a/tests/e2e.rs +++ b/tests/e2e.rs @@ -795,6 +795,52 @@ async fn test_e2e_create_human_user() -> Result<()> { Ok(()) } +#[test(tokio::test)] +#[test_log(default_log_filter = "debug")] +async fn test_e2e_create_user_human_and_machine() -> Result<()> { + let zitadel = mk_zitadel_client().await?; + + let suffix = Alphanumeric.sample_string(&mut rand::rng(), 12); + + let human_id = create_user(&zitadel, &format!("Jane{suffix}"), "Doe").await?; + let fetched_human = zitadel + .get_user_by_id(&human_id) + .await? + .user() + .cloned() + .expect("fetched human user missing from response"); + assert!(fetched_human.human().is_some(), "expected human payload on created human user"); + assert!(fetched_human.machine().is_none(), "did not expect machine payload on human user"); + + let machine_username = format!("bot-{suffix}"); + let machine_req = V1AddMachineUserRequest::new(machine_username.clone(), "CI Bot".to_owned()) + .with_description("Created by e2e test".to_owned()); + let machine_resp = zitadel.create_machine_user(None, machine_req).await?; + let machine_id = machine_resp.user_id().expect("machine user id missing from response").clone(); + + let fetched_machine = zitadel + .get_user_by_id(&machine_id) + .await? + .user() + .cloned() + .expect("fetched machine user missing from response"); + assert_eq!(fetched_machine.username().map(String::as_str), Some(machine_username.as_str())); + assert!( + fetched_machine.machine().is_some(), + "expected machine payload on created machine user" + ); + assert!(fetched_machine.human().is_none(), "did not expect human payload on machine user"); + let machine = fetched_machine.machine().unwrap(); + assert_eq!(machine.name().map(String::as_str), Some("CI Bot")); + assert_eq!(machine.description().map(String::as_str), Some("Created by e2e test")); + + // tear_down only cleans up human users; remove the machine one explicitly. + zitadel.delete_user(&machine_id).await?; + tear_down(&zitadel).await; + + Ok(()) +} + #[test(tokio::test)] #[test_log(default_log_filter = "debug")] async fn test_e2e_list_human_users() -> Result<()> { From 0b61b596d9256b96a383a99bd727c2910d11c390 Mon Sep 17 00:00:00 2001 From: Matheus Zaniolo Date: Wed, 20 May 2026 17:04:07 +0200 Subject: [PATCH 3/3] release: v0.12.2 --- CHANGELOG.md | 14 ++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16b22a5..a37a96d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,20 @@ SPDX-License-Identifier: Apache-2.0 All notable changes to this project will be documented in this file. +## [0.12.2] - 2026-05-22 + +### Features + +- Add `create_machine_user` + +### Bug Fixes + +- Correct the url for get_password_complexity_policy + +### Release + +- V0.12.2 + ## [0.12.1] - 2026-05-05 ### Features diff --git a/Cargo.lock b/Cargo.lock index 3d10f4a..79fa08c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -734,7 +734,7 @@ dependencies = [ [[package]] name = "famedly-zitadel-rust-client" -version = "0.12.1" +version = "0.12.2" dependencies = [ "anyhow", "anyhow_ext", diff --git a/Cargo.toml b/Cargo.toml index 8d3555c..971cba4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "famedly-zitadel-rust-client" description = "HTTP client for Zitadel IdP built by Famedly" -version = "0.12.1" +version = "0.12.2" authors = [] edition = "2024" resolver = "2"