open_lark/service/cloud_docs/permission/public_v1/password/
create.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 CreatePasswordRequest {
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 CreatePasswordRequest {
32    pub fn builder() -> CreatePasswordRequestBuilder {
33        CreatePasswordRequestBuilder::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 CreatePasswordRequestBuilder {
68    request: CreatePasswordRequest,
69}
70
71impl CreatePasswordRequestBuilder {
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    pub fn build(mut self) -> CreatePasswordRequest {
135        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
136        self.request
137    }
138}
139
140/// 密码创建结果
141#[derive(Debug, Deserialize)]
142pub struct PasswordResult {
143    /// 密码
144    pub password: String,
145    /// 创建时间
146    pub create_time: Option<i64>,
147    /// 过期时间(如果有)
148    pub expire_time: Option<i64>,
149}
150
151/// 开启密码保护响应
152#[derive(Debug, Deserialize)]
153pub struct CreatePasswordResponse {
154    /// 密码信息
155    pub password: PasswordResult,
156}
157
158impl ApiResponseTrait for CreatePasswordResponse {
159    fn data_format() -> ResponseFormat {
160        ResponseFormat::Data
161    }
162}
163
164/// 开启密码保护
165pub async fn create_password(
166    request: CreatePasswordRequest,
167    config: &Config,
168    option: Option<RequestOption>,
169) -> SDKResult<BaseResponse<CreatePasswordResponse>> {
170    let mut api_req = request.api_request;
171    api_req.http_method = Method::POST;
172    api_req.api_path = EndpointBuilder::replace_param(
173        DRIVE_V1_PERMISSIONS_PUBLIC_PASSWORD,
174        "token",
175        &request.token,
176    );
177
178    // 添加查询参数
179    api_req
180        .query_params
181        .insert(QueryParams::TYPE, request.obj_type);
182
183    api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
184
185    let api_resp = Transport::request(api_req, config, option).await?;
186    Ok(api_resp)
187}
188
189impl PasswordResult {
190    /// 是否有创建时间
191    pub fn has_create_time(&self) -> bool {
192        self.create_time.is_some()
193    }
194
195    /// 是否有过期时间
196    pub fn has_expire_time(&self) -> bool {
197        self.expire_time.is_some()
198    }
199
200    /// 获取创建时间格式化字符串
201    pub fn create_time_formatted(&self) -> Option<String> {
202        self.create_time
203            .map(|timestamp| format!("创建时间: {timestamp}"))
204    }
205
206    /// 获取过期时间格式化字符串
207    pub fn expire_time_formatted(&self) -> Option<String> {
208        self.expire_time
209            .map(|timestamp| format!("过期时间: {timestamp}"))
210    }
211
212    /// 密码强度评估
213    pub fn password_strength(&self) -> &'static str {
214        let password = &self.password;
215        let length = password.len();
216
217        if length < 6 {
218            "弱"
219        } else if length < 8 {
220            "中等"
221        } else if password.chars().any(|c| c.is_ascii_digit())
222            && password.chars().any(|c| c.is_ascii_alphabetic())
223        {
224            "强"
225        } else {
226            "中等"
227        }
228    }
229
230    /// 是否为数字密码
231    pub fn is_numeric_password(&self) -> bool {
232        self.password.chars().all(|c| c.is_ascii_digit())
233    }
234
235    /// 密码长度
236    pub fn password_length(&self) -> usize {
237        self.password.len()
238    }
239
240    /// 获取密码信息摘要
241    pub fn password_summary(&self) -> String {
242        format!(
243            "密码长度: {}, 强度: {}, 类型: {}",
244            self.password_length(),
245            self.password_strength(),
246            if self.is_numeric_password() {
247                "纯数字"
248            } else {
249                "混合字符"
250            }
251        )
252    }
253}
254
255impl CreatePasswordResponse {
256    /// 获取密码信息
257    pub fn password_info(&self) -> &PasswordResult {
258        &self.password
259    }
260
261    /// 获取实际密码
262    pub fn password_value(&self) -> &str {
263        &self.password.password
264    }
265
266    /// 是否创建成功
267    pub fn is_created(&self) -> bool {
268        !self.password.password.is_empty()
269    }
270
271    /// 获取创建摘要
272    pub fn creation_summary(&self) -> String {
273        if self.is_created() {
274            format!("密码保护已开启 - {}", self.password.password_summary())
275        } else {
276            "密码保护开启失败".to_string()
277        }
278    }
279
280    /// 安全提醒
281    pub fn security_tips(&self) -> Vec<String> {
282        let mut tips = Vec::new();
283
284        if self.password.is_numeric_password() {
285            tips.push("建议使用包含字母和数字的混合密码".to_string());
286        }
287
288        if self.password.password_length() < 8 {
289            tips.push("建议使用8位以上的密码".to_string());
290        }
291
292        tips.push("请妥善保管密码,遗失后需要重新设置".to_string());
293
294        if self.password.has_expire_time() {
295            tips.push("密码有过期时间,请注意及时更新".to_string());
296        }
297
298        tips
299    }
300
301    /// 获取操作建议
302    pub fn operation_recommendations(&self) -> Vec<String> {
303        let mut recommendations = Vec::new();
304
305        recommendations.push("建议定期更换密码".to_string());
306        recommendations.push("不要在不安全的环境中输入密码".to_string());
307        recommendations.push("可以设置更复杂的密码提高安全性".to_string());
308
309        if self.password.password_strength() == "弱" {
310            recommendations.push("当前密码强度较弱,建议立即更换".to_string());
311        }
312
313        recommendations
314    }
315}
316
317#[cfg(test)]
318#[allow(unused_variables, unused_unsafe)]
319mod tests {
320    use super::*;
321
322    #[test]
323    fn test_create_password_request_builder() {
324        let request = CreatePasswordRequest::builder()
325            .token("doccnxxxxxx")
326            .as_doc()
327            .password("123456")
328            .build();
329
330        assert_eq!(request.token, "doccnxxxxxx");
331        assert_eq!(request.obj_type, "doc");
332        assert_eq!(request.password, "123456");
333    }
334
335    #[test]
336    fn test_convenience_methods() {
337        let request = CreatePasswordRequest::for_doc("doccnxxxxxx", "password123");
338        assert_eq!(request.obj_type, "doc");
339        assert_eq!(request.password, "password123");
340
341        let request = CreatePasswordRequest::for_sheet("shtcnxxxxxx", "sheet456");
342        assert_eq!(request.obj_type, "sheet");
343        assert_eq!(request.password, "sheet456");
344    }
345
346    #[test]
347    fn test_password_builder_methods() {
348        let request = CreatePasswordRequest::builder()
349            .token("doccnxxxxxx")
350            .as_doc()
351            .simple_password(123456)
352            .build();
353
354        assert_eq!(request.password, "123456");
355    }
356
357    #[test]
358    fn test_password_result_methods() {
359        let result = PasswordResult {
360            password: "password123".to_string(),
361            create_time: Some(1234567890),
362            expire_time: None,
363        };
364
365        assert!(result.has_create_time());
366        assert!(!result.has_expire_time());
367        assert!(!result.is_numeric_password());
368        assert_eq!(result.password_length(), 11);
369        assert_eq!(result.password_strength(), "强");
370
371        let numeric_result = PasswordResult {
372            password: "123456".to_string(),
373            create_time: None,
374            expire_time: None,
375        };
376
377        assert!(numeric_result.is_numeric_password());
378        assert_eq!(numeric_result.password_strength(), "中等");
379    }
380}