wekan_cli/client/users/
tokens.rs1use reqwest::{StatusCode, header::ACCEPT};
2use secrecy::{ExposeSecret, SecretString};
3use serde::{Deserialize, Serialize, Serializer};
4
5use crate::client::{
6 ClientError, WekanClient,
7 decoding::{decode_value, parse_json},
8 transport::{decode_success, embedded_error},
9};
10
11#[derive(Debug, Deserialize)]
12#[serde(deny_unknown_fields)]
13pub struct CreatedUserToken {
14 #[serde(rename = "_id")]
15 pub user_id: String,
16 #[serde(rename = "authToken", deserialize_with = "deserialize_secret")]
17 pub auth_token: SecretString,
18}
19
20#[derive(Debug, Serialize)]
21#[serde(rename_all = "camelCase")]
22pub struct DeleteUserTokenRequest {
23 pub user_id: String,
24 #[serde(
25 skip_serializing_if = "Option::is_none",
26 serialize_with = "serialize_token"
27 )]
28 pub token: Option<SecretString>,
29}
30
31fn serialize_token<S: Serializer>(value: &Option<SecretString>, s: S) -> Result<S::Ok, S::Error> {
32 value.as_ref().map(ExposeSecret::expose_secret).serialize(s)
33}
34
35#[derive(Debug, Deserialize)]
36#[serde(deny_unknown_fields)]
37pub struct UserTokenDeletion {
38 #[serde(deserialize_with = "deserialize_secret")]
40 pub message: SecretString,
41}
42
43fn deserialize_secret<'de, D: serde::Deserializer<'de>>(d: D) -> Result<SecretString, D::Error> {
44 String::deserialize(d).map(SecretString::from)
45}
46
47impl WekanClient {
48 pub async fn create_user_token(
49 &self,
50 user_id: &str,
51 credential: &SecretString,
52 ) -> Result<CreatedUserToken, ClientError> {
53 let mut endpoint = self.endpoint("api/createtoken", "token creation")?;
54 endpoint
55 .path_segments_mut()
56 .map_err(|()| ClientError::Protocol {
57 diagnostic: None,
58 message: "could not add the token user ID to the endpoint".to_owned(),
59 success_status_received: false,
60 })?
61 .push(user_id);
62 let body = self
63 .execute_success_request(
64 self.http
65 .post(endpoint)
66 .header(ACCEPT, "application/json")
67 .bearer_auth(credential.expose_secret()),
68 )
69 .await?;
70 let response: CreatedUserToken = decode_success(&body, "token creation", true)?;
71 if response.user_id.is_empty()
72 || response.user_id != user_id
73 || response.auth_token.expose_secret().is_empty()
74 {
75 return Err(ClientError::Protocol {
76 diagnostic: None,
77 message:
78 "the token creation response contained an empty token or an unexpected user ID"
79 .to_owned(),
80 success_status_received: true,
81 });
82 }
83 Ok(response)
84 }
85
86 pub async fn delete_user_token(
87 &self,
88 request: &DeleteUserTokenRequest,
89 credential: &SecretString,
90 ) -> Result<UserTokenDeletion, ClientError> {
91 let body = self
92 .execute_success_request(
93 self.http
94 .post(self.endpoint("api/deletetoken", "token deletion")?)
95 .header(ACCEPT, "application/json")
96 .bearer_auth(credential.expose_secret())
97 .json(request),
98 )
99 .await?;
100 let value = parse_json(&body, "token deletion")?;
101 let success = decode_value::<UserTokenDeletion>(value.clone(), "token deletion", true);
104 let response = match success {
105 Ok(response) => response,
106 Err(error) => {
107 if value.as_object().is_some_and(|o| {
108 ["error", "reason", "statusCode", "errorType"]
109 .iter()
110 .any(|key| o.contains_key(*key))
111 }) {
112 if let Some(embedded) = embedded_error(&value, StatusCode::OK)? {
113 return Err(embedded);
114 }
115 }
116 return Err(error);
117 }
118 };
119 Ok(response)
120 }
121}