From 199a7f13475995fde370b3f364f7ba4034826965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralf=20B=C3=B6hler?= Date: Fri, 30 Aug 2024 08:57:25 +0200 Subject: [PATCH 1/6] feat(roles): Add roles module and endpoint --- src/constants.rs | 5 ++ src/lib.rs | 9 +++ src/roles/mod.rs | 173 +++++++++++++++++++++++++++++++++++++++++ src/roles/models.rs | 121 +++++++++++++++++++++++++++++ src/roles/roles.rs | 185 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 493 insertions(+) create mode 100644 src/roles/mod.rs create mode 100644 src/roles/models.rs create mode 100644 src/roles/roles.rs diff --git a/src/constants.rs b/src/constants.rs index 268a9df..ce6ef9b 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -89,6 +89,11 @@ pub const PROVISIONING_CUSTOMER_ATTRIBUTES: &str = "customerAttributes"; pub const PROVISIONING_CUSTOMER_USERS: &str = "users"; pub const PROVISIONING_TOKEN_HEADER: &str = "X-Sds-Service-Token"; +// ROLES +pub const ROLES_BASE: &str = "roles"; +pub const ROLES_GROUPS: &str = "groups"; +pub const ROLES_USERS: &str = "users"; + // SETTINGS pub const SETTINGS_BASE: &str = "settings"; pub const SETTINGS_KEYPAIR: &str = "keypair"; diff --git a/src/lib.rs b/src/lib.rs index 7b3ab5c..eebfe1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -434,6 +434,7 @@ use nodes::NodesEndpoint; use provisioning::ProvisioningEndpoint; use public::{PublicEndpoint, SystemInfo}; use reqwest::Url; +use roles::RolesEndpoint; use settings::SettingsEndpoint; use shares::SharesEndpoint; use system::SystemEndpoint; @@ -473,6 +474,7 @@ pub mod models; pub mod nodes; pub mod provisioning; pub mod public; +pub mod roles; pub mod settings; pub mod shares; pub mod system; @@ -501,6 +503,7 @@ pub struct Dracoon { pub users: UsersEndpoint, pub public: PublicEndpoint, pub provisioning: ProvisioningEndpoint, + pub roles: RolesEndpoint, } impl GetClient for Dracoon { @@ -616,6 +619,7 @@ impl DracoonBuilder { let system_endpoint = SystemEndpoint::new(Arc::clone(&dracoon)); let nodes_endpoint = NodesEndpoint::new(Arc::clone(&dracoon)); let eventlog_endpoint = EventlogEndpoint::new(Arc::clone(&dracoon)); + let roles_endpoint = RolesEndpoint::new(Arc::clone(&dracoon)); Ok(Dracoon { client: dracoon, @@ -635,6 +639,7 @@ impl DracoonBuilder { system: system_endpoint, users: users_endpoint, groups: groups_endpoint, + roles: roles_endpoint, }) } @@ -653,6 +658,7 @@ impl DracoonBuilder { let system_endpoint = SystemEndpoint::new(Arc::clone(&dracoon)); let nodes_endpoint = NodesEndpoint::new(Arc::clone(&dracoon)); let eventlog_endpoint = EventlogEndpoint::new(Arc::clone(&dracoon)); + let roles_endpoint = RolesEndpoint::new(Arc::clone(&dracoon)); Ok(Dracoon { client: dracoon, @@ -672,6 +678,7 @@ impl DracoonBuilder { system: system_endpoint, users: users_endpoint, groups: groups_endpoint, + roles: roles_endpoint, }) } } @@ -699,6 +706,7 @@ impl Dracoon { let system_endpoint = SystemEndpoint::new(Arc::clone(&connected_client)); let nodes_endpoint = NodesEndpoint::new(Arc::clone(&connected_client)); let eventlog_endpoint = EventlogEndpoint::new(Arc::clone(&connected_client)); + let roles_endpoint = RolesEndpoint::new(Arc::clone(&connected_client)); let mut dracoon = Dracoon { client: connected_client, @@ -712,6 +720,7 @@ impl Dracoon { user: user_endpoint, public: public_endpoint, provisioning: provisioning_endpoint, + roles: roles_endpoint, nodes: nodes_endpoint, shares: shares_endpoint, settings: settings_endpoint, diff --git a/src/roles/mod.rs b/src/roles/mod.rs new file mode 100644 index 0000000..901b424 --- /dev/null +++ b/src/roles/mod.rs @@ -0,0 +1,173 @@ +use async_trait::async_trait; + +mod models; +#[allow(clippy::module_inception)] +mod roles; + +pub use models::*; + +use crate::{models::ListAllParams, DracoonClientError}; + +#[async_trait] +pub trait Roles { + /// Get a list of all roles. + /// ```no_run + /// # use dco3::{Dracoon, auth::OAuth2Flow, Roles}; + /// # #[tokio::main] + /// # async fn main() { + /// # let dracoon = Dracoon::builder() + /// # .with_base_url("https://dracoon.team") + /// # .with_client_id("client_id") + /// # .with_client_secret("client_secret") + /// # .build() + /// # .unwrap() + /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) + /// # .await + /// # .unwrap(); + /// let roles = dracoon.roles.get_roles().await.unwrap(); + /// # } + /// ``` + async fn get_roles(&self) -> Result; + /// Get a list of all groups with a specific role. + /// ```no_run + /// # use dco3::{Dracoon, auth::OAuth2Flow, Roles, ListAllParams}; + /// # #[tokio::main] + /// # async fn main() { + /// # let dracoon = Dracoon::builder() + /// # .with_base_url("https://dracoon.team") + /// # .with_client_id("client_id") + /// # .with_client_secret("client_secret") + /// # .build() + /// # .unwrap() + /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) + /// # .await + /// # .unwrap(); + /// // Params are optional + /// let params = ListAllParams::builder() + /// .with_offset(0) + /// .with_limit(100) + /// .build(); + /// let groups = dracoon.roles.get_all_groups_with_role(1, Some(params)).await.unwrap(); + /// # } + /// ``` + async fn get_all_groups_with_role( + &self, + role_id: u64, + params: Option, + ) -> Result; + /// Assign a role to a list of groups. + /// ```no_run + /// # use dco3::{Dracoon, auth::OAuth2Flow, Roles}; + /// # #[tokio::main] + /// # async fn main() { + /// # let dracoon = Dracoon::builder() + /// # .with_base_url("https://dracoon.team") + /// # .with_client_id("client_id") + /// # .with_client_secret("client_secret") + /// # .build() + /// # .unwrap() + /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) + /// # .await + /// # .unwrap(); + /// let groups = dracoon.roles.assign_role_to_groups(vec![1, 2, 3], 1).await.unwrap(); + /// # } + /// ``` + async fn assign_role_to_groups( + &self, + role_id: u64, + group_ids: AssignRoleBatchRequest, + ) -> Result; + /// Revoke a role from a list of groups. + /// ```no_run + /// # use dco3::{Dracoon, auth::OAuth2Flow, Roles}; + /// # #[tokio::main] + /// # async fn main() { + /// # let dracoon = Dracoon::builder() + /// # .with_base_url("https://dracoon.team") + /// # .with_client_id("client_id") + /// # .with_client_secret("client_secret") + /// # .build() + /// # .unwrap() + /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) + /// # .await + /// # .unwrap(); + /// let groups = dracoon.roles.revoke_role_from_groups(vec![1, 2, 3], 1).await.unwrap(); + /// # } + /// ``` + async fn revoke_role_from_groups( + &self, + role_id: u64, + group_ids: RevokeRoleBatchRequest, + ) -> Result; + /// Get a list of all users with a specific role. + /// ```no_run + /// # use dco3::{Dracoon, auth::OAuth2Flow, Roles, ListAllParams}; + /// # #[tokio::main] + /// # async fn main() { + /// # let dracoon = Dracoon::builder() + /// # .with_base_url("https://dracoon.team") + /// # .with_client_id("client_id") + /// # .with_client_secret("client_secret") + /// # .build() + /// # .unwrap() + /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) + /// # .await + /// # .unwrap(); + /// // Params are optional + /// let params = ListAllParams::builder() + /// .with_offset(0) + /// .with_limit(100) + /// .build(); + /// let users = dracoon.roles.get_all_users_with_role(1, Some(params)).await.unwrap(); + /// # } + /// ``` + async fn get_all_users_with_role( + &self, + role_id: u64, + params: Option, + ) -> Result; + /// Assign a role to a list of users. + /// ```no_run + /// # use dco3::{Dracoon, auth::OAuth2Flow, Roles}; + /// # #[tokio::main] + /// # async fn main() { + /// # let dracoon = Dracoon::builder() + /// # .with_base_url("https://dracoon.team") + /// # .with_client_id("client_id") + /// # .with_client("client_secret") + /// # .build() + /// # .unwrap() + /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) + /// # .await + /// # .unwrap(); + /// let users = dracoon.roles.assign_role_to_users(vec![1, 2, 3], 1).await.unwrap(); + /// # } + /// ``` + async fn assign_role_to_users( + &self, + role_id: u64, + user_ids: AssignRoleBatchRequest, + ) -> Result; + /// Revoke a role from a list of users. + /// ```no_run + /// # use dco3::{Dracoon, auth::OAuth2Flow, Roles}; + /// # #[tokio::main] + /// # async fn main() { + /// # let dracoon = Dracoon::builder() + /// # .with_base_url("https://dracoon.team") + /// # .with_client_id("client_id") + /// # .with_client_secret("client_secret") + /// # .build() + /// # .unwrap() + /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) + /// # .await + /// # .unwrap(); + /// let users = dracoon.roles.revoke_role_from_users(vec![1, 2, 3], 1).await.unwrap(); + /// # } + /// ``` + async fn revoke_role_from_users( + &self, + role_id: u64, + user_ids: RevokeRoleBatchRequest, + ) -> Result; +} diff --git a/src/roles/models.rs b/src/roles/models.rs new file mode 100644 index 0000000..b7da450 --- /dev/null +++ b/src/roles/models.rs @@ -0,0 +1,121 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use reqwest::Response; +use serde::{Deserialize, Serialize}; + +use crate::{ + auth::{DracoonClient, DracoonErrorResponse}, + models::RangedItems, + nodes::UserInfo, + utils::{parse_body, FromResponse}, + DracoonClientError, +}; + +#[derive(Clone)] +pub struct RolesEndpoint { + client: Arc>, + state: std::marker::PhantomData, +} + +impl RolesEndpoint { + pub fn new(client: Arc>) -> Self { + Self { + client, + state: std::marker::PhantomData, + } + } + + pub fn client(&self) -> &Arc> { + &self.client + } +} + +pub type RoleGroupList = RangedItems; + +#[async_trait] +impl FromResponse for RoleGroupList { + async fn from_response(response: Response) -> Result { + parse_body::(response).await + } +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RoleGroup { + pub id: u64, + pub is_member: bool, + pub name: String, +} + +pub type RoleUserList = RangedItems; + +#[derive(Debug, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Right { + pub id: u64, + pub name: String, + pub description: String, +} + +#[derive(Debug, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Role { + pub id: u64, + pub name: String, + pub description: String, + pub items: Option>, +} + +#[derive(Debug, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct RoleList { + pub items: Vec, +} + +#[async_trait] +impl FromResponse for RoleList { + async fn from_response(response: Response) -> Result { + parse_body::(response).await + } +} + +#[async_trait] +impl FromResponse for RoleUserList { + async fn from_response(response: Response) -> Result { + parse_body::(response).await + } +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RoleUser { + pub user_info: UserInfo, + pub is_member: bool, + pub id: Option, //depreacted, but still returned + pub display_name: Option, // depreacted, but still returned +} + +#[derive(Debug, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct RevokeRoleBatchRequest { + ids: Vec, +} + +impl From> for RevokeRoleBatchRequest { + fn from(ids: Vec) -> Self { + RevokeRoleBatchRequest { ids } + } +} + +#[derive(Debug, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct AssignRoleBatchRequest { + ids: Vec, +} + +impl From> for AssignRoleBatchRequest { + fn from(ids: Vec) -> Self { + AssignRoleBatchRequest { ids } + } +} diff --git a/src/roles/roles.rs b/src/roles/roles.rs new file mode 100644 index 0000000..9073e84 --- /dev/null +++ b/src/roles/roles.rs @@ -0,0 +1,185 @@ +use async_trait::async_trait; +use reqwest::header; + +use crate::{ + auth::Connected, + constants::{DRACOON_API_PREFIX, ROLES_BASE, ROLES_GROUPS, ROLES_USERS}, + utils::FromResponse, + DracoonClientError, ListAllParams, +}; + +use super::{ + AssignRoleBatchRequest, RevokeRoleBatchRequest, RoleGroupList, RoleList, RoleUserList, Roles, + RolesEndpoint, +}; + +#[async_trait] +impl Roles for RolesEndpoint { + async fn get_roles(&self) -> Result { + let url_part = format!("{DRACOON_API_PREFIX}/{ROLES_BASE}"); + + let url = self.client().build_api_url(&url_part); + + let response = self + .client() + .http + .get(url) + .header( + header::AUTHORIZATION, + self.client().get_auth_header().await?, + ) + .header(header::CONTENT_TYPE, "application/json") + .send() + .await?; + + RoleList::from_response(response).await + } + + async fn get_all_groups_with_role( + &self, + role_id: u64, + params: Option, + ) -> Result { + let url_part = format!("{DRACOON_API_PREFIX}/{ROLES_BASE}/{role_id}/{ROLES_GROUPS}"); + + let url = self.client().build_api_url(&url_part); + + let response = self + .client() + .http + .get(url) + .header( + header::AUTHORIZATION, + self.client().get_auth_header().await?, + ) + .header(header::CONTENT_TYPE, "application/json") + .send() + .await?; + + RoleGroupList::from_response(response).await + } + + async fn assign_role_to_groups( + &self, + role_id: u64, + group_ids: AssignRoleBatchRequest, + ) -> Result { + let url_part = format!("{DRACOON_API_PREFIX}/{ROLES_BASE}/{role_id}/{ROLES_GROUPS}"); + + let url = self.client().build_api_url(&url_part); + + let response = self + .client() + .http + .post(url) + .header( + header::AUTHORIZATION, + self.client().get_auth_header().await?, + ) + .header(header::CONTENT_TYPE, "application/json") + .json(&group_ids) + .send() + .await?; + + RoleGroupList::from_response(response).await + } + + async fn revoke_role_from_groups( + &self, + role_id: u64, + group_ids: RevokeRoleBatchRequest, + ) -> Result { + let url_part = format!("{DRACOON_API_PREFIX}/{ROLES_BASE}/{role_id}/{ROLES_GROUPS}"); + + let url = self.client().build_api_url(&url_part); + + let response = self + .client() + .http + .delete(url) + .header( + header::AUTHORIZATION, + self.client().get_auth_header().await?, + ) + .header(header::CONTENT_TYPE, "application/json") + .json(&group_ids) + .send() + .await?; + + RoleGroupList::from_response(response).await + } + + async fn get_all_users_with_role( + &self, + role_id: u64, + params: Option, + ) -> Result { + let url_part = format!("{DRACOON_API_PREFIX}/{ROLES_BASE}/{role_id}/{ROLES_USERS}"); + + let url = self.client().build_api_url(&url_part); + + let response = self + .client() + .http + .get(url) + .header( + header::AUTHORIZATION, + self.client().get_auth_header().await?, + ) + .header(header::CONTENT_TYPE, "application/json") + .send() + .await?; + + RoleUserList::from_response(response).await + } + + async fn assign_role_to_users( + &self, + role_id: u64, + user_ids: AssignRoleBatchRequest, + ) -> Result { + let url_part = format!("{DRACOON_API_PREFIX}/{ROLES_BASE}/{role_id}/{ROLES_USERS}"); + + let url = self.client().build_api_url(&url_part); + + let response = self + .client() + .http + .post(url) + .header( + header::AUTHORIZATION, + self.client().get_auth_header().await?, + ) + .header(header::CONTENT_TYPE, "application/json") + .json(&user_ids) + .send() + .await?; + + RoleUserList::from_response(response).await + } + + async fn revoke_role_from_users( + &self, + role_id: u64, + user_ids: RevokeRoleBatchRequest, + ) -> Result { + let url_part = format!("{DRACOON_API_PREFIX}/{ROLES_BASE}/{role_id}/{ROLES_USERS}"); + + let url = self.client().build_api_url(&url_part); + + let response = self + .client() + .http + .delete(url) + .header( + header::AUTHORIZATION, + self.client().get_auth_header().await?, + ) + .header(header::CONTENT_TYPE, "application/json") + .json(&user_ids) + .send() + .await?; + + RoleUserList::from_response(response).await + } +} From 8ef99fcd328e8b642f3c740c3a46f66aa06b092a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralf=20B=C3=B6hler?= Date: Fri, 30 Aug 2024 08:57:52 +0200 Subject: [PATCH 2/6] chore: Update import statements for roles module in user and users models --- src/groups/models.rs | 2 +- src/user/models.rs | 24 +----------------------- src/users/models.rs | 2 +- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/src/groups/models.rs b/src/groups/models.rs index d775eee..290d9f0 100644 --- a/src/groups/models.rs +++ b/src/groups/models.rs @@ -10,7 +10,7 @@ use crate::{ auth::{DracoonClient, DracoonErrorResponse}, models::{FilterOperator, FilterQuery, ObjectExpiration, RangedItems, SortOrder, SortQuery}, nodes::models::UserInfo, - user::models::RoleList, + roles::RoleList, utils::{parse_body, FromResponse}, DracoonClientError, }; diff --git a/src/user/models.rs b/src/user/models.rs index e0c9ee0..2071eb2 100644 --- a/src/user/models.rs +++ b/src/user/models.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; use crate::{ auth::{errors::DracoonClientError, models::DracoonErrorResponse, DracoonClient}, + roles::RoleList, utils::{parse_body, FromResponse}, }; @@ -67,29 +68,6 @@ pub struct UserAuthData { pub oid_config_id: Option, } -#[derive(Debug, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct Right { - pub id: u64, - pub name: String, - pub description: String, -} - -#[derive(Debug, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct Role { - pub id: u64, - pub name: String, - pub description: String, - pub items: Option>, -} - -#[derive(Debug, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct RoleList { - pub items: Vec, -} - #[derive(Debug, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct UserGroup { diff --git a/src/users/models.rs b/src/users/models.rs index c877db3..a041a25 100644 --- a/src/users/models.rs +++ b/src/users/models.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use crate::{ auth::{DracoonClient, DracoonErrorResponse}, models::{ObjectExpiration, RangedItems}, - user::RoleList, + roles::RoleList, utils::{parse_body, FromResponse}, DracoonClientError, FilterOperator, FilterQuery, SortOrder, SortQuery, }; From 7ebdc1b30efae8e8ad76b7ddda149d5f77f3c4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralf=20B=C3=B6hler?= Date: Fri, 30 Aug 2024 09:00:16 +0200 Subject: [PATCH 3/6] test: tests for roles endpoints --- src/tests/mod.rs | 1 + .../responses/roles/role_group_list_ok.json | 14 + .../responses/roles/role_user_list_ok.json | 23 ++ src/tests/responses/roles/roles_ok.json | 16 ++ src/tests/roles.rs | 243 ++++++++++++++++++ 5 files changed, 297 insertions(+) create mode 100644 src/tests/responses/roles/role_group_list_ok.json create mode 100644 src/tests/responses/roles/role_user_list_ok.json create mode 100644 src/tests/responses/roles/roles_ok.json create mode 100644 src/tests/roles.rs diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 31ebd2e..3395bc2 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,6 +1,7 @@ mod groups; pub mod nodes; mod provisioning; +mod roles; mod rooms; mod shares; mod user; diff --git a/src/tests/responses/roles/role_group_list_ok.json b/src/tests/responses/roles/role_group_list_ok.json new file mode 100644 index 0000000..6cba496 --- /dev/null +++ b/src/tests/responses/roles/role_group_list_ok.json @@ -0,0 +1,14 @@ +{ + "range": { + "offset": 0, + "limit": 0, + "total": 1 + }, + "items": [ + { + "id": 1, + "isMember": true, + "name": "group name" + } + ] +} diff --git a/src/tests/responses/roles/role_user_list_ok.json b/src/tests/responses/roles/role_user_list_ok.json new file mode 100644 index 0000000..e0902c6 --- /dev/null +++ b/src/tests/responses/roles/role_user_list_ok.json @@ -0,0 +1,23 @@ +{ + "range": { + "offset": 0, + "limit": 0, + "total": 1 + }, + "items": [ + { + "userInfo": { + "id": 1, + "userType": "internal", + "avatarUuid": "avatar uuid", + "userName": "user name", + "firstName": "first name", + "lastName": "last name", + "email": "email" + }, + "isMember": true, + "id": 1, + "displayName": "display name" + } + ] +} diff --git a/src/tests/responses/roles/roles_ok.json b/src/tests/responses/roles/roles_ok.json new file mode 100644 index 0000000..34dc543 --- /dev/null +++ b/src/tests/responses/roles/roles_ok.json @@ -0,0 +1,16 @@ +{ + "items": [ + { + "id": 1, + "name": "role name", + "description": "role description", + "items": [ + { + "id": 1, + "name": "item name", + "description": "item description" + } + ] + } + ] +} diff --git a/src/tests/roles.rs b/src/tests/roles.rs new file mode 100644 index 0000000..8174bf6 --- /dev/null +++ b/src/tests/roles.rs @@ -0,0 +1,243 @@ +#[cfg(test)] +pub mod tests { + use crate::{ + nodes::UserType, + roles::{RoleGroup, RoleUser, Roles}, + tests::dracoon::get_connected_client, + }; + + pub fn assert_role_group(role_group: &RoleGroup) { + assert_eq!(role_group.id, 1); + assert_eq!(role_group.is_member, true); + assert_eq!(role_group.name, "group name"); + } + + pub fn assert_role_user(role_user: &RoleUser) { + assert_eq!(role_user.id, Some(1)); + assert_eq!(role_user.display_name, Some("display name".to_string())); + assert!(role_user.is_member); + + let user_info = role_user.user_info.clone(); + + assert_eq!(user_info.id, 1); + assert_eq!(user_info.user_name, Some("user name".to_string())); + assert_eq!(user_info.first_name, Some("first name".to_string())); + assert_eq!(user_info.last_name, Some("last name".to_string())); + assert_eq!(user_info.email, Some("email".to_string())); + assert_eq!(user_info.avatar_uuid, "avatar uuid"); + assert_eq!(user_info.user_type, UserType::Internal); + } + + #[tokio::test] + async fn get_roles() { + let (dracoon, mock_server) = get_connected_client().await; + + let mut mock_server = mock_server; + + let roles_res = include_str!("./responses/roles/roles_ok.json"); + + let roles_mock = mock_server + .mock("GET", "/api/v4/roles") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(roles_res) + .create(); + + let roles = dracoon.roles.get_roles().await.unwrap(); + + roles_mock.assert(); + + assert_eq!(roles.items.len(), 1); + + let role = roles.items.first().unwrap(); + + assert_eq!(role.id, 1); + assert_eq!(role.name, "role name"); + assert_eq!(role.description, "role description"); + + let role_items = role.items.clone().unwrap(); + assert_eq!(role_items.len(), 1); + let role_item = role_items.first().unwrap(); + assert_eq!(role_item.id, 1); + assert_eq!(role_item.name, "item name"); + assert_eq!(role_item.description, "item description"); + } + + #[tokio::test] + async fn get_all_groups_with_role() { + let (dracoon, mock_server) = get_connected_client().await; + + let mut mock_server = mock_server; + + let groups_res = include_str!("./responses/roles/role_group_list_ok.json"); + + let groups_mock = mock_server + .mock("GET", "/api/v4/roles/1/groups") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(groups_res) + .create(); + + let groups = dracoon + .roles + .get_all_groups_with_role(1, None) + .await + .unwrap(); + + groups_mock.assert(); + + assert_eq!(groups.items.len(), 1); + + let role_group = groups.items.first().unwrap(); + assert_role_group(role_group) + } + + #[tokio::test] + async fn assign_role_to_groups() { + let (dracoon, mock_server) = get_connected_client().await; + + let mut mock_server = mock_server; + + let groups_res = include_str!("./responses/roles/role_group_list_ok.json"); + + let groups_mock = mock_server + .mock("POST", "/api/v4/roles/1/groups") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(groups_res) + .create(); + + let groups = dracoon + .roles + .assign_role_to_groups(1, vec![1, 2, 3].into()) + .await + .unwrap(); + + groups_mock.assert(); + + assert_eq!(groups.items.len(), 1); + + let role_group = groups.items.first().unwrap(); + + assert_role_group(role_group) + } + + #[tokio::test] + async fn revoke_role_from_groups() { + let (dracoon, mock_server) = get_connected_client().await; + + let mut mock_server = mock_server; + + let groups_res = include_str!("./responses/roles/role_group_list_ok.json"); + + let groups_mock = mock_server + .mock("DELETE", "/api/v4/roles/1/groups") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(groups_res) + .create(); + + let groups = dracoon + .roles + .revoke_role_from_groups(1, vec![1, 2, 3].into()) + .await + .unwrap(); + + groups_mock.assert(); + + assert_eq!(groups.items.len(), 1); + + let role_group = groups.items.first().unwrap(); + assert_role_group(role_group) + } + + #[tokio::test] + async fn get_all_users_with_role() { + let (dracoon, mock_server) = get_connected_client().await; + + let mut mock_server = mock_server; + + let users_res = include_str!("./responses/roles/role_user_list_ok.json"); + + let users_mock = mock_server + .mock("GET", "/api/v4/roles/1/users") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(users_res) + .create(); + + let res = dracoon + .roles + .get_all_users_with_role(1, None) + .await + .unwrap(); + + users_mock.assert(); + + assert_eq!(res.items.len(), 1); + + let role_user = res.items.first().unwrap(); + + assert_role_user(role_user) + } + + #[tokio::test] + async fn assign_role_to_users() { + let (dracoon, mock_server) = get_connected_client().await; + + let mut mock_server = mock_server; + + let users_res = include_str!("./responses/roles/role_user_list_ok.json"); + + let users_mock = mock_server + .mock("POST", "/api/v4/roles/1/users") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(users_res) + .create(); + + let res = dracoon + .roles + .assign_role_to_users(1, vec![1, 2, 3].into()) + .await + .unwrap(); + + users_mock.assert(); + + assert_eq!(res.items.len(), 1); + + let role_user = res.items.first().unwrap(); + + assert_role_user(role_user) + } + + #[tokio::test] + async fn revoke_role_from_users() { + let (dracoon, mock_server) = get_connected_client().await; + + let mut mock_server = mock_server; + + let users_res = include_str!("./responses/roles/role_user_list_ok.json"); + + let users_mock = mock_server + .mock("DELETE", "/api/v4/roles/1/users") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(users_res) + .create(); + + let res = dracoon + .roles + .revoke_role_from_users(1, vec![1, 2, 3].into()) + .await + .unwrap(); + + users_mock.assert(); + + assert_eq!(res.items.len(), 1); + + let role_user = res.items.first().unwrap(); + + assert_role_user(role_user) + } +} From 62a62bc06976f4dd8c0230de5392d7a53f99107a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralf=20B=C3=B6hler?= Date: Fri, 30 Aug 2024 10:10:29 +0200 Subject: [PATCH 4/6] docs(roles): change function parameter sequence and convert to correct type with into(). --- src/roles/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/roles/mod.rs b/src/roles/mod.rs index 901b424..1476b90 100644 --- a/src/roles/mod.rs +++ b/src/roles/mod.rs @@ -69,7 +69,7 @@ pub trait Roles { /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) /// # .await /// # .unwrap(); - /// let groups = dracoon.roles.assign_role_to_groups(vec![1, 2, 3], 1).await.unwrap(); + /// let groups = dracoon.roles.assign_role_to_groups(1, vec![1, 2, 3].into()).await.unwrap(); /// # } /// ``` async fn assign_role_to_groups( @@ -91,7 +91,7 @@ pub trait Roles { /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) /// # .await /// # .unwrap(); - /// let groups = dracoon.roles.revoke_role_from_groups(vec![1, 2, 3], 1).await.unwrap(); + /// let groups = dracoon.roles.revoke_role_from_groups(1, vec![1, 2, 3].into()).await.unwrap(); /// # } /// ``` async fn revoke_role_from_groups( @@ -140,7 +140,7 @@ pub trait Roles { /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) /// # .await /// # .unwrap(); - /// let users = dracoon.roles.assign_role_to_users(vec![1, 2, 3], 1).await.unwrap(); + /// let users = dracoon.roles.assign_role_to_users(1, vec![1, 2, 3].into()).await.unwrap(); /// # } /// ``` async fn assign_role_to_users( @@ -162,7 +162,7 @@ pub trait Roles { /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into())) /// # .await /// # .unwrap(); - /// let users = dracoon.roles.revoke_role_from_users(vec![1, 2, 3], 1).await.unwrap(); + /// let users = dracoon.roles.revoke_role_from_users(1, vec![1, 2, 3].into()).await.unwrap(); /// # } /// ``` async fn revoke_role_from_users( From 7bc9c6d0fdd7b02e2e29519cb2866110a0abdaab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralf=20B=C3=B6hler?= Date: Fri, 30 Aug 2024 14:57:03 +0200 Subject: [PATCH 5/6] fix(roles): Add re-export for doc test --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index eebfe1f..0f81559 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ //! * [Public] - for public information //! * [PublicDownload] - for public download via share //! * [PublicUpload] - for public upload via file request +//! * [Roles] - for role operations //! //! ### Example //! ```no_run @@ -458,6 +459,7 @@ pub use self::{ nodes::{Download, Folders, MissingFileKeys, Nodes, Rooms, Upload}, provisioning::CustomerProvisioning, public::{Public, PublicDownload, PublicUpload}, + roles::Roles, settings::RescueKeyPair, shares::{DownloadShares, UploadShares}, system::AuthenticationMethods, From 013245b2ebc5a76ff480ccc69f84150b47249d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralf=20B=C3=B6hler?= Date: Fri, 30 Aug 2024 14:59:13 +0200 Subject: [PATCH 6/6] fix(roles): fix set client secret in Dracoon builder --- src/roles/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/roles/mod.rs b/src/roles/mod.rs index 1476b90..130820f 100644 --- a/src/roles/mod.rs +++ b/src/roles/mod.rs @@ -134,7 +134,7 @@ pub trait Roles { /// # let dracoon = Dracoon::builder() /// # .with_base_url("https://dracoon.team") /// # .with_client_id("client_id") - /// # .with_client("client_secret") + /// # .with_client_secret("client_secret") /// # .build() /// # .unwrap() /// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into()))