open_lark/service/cloud_docs/permission/public_v1/password/
update.rs

1use 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/// 刷新密码请求
17#[derive(Debug, Serialize, Default, Clone)]
18pub struct UpdatePasswordRequest {
19    #[serde(skip)]
20    api_request: ApiRequest,
21    /// 文档token
22    #[serde(skip)]
23    token: String,
24    /// 文档类型
25    #[serde(skip)]
26    obj_type: String,
27    /// 新密码
28    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    /// 刷新文档密码
46    pub fn for_doc(token: impl ToString, password: impl ToString) -> Self {
47        Self::new(token, "doc", password)
48    }
49
50    /// 刷新电子表格密码
51    pub fn for_sheet(token: impl ToString, password: impl ToString) -> Self {
52        Self::new(token, "sheet", password)
53    }
54
55    /// 刷新多维表格密码
56    pub fn for_bitable(token: impl ToString, password: impl ToString) -> Self {
57        Self::new(token, "bitable", password)
58    }
59
60    /// 刷新知识库密码
61    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    /// 文档token
73    pub fn token(mut self, token: impl ToString) -> Self {
74        self.request.token = token.to_string();
75        self
76    }
77
78    /// 文档类型
79    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    /// 设置为文档类型
85    pub fn as_doc(mut self) -> Self {
86        self.request.obj_type = "doc".to_string();
87        self
88    }
89
90    /// 设置为电子表格类型
91    pub fn as_sheet(mut self) -> Self {
92        self.request.obj_type = "sheet".to_string();
93        self
94    }
95
96    /// 设置为多维表格类型
97    pub fn as_bitable(mut self) -> Self {
98        self.request.obj_type = "bitable".to_string();
99        self
100    }
101
102    /// 设置为知识库类型
103    pub fn as_wiki(mut self) -> Self {
104        self.request.obj_type = "wiki".to_string();
105        self
106    }
107
108    /// 设置新密码
109    pub fn password(mut self, password: impl ToString) -> Self {
110        self.request.password = password.to_string();
111        self
112    }
113
114    /// 设置新的简单密码(6位数字)
115    pub fn simple_password(mut self, digits: u32) -> Self {
116        self.request.password = format!("{:06}", digits % 1000000);
117        self
118    }
119
120    /// 生成新的随机密码
121    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    /// 增强密码(在原密码基础上增加复杂度)
135    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/// 密码更新结果
151#[derive(Debug, Deserialize)]
152pub struct PasswordUpdateResult {
153    /// 新密码
154    pub password: String,
155    /// 更新时间
156    pub update_time: Option<i64>,
157    /// 过期时间(如果有)
158    pub expire_time: Option<i64>,
159    /// 上次密码(脱敏显示,如果有)
160    pub previous_password_hint: Option<String>,
161}
162
163/// 刷新密码响应
164#[derive(Debug, Deserialize)]
165pub struct UpdatePasswordResponse {
166    /// 密码更新信息
167    pub password: PasswordUpdateResult,
168}
169
170impl ApiResponseTrait for UpdatePasswordResponse {
171    fn data_format() -> ResponseFormat {
172        ResponseFormat::Data
173    }
174}
175
176/// 刷新密码
177pub 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    // 添加查询参数
191    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    /// 是否有更新时间
203    pub fn has_update_time(&self) -> bool {
204        self.update_time.is_some()
205    }
206
207    /// 是否有过期时间
208    pub fn has_expire_time(&self) -> bool {
209        self.expire_time.is_some()
210    }
211
212    /// 是否有上次密码提示
213    pub fn has_previous_hint(&self) -> bool {
214        self.previous_password_hint.is_some()
215    }
216
217    /// 获取更新时间格式化字符串
218    pub fn update_time_formatted(&self) -> Option<String> {
219        self.update_time
220            .map(|timestamp| format!("更新时间: {timestamp}"))
221    }
222
223    /// 获取过期时间格式化字符串
224    pub fn expire_time_formatted(&self) -> Option<String> {
225        self.expire_time
226            .map(|timestamp| format!("过期时间: {timestamp}"))
227    }
228
229    /// 密码强度评估
230    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    /// 是否为数字密码
253    pub fn is_numeric_password(&self) -> bool {
254        self.password.chars().all(|c| c.is_ascii_digit())
255    }
256
257    /// 密码长度
258    pub fn password_length(&self) -> usize {
259        self.password.len()
260    }
261
262    /// 密码类型
263    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    /// 获取密码变更摘要
278    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    /// 安全性改进评估
293    pub fn security_improvement(&self) -> &'static str {
294        match self.password_strength() {
295            "很强" => "密码安全性显著提升",
296            "强" => "密码安全性有所提升",
297            "中等" => "密码安全性一般",
298            "弱" => "建议使用更强的密码",
299            _ => "未知",
300        }
301    }
302}
303
304impl UpdatePasswordResponse {
305    /// 获取密码更新信息
306    pub fn password_info(&self) -> &PasswordUpdateResult {
307        &self.password
308    }
309
310    /// 获取新密码
311    pub fn new_password(&self) -> &str {
312        &self.password.password
313    }
314
315    /// 是否更新成功
316    pub fn is_updated(&self) -> bool {
317        !self.password.password.is_empty()
318    }
319
320    /// 获取更新摘要
321    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    /// 安全性评估
330    pub fn security_assessment(&self) -> String {
331        format!(
332            "安全评估: {} - {}",
333            self.password.password_strength(),
334            self.password.security_improvement()
335        )
336    }
337
338    /// 安全建议
339    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    /// 获取操作建议
365    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}