open_lark/service/cloud_docs/permission/public_v1/password/
update.rs1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3
4use crate::core::{
5 api_req::ApiRequest,
6 api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
7 config::Config,
8 constants::AccessTokenType,
9 endpoints::{cloud_docs::*, EndpointBuilder},
10 http::Transport,
11 query_params::QueryParams,
12 req_option::RequestOption,
13 SDKResult,
14};
15
16#[derive(Debug, Serialize, Default, Clone)]
18pub struct UpdatePasswordRequest {
19 #[serde(skip)]
20 api_request: ApiRequest,
21 #[serde(skip)]
23 token: String,
24 #[serde(skip)]
26 obj_type: String,
27 password: String,
29}
30
31impl UpdatePasswordRequest {
32 pub fn builder() -> UpdatePasswordRequestBuilder {
33 UpdatePasswordRequestBuilder::default()
34 }
35
36 pub fn new(token: impl ToString, obj_type: impl ToString, password: impl ToString) -> Self {
37 Self {
38 token: token.to_string(),
39 obj_type: obj_type.to_string(),
40 password: password.to_string(),
41 ..Default::default()
42 }
43 }
44
45 pub fn for_doc(token: impl ToString, password: impl ToString) -> Self {
47 Self::new(token, "doc", password)
48 }
49
50 pub fn for_sheet(token: impl ToString, password: impl ToString) -> Self {
52 Self::new(token, "sheet", password)
53 }
54
55 pub fn for_bitable(token: impl ToString, password: impl ToString) -> Self {
57 Self::new(token, "bitable", password)
58 }
59
60 pub fn for_wiki(token: impl ToString, password: impl ToString) -> Self {
62 Self::new(token, "wiki", password)
63 }
64}
65
66#[derive(Default)]
67pub struct UpdatePasswordRequestBuilder {
68 request: UpdatePasswordRequest,
69}
70
71impl UpdatePasswordRequestBuilder {
72 pub fn token(mut self, token: impl ToString) -> Self {
74 self.request.token = token.to_string();
75 self
76 }
77
78 pub fn obj_type(mut self, obj_type: impl ToString) -> Self {
80 self.request.obj_type = obj_type.to_string();
81 self
82 }
83
84 pub fn as_doc(mut self) -> Self {
86 self.request.obj_type = "doc".to_string();
87 self
88 }
89
90 pub fn as_sheet(mut self) -> Self {
92 self.request.obj_type = "sheet".to_string();
93 self
94 }
95
96 pub fn as_bitable(mut self) -> Self {
98 self.request.obj_type = "bitable".to_string();
99 self
100 }
101
102 pub fn as_wiki(mut self) -> Self {
104 self.request.obj_type = "wiki".to_string();
105 self
106 }
107
108 pub fn password(mut self, password: impl ToString) -> Self {
110 self.request.password = password.to_string();
111 self
112 }
113
114 pub fn simple_password(mut self, digits: u32) -> Self {
116 self.request.password = format!("{:06}", digits % 1000000);
117 self
118 }
119
120 pub fn random_password(mut self, length: usize) -> Self {
122 use rand::{distributions::Alphanumeric, thread_rng, Rng};
123
124 let password: String = thread_rng()
125 .sample_iter(&Alphanumeric)
126 .take(length.clamp(6, 32))
127 .map(char::from)
128 .collect();
129
130 self.request.password = password;
131 self
132 }
133
134 pub fn enhance_password(mut self, base_password: impl ToString) -> Self {
136 use rand::{thread_rng, Rng};
137
138 let base = base_password.to_string();
139 let suffix: u32 = thread_rng().gen_range(10..100);
140 self.request.password = format!("{base}@{suffix}");
141 self
142 }
143
144 pub fn build(mut self) -> UpdatePasswordRequest {
145 self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
146 self.request
147 }
148}
149
150#[derive(Debug, Deserialize)]
152pub struct PasswordUpdateResult {
153 pub password: String,
155 pub update_time: Option<i64>,
157 pub expire_time: Option<i64>,
159 pub previous_password_hint: Option<String>,
161}
162
163#[derive(Debug, Deserialize)]
165pub struct UpdatePasswordResponse {
166 pub password: PasswordUpdateResult,
168}
169
170impl ApiResponseTrait for UpdatePasswordResponse {
171 fn data_format() -> ResponseFormat {
172 ResponseFormat::Data
173 }
174}
175
176pub async fn update_password(
178 request: UpdatePasswordRequest,
179 config: &Config,
180 option: Option<RequestOption>,
181) -> SDKResult<BaseResponse<UpdatePasswordResponse>> {
182 let mut api_req = request.api_request;
183 api_req.http_method = Method::PUT;
184 api_req.api_path = EndpointBuilder::replace_param(
185 DRIVE_V1_PERMISSIONS_PUBLIC_PASSWORD,
186 "token",
187 &request.token,
188 );
189
190 api_req
192 .query_params
193 .insert(QueryParams::TYPE, request.obj_type);
194
195 api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
196
197 let api_resp = Transport::request(api_req, config, option).await?;
198 Ok(api_resp)
199}
200
201impl PasswordUpdateResult {
202 pub fn has_update_time(&self) -> bool {
204 self.update_time.is_some()
205 }
206
207 pub fn has_expire_time(&self) -> bool {
209 self.expire_time.is_some()
210 }
211
212 pub fn has_previous_hint(&self) -> bool {
214 self.previous_password_hint.is_some()
215 }
216
217 pub fn update_time_formatted(&self) -> Option<String> {
219 self.update_time
220 .map(|timestamp| format!("更新时间: {timestamp}"))
221 }
222
223 pub fn expire_time_formatted(&self) -> Option<String> {
225 self.expire_time
226 .map(|timestamp| format!("过期时间: {timestamp}"))
227 }
228
229 pub fn password_strength(&self) -> &'static str {
231 let password = &self.password;
232 let length = password.len();
233
234 if length < 6 {
235 "弱"
236 } else if length < 8 {
237 "中等"
238 } else if password.chars().any(|c| c.is_ascii_digit())
239 && password.chars().any(|c| c.is_ascii_alphabetic())
240 && password.chars().any(|c| !c.is_ascii_alphanumeric())
241 {
242 "很强"
243 } else if password.chars().any(|c| c.is_ascii_digit())
244 && password.chars().any(|c| c.is_ascii_alphabetic())
245 {
246 "强"
247 } else {
248 "中等"
249 }
250 }
251
252 pub fn is_numeric_password(&self) -> bool {
254 self.password.chars().all(|c| c.is_ascii_digit())
255 }
256
257 pub fn password_length(&self) -> usize {
259 self.password.len()
260 }
261
262 pub fn password_type(&self) -> &'static str {
264 let password = &self.password;
265
266 if password.chars().all(|c| c.is_ascii_digit()) {
267 "纯数字"
268 } else if password.chars().all(|c| c.is_ascii_alphabetic()) {
269 "纯字母"
270 } else if password.chars().any(|c| !c.is_ascii_alphanumeric()) {
271 "包含特殊字符"
272 } else {
273 "字母数字组合"
274 }
275 }
276
277 pub fn change_summary(&self) -> String {
279 let mut info = Vec::new();
280
281 info.push(format!("新密码长度: {}", self.password_length()));
282 info.push(format!("强度: {}", self.password_strength()));
283 info.push(format!("类型: {}", self.password_type()));
284
285 if let Some(ref hint) = self.previous_password_hint {
286 info.push(format!("原密码: {hint}"));
287 }
288
289 info.join(", ")
290 }
291
292 pub fn security_improvement(&self) -> &'static str {
294 match self.password_strength() {
295 "很强" => "密码安全性显著提升",
296 "强" => "密码安全性有所提升",
297 "中等" => "密码安全性一般",
298 "弱" => "建议使用更强的密码",
299 _ => "未知",
300 }
301 }
302}
303
304impl UpdatePasswordResponse {
305 pub fn password_info(&self) -> &PasswordUpdateResult {
307 &self.password
308 }
309
310 pub fn new_password(&self) -> &str {
312 &self.password.password
313 }
314
315 pub fn is_updated(&self) -> bool {
317 !self.password.password.is_empty()
318 }
319
320 pub fn update_summary(&self) -> String {
322 if self.is_updated() {
323 format!("密码已更新 - {}", self.password.change_summary())
324 } else {
325 "密码更新失败".to_string()
326 }
327 }
328
329 pub fn security_assessment(&self) -> String {
331 format!(
332 "安全评估: {} - {}",
333 self.password.password_strength(),
334 self.password.security_improvement()
335 )
336 }
337
338 pub fn security_recommendations(&self) -> Vec<String> {
340 let mut recommendations = Vec::new();
341
342 if self.password.is_numeric_password() {
343 recommendations.push("建议使用包含字母、数字和特殊字符的混合密码".to_string());
344 }
345
346 if self.password.password_length() < 8 {
347 recommendations.push("建议使用8位以上的密码".to_string());
348 }
349
350 if self.password.password_strength() == "弱" {
351 recommendations.push("当前密码强度较弱,建议立即更换为更复杂的密码".to_string());
352 }
353
354 recommendations.push("定期更换密码以提高安全性".to_string());
355 recommendations.push("请妥善保管新密码".to_string());
356
357 if self.password.has_expire_time() {
358 recommendations.push("密码有过期时间,请注意及时更新".to_string());
359 }
360
361 recommendations
362 }
363
364 pub fn operation_tips(&self) -> Vec<String> {
366 let mut tips = Vec::new();
367
368 tips.push("新密码已生效,旧密码立即失效".to_string());
369 tips.push("请及时通知相关人员密码变更".to_string());
370 tips.push("建议在安全环境下记录新密码".to_string());
371
372 if self.password.password_type() == "包含特殊字符" {
373 tips.push("输入密码时请注意特殊字符的准确性".to_string());
374 }
375
376 tips
377 }
378}
379
380#[cfg(test)]
381#[allow(unused_variables, unused_unsafe)]
382mod tests {
383 use super::*;
384
385 #[test]
386 fn test_update_password_request_builder() {
387 let request = UpdatePasswordRequest::builder()
388 .token("doccnxxxxxx")
389 .as_doc()
390 .password("newpassword123")
391 .build();
392
393 assert_eq!(request.token, "doccnxxxxxx");
394 assert_eq!(request.obj_type, "doc");
395 assert_eq!(request.password, "newpassword123");
396 }
397
398 #[test]
399 fn test_convenience_methods() {
400 let request = UpdatePasswordRequest::for_doc("doccnxxxxxx", "newpass456");
401 assert_eq!(request.obj_type, "doc");
402 assert_eq!(request.password, "newpass456");
403
404 let request = UpdatePasswordRequest::for_sheet("shtcnxxxxxx", "sheet789");
405 assert_eq!(request.obj_type, "sheet");
406 assert_eq!(request.password, "sheet789");
407 }
408
409 #[test]
410 fn test_password_builder_methods() {
411 let request = UpdatePasswordRequest::builder()
412 .token("doccnxxxxxx")
413 .as_doc()
414 .simple_password(789012)
415 .build();
416
417 assert_eq!(request.password, "789012");
418
419 let request = UpdatePasswordRequest::builder()
420 .token("doccnxxxxxx")
421 .as_doc()
422 .enhance_password("base")
423 .build();
424
425 assert!(request.password.starts_with("base@"));
426 assert!(request.password.len() > 5);
427 }
428
429 #[test]
430 fn test_password_update_result_methods() {
431 let result = PasswordUpdateResult {
432 password: "Complex@123".to_string(),
433 update_time: Some(1234567890),
434 expire_time: Some(1234567999),
435 previous_password_hint: Some("old****".to_string()),
436 };
437
438 assert!(result.has_update_time());
439 assert!(result.has_expire_time());
440 assert!(result.has_previous_hint());
441 assert!(!result.is_numeric_password());
442 assert_eq!(result.password_length(), 11);
443 assert_eq!(result.password_type(), "包含特殊字符");
444 assert_eq!(result.password_strength(), "很强");
445 assert_eq!(result.security_improvement(), "密码安全性显著提升");
446
447 let weak_result = PasswordUpdateResult {
448 password: "123".to_string(),
449 update_time: None,
450 expire_time: None,
451 previous_password_hint: None,
452 };
453
454 assert_eq!(weak_result.password_strength(), "弱");
455 assert_eq!(weak_result.security_improvement(), "建议使用更强的密码");
456 }
457}