1use crate::httpx::client::Client;
20use crate::mgmtx::error;
21use crate::mgmtx::mgmt::{parse_response_json, Management};
22use crate::mgmtx::options::{
23 ChangePasswordOptions, DeleteGroupOptions, DeleteUserOptions, GetAllGroupsOptions,
24 GetAllUsersOptions, GetGroupOptions, GetRolesOptions, GetUserOptions, UpsertGroupOptions,
25 UpsertUserOptions,
26};
27use crate::mgmtx::user::{Group, Role, RoleAndDescription, UserAndMetadata};
28use crate::mgmtx::user_json::{GroupJson, RoleAndDescriptionJson, UserAndMetadataJson};
29use crate::tracingcomponent::{BeginDispatchFields, EndDispatchFields};
30use crate::util::get_host_port_tuple_from_uri;
31use bytes::Bytes;
32use http::Method;
33
34impl<C: Client> Management<C> {
35 pub async fn get_user(&self, opts: &GetUserOptions<'_>) -> error::Result<UserAndMetadata> {
36 let method = Method::GET;
37 let path = format!(
38 "settings/rbac/users/{}/{}",
39 urlencoding::encode(opts.auth_domain),
40 urlencoding::encode(opts.username)
41 )
42 .to_string();
43
44 let peer_addr = get_host_port_tuple_from_uri(&self.endpoint).unwrap_or_default();
45 let canonical_addr =
46 get_host_port_tuple_from_uri(&self.canonical_endpoint).unwrap_or_default();
47 let resp = self
48 .tracing
49 .orchestrate_dispatch_span(
50 BeginDispatchFields::new(
51 (&peer_addr.0, &peer_addr.1),
52 (&canonical_addr.0, &canonical_addr.1),
53 None,
54 ),
55 self.execute(
56 method.clone(),
57 &path,
58 "",
59 opts.on_behalf_of_info.cloned(),
60 None,
61 None,
62 ),
63 |_| EndDispatchFields::new(None, None),
64 )
65 .await?;
66
67 if resp.status() != 200 {
68 return Err(Self::decode_common_error(method, path, "get_user", resp).await);
69 }
70
71 let user_json: UserAndMetadataJson = parse_response_json(resp).await?;
72
73 user_json.try_into()
74 }
75
76 pub async fn get_all_users(
77 &self,
78 opts: &GetAllUsersOptions<'_>,
79 ) -> error::Result<Vec<UserAndMetadata>> {
80 let method = Method::GET;
81 let path = format!(
82 "settings/rbac/users/{}",
83 urlencoding::encode(opts.auth_domain),
84 )
85 .to_string();
86
87 let peer_addr = get_host_port_tuple_from_uri(&self.endpoint).unwrap_or_default();
88 let canonical_addr =
89 get_host_port_tuple_from_uri(&self.canonical_endpoint).unwrap_or_default();
90 let resp = self
91 .tracing
92 .orchestrate_dispatch_span(
93 BeginDispatchFields::new(
94 (&peer_addr.0, &peer_addr.1),
95 (&canonical_addr.0, &canonical_addr.1),
96 None,
97 ),
98 self.execute(
99 method.clone(),
100 &path,
101 "",
102 opts.on_behalf_of_info.cloned(),
103 None,
104 None,
105 ),
106 |_| EndDispatchFields::new(None, None),
107 )
108 .await?;
109
110 if resp.status() != 200 {
111 return Err(Self::decode_common_error(method, path, "get_all_users", resp).await);
112 }
113
114 let users_json: Vec<UserAndMetadataJson> = parse_response_json(resp).await?;
115
116 users_json
117 .into_iter()
118 .map(UserAndMetadata::try_from)
119 .collect()
120 }
121
122 pub async fn upsert_user(&self, opts: &UpsertUserOptions<'_>) -> error::Result<()> {
123 let body = {
124 let mut form = url::form_urlencoded::Serializer::new(String::new());
125 form.append_pair("name", opts.user.display_name.as_str())
126 .append_pair(
127 "roles",
128 &opts
129 .user
130 .roles
131 .iter()
132 .map(Self::build_role)
133 .collect::<Vec<String>>()
134 .join(","),
135 );
136
137 if let Some(password) = &opts.user.password {
138 form.append_pair("password", password.as_str());
139 }
140
141 if !opts.user.groups.is_empty() {
142 form.append_pair("groups", &opts.user.groups.join(","));
143 }
144
145 Bytes::from(form.finish())
146 };
147
148 let method = Method::PUT;
149 let path = format!(
150 "settings/rbac/users/{}/{}",
151 urlencoding::encode(opts.auth_domain),
152 urlencoding::encode(&opts.user.username),
153 );
154
155 let peer_addr = get_host_port_tuple_from_uri(&self.endpoint).unwrap_or_default();
156 let canonical_addr =
157 get_host_port_tuple_from_uri(&self.canonical_endpoint).unwrap_or_default();
158 let resp = self
159 .tracing
160 .orchestrate_dispatch_span(
161 BeginDispatchFields::new(
162 (&peer_addr.0, &peer_addr.1),
163 (&canonical_addr.0, &canonical_addr.1),
164 None,
165 ),
166 self.execute(
167 method.clone(),
168 &path,
169 "application/x-www-form-urlencoded",
170 opts.on_behalf_of_info.cloned(),
171 None,
172 Some(body),
173 ),
174 |_| EndDispatchFields::new(None, None),
175 )
176 .await?;
177
178 if resp.status().as_u16() < 200 || resp.status().as_u16() >= 300 {
179 return Err(Self::decode_common_error(method, path, "upsert_user", resp).await);
180 }
181
182 Ok(())
183 }
184
185 pub async fn delete_user(&self, opts: &DeleteUserOptions<'_>) -> error::Result<()> {
186 let method = Method::DELETE;
187 let path = format!(
188 "settings/rbac/users/{}/{}",
189 urlencoding::encode(opts.auth_domain),
190 urlencoding::encode(opts.username),
191 )
192 .to_string();
193
194 let peer_addr = get_host_port_tuple_from_uri(&self.endpoint).unwrap_or_default();
195 let canonical_addr =
196 get_host_port_tuple_from_uri(&self.canonical_endpoint).unwrap_or_default();
197 let resp = self
198 .tracing
199 .orchestrate_dispatch_span(
200 BeginDispatchFields::new(
201 (&peer_addr.0, &peer_addr.1),
202 (&canonical_addr.0, &canonical_addr.1),
203 None,
204 ),
205 self.execute(
206 method.clone(),
207 &path,
208 "",
209 opts.on_behalf_of_info.cloned(),
210 None,
211 None,
212 ),
213 |_| EndDispatchFields::new(None, None),
214 )
215 .await?;
216
217 if resp.status() != 200 {
218 return Err(Self::decode_common_error(method, path, "delete_user", resp).await);
219 }
220
221 Ok(())
222 }
223
224 pub async fn get_roles(
225 &self,
226 opts: &GetRolesOptions<'_>,
227 ) -> error::Result<Vec<RoleAndDescription>> {
228 let method = Method::GET;
229
230 let path = if let Some(p) = opts.permission {
231 format!("settings/rbac/roles?permission={}", urlencoding::encode(p))
232 } else {
233 "settings/rbac/roles".to_string()
234 };
235
236 let peer_addr = get_host_port_tuple_from_uri(&self.endpoint).unwrap_or_default();
237 let canonical_addr =
238 get_host_port_tuple_from_uri(&self.canonical_endpoint).unwrap_or_default();
239 let resp = self
240 .tracing
241 .orchestrate_dispatch_span(
242 BeginDispatchFields::new(
243 (&peer_addr.0, &peer_addr.1),
244 (&canonical_addr.0, &canonical_addr.1),
245 None,
246 ),
247 self.execute(
248 method.clone(),
249 &path,
250 "",
251 opts.on_behalf_of_info.cloned(),
252 None,
253 None,
254 ),
255 |_| EndDispatchFields::new(None, None),
256 )
257 .await?;
258
259 if resp.status() != 200 {
260 return Err(Self::decode_common_error(method, path, "get_roles", resp).await);
261 }
262
263 let roles_json: Vec<RoleAndDescriptionJson> = parse_response_json(resp).await?;
264
265 Ok(roles_json
266 .into_iter()
267 .map(RoleAndDescription::from)
268 .collect())
269 }
270
271 pub async fn get_group(&self, opts: &GetGroupOptions<'_>) -> error::Result<Group> {
272 let method = Method::GET;
273 let path = format!("settings/rbac/groups/{}", opts.group_name).to_string();
274
275 let peer_addr = get_host_port_tuple_from_uri(&self.endpoint).unwrap_or_default();
276 let canonical_addr =
277 get_host_port_tuple_from_uri(&self.canonical_endpoint).unwrap_or_default();
278 let resp = self
279 .tracing
280 .orchestrate_dispatch_span(
281 BeginDispatchFields::new(
282 (&peer_addr.0, &peer_addr.1),
283 (&canonical_addr.0, &canonical_addr.1),
284 None,
285 ),
286 self.execute(
287 method.clone(),
288 &path,
289 "",
290 opts.on_behalf_of_info.cloned(),
291 None,
292 None,
293 ),
294 |_| EndDispatchFields::new(None, None),
295 )
296 .await?;
297
298 if resp.status() != 200 {
299 return Err(Self::decode_common_error(method, path, "get_group", resp).await);
300 }
301
302 let group_json: GroupJson = parse_response_json(resp).await?;
303
304 Ok(group_json.into())
305 }
306
307 pub async fn get_all_groups(
308 &self,
309 opts: &GetAllGroupsOptions<'_>,
310 ) -> error::Result<Vec<Group>> {
311 let method = Method::GET;
312 let path = "settings/rbac/groups".to_string();
313
314 let peer_addr = get_host_port_tuple_from_uri(&self.endpoint).unwrap_or_default();
315 let canonical_addr =
316 get_host_port_tuple_from_uri(&self.canonical_endpoint).unwrap_or_default();
317 let resp = self
318 .tracing
319 .orchestrate_dispatch_span(
320 BeginDispatchFields::new(
321 (&peer_addr.0, &peer_addr.1),
322 (&canonical_addr.0, &canonical_addr.1),
323 None,
324 ),
325 self.execute(
326 method.clone(),
327 &path,
328 "",
329 opts.on_behalf_of_info.cloned(),
330 None,
331 None,
332 ),
333 |_| EndDispatchFields::new(None, None),
334 )
335 .await?;
336
337 if resp.status() != 200 {
338 return Err(Self::decode_common_error(method, path, "get_all_groups", resp).await);
339 }
340
341 let groups_json: Vec<GroupJson> = parse_response_json(resp).await?;
342
343 Ok(groups_json.into_iter().map(Group::from).collect())
344 }
345
346 pub async fn upsert_group(&self, opts: &UpsertGroupOptions<'_>) -> error::Result<()> {
347 let method = Method::PUT;
348 let path = format!(
349 "settings/rbac/groups/{}",
350 urlencoding::encode(&opts.group.name),
351 )
352 .to_string();
353
354 let body = {
355 let mut form = url::form_urlencoded::Serializer::new(String::new());
356 form.append_pair(
357 "roles",
358 &opts
359 .group
360 .roles
361 .iter()
362 .map(Self::build_role)
363 .collect::<Vec<String>>()
364 .join(","),
365 );
366
367 if let Some(desc) = &opts.group.description {
368 form.append_pair("description", desc.as_str());
369 }
370
371 if let Some(group_ref) = &opts.group.ldap_group_reference {
372 form.append_pair("ldap_group_ref", group_ref.as_str());
373 }
374
375 Bytes::from(form.finish())
376 };
377
378 let peer_addr = get_host_port_tuple_from_uri(&self.endpoint).unwrap_or_default();
379 let canonical_addr =
380 get_host_port_tuple_from_uri(&self.canonical_endpoint).unwrap_or_default();
381 let resp = self
382 .tracing
383 .orchestrate_dispatch_span(
384 BeginDispatchFields::new(
385 (&peer_addr.0, &peer_addr.1),
386 (&canonical_addr.0, &canonical_addr.1),
387 None,
388 ),
389 self.execute(
390 method.clone(),
391 &path,
392 "application/x-www-form-urlencoded",
393 opts.on_behalf_of_info.cloned(),
394 None,
395 Some(body),
396 ),
397 |_| EndDispatchFields::new(None, None),
398 )
399 .await?;
400
401 if resp.status().as_u16() < 200 || resp.status().as_u16() >= 300 {
402 return Err(Self::decode_common_error(method, path, "upsert_group", resp).await);
403 }
404
405 Ok(())
406 }
407
408 pub async fn delete_group(&self, opts: &DeleteGroupOptions<'_>) -> error::Result<()> {
409 let method = Method::DELETE;
410 let path = format!(
411 "settings/rbac/groups/{}",
412 urlencoding::encode(opts.group_name),
413 )
414 .to_string();
415
416 let peer_addr = get_host_port_tuple_from_uri(&self.endpoint).unwrap_or_default();
417 let canonical_addr =
418 get_host_port_tuple_from_uri(&self.canonical_endpoint).unwrap_or_default();
419 let resp = self
420 .tracing
421 .orchestrate_dispatch_span(
422 BeginDispatchFields::new(
423 (&peer_addr.0, &peer_addr.1),
424 (&canonical_addr.0, &canonical_addr.1),
425 None,
426 ),
427 self.execute(
428 method.clone(),
429 &path,
430 "",
431 opts.on_behalf_of_info.cloned(),
432 None,
433 None,
434 ),
435 |_| EndDispatchFields::new(None, None),
436 )
437 .await?;
438
439 if resp.status() != 200 {
440 return Err(Self::decode_common_error(method, path, "delete_group", resp).await);
441 }
442
443 Ok(())
444 }
445
446 pub async fn change_password(&self, opts: &ChangePasswordOptions<'_>) -> error::Result<()> {
447 let method = Method::POST;
448 let path = "controller/changePassword".to_string();
449
450 let body = {
451 let mut form = url::form_urlencoded::Serializer::new(String::new());
452 form.append_pair("password", opts.new_password);
453
454 Bytes::from(form.finish())
455 };
456
457 let peer_addr = get_host_port_tuple_from_uri(&self.endpoint).unwrap_or_default();
458 let canonical_addr =
459 get_host_port_tuple_from_uri(&self.canonical_endpoint).unwrap_or_default();
460 let resp = self
461 .tracing
462 .orchestrate_dispatch_span(
463 BeginDispatchFields::new(
464 (&peer_addr.0, &peer_addr.1),
465 (&canonical_addr.0, &canonical_addr.1),
466 None,
467 ),
468 self.execute(
469 method.clone(),
470 &path,
471 "application/x-www-form-urlencoded",
472 opts.on_behalf_of_info.cloned(),
473 None,
474 Some(body),
475 ),
476 |_| EndDispatchFields::new(None, None),
477 )
478 .await?;
479
480 if resp.status() != 200 {
481 return Err(Self::decode_common_error(method, path, "change_password", resp).await);
482 }
483
484 Ok(())
485 }
486
487 fn build_role(role: &Role) -> String {
488 let mut role_str = role.name.clone();
489
490 if let Some(bucket) = &role.bucket {
491 role_str = format!("{role_str}[{bucket}");
492
493 if let Some(scope) = &role.scope {
494 role_str = format!("{role_str}:{scope}");
495 }
496 if let Some(collection) = &role.collection {
497 role_str = format!("{role_str}:{collection}");
498 }
499
500 role_str = format!("{role_str}]");
501 }
502
503 role_str
504 }
505}