open_lark/service/cloud_docs/permission/public_v1/password/
delete.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 DeletePasswordRequest {
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
29impl DeletePasswordRequest {
30    pub fn builder() -> DeletePasswordRequestBuilder {
31        DeletePasswordRequestBuilder::default()
32    }
33
34    pub fn new(token: impl ToString, obj_type: impl ToString) -> Self {
35        Self {
36            token: token.to_string(),
37            obj_type: obj_type.to_string(),
38            ..Default::default()
39        }
40    }
41
42    /// 关闭文档密码保护
43    pub fn for_doc(token: impl ToString) -> Self {
44        Self::new(token, "doc")
45    }
46
47    /// 关闭电子表格密码保护
48    pub fn for_sheet(token: impl ToString) -> Self {
49        Self::new(token, "sheet")
50    }
51
52    /// 关闭多维表格密码保护
53    pub fn for_bitable(token: impl ToString) -> Self {
54        Self::new(token, "bitable")
55    }
56
57    /// 关闭知识库密码保护
58    pub fn for_wiki(token: impl ToString) -> Self {
59        Self::new(token, "wiki")
60    }
61}
62
63#[derive(Default)]
64pub struct DeletePasswordRequestBuilder {
65    request: DeletePasswordRequest,
66}
67
68impl DeletePasswordRequestBuilder {
69    /// 文档token
70    pub fn token(mut self, token: impl ToString) -> Self {
71        self.request.token = token.to_string();
72        self
73    }
74
75    /// 文档类型
76    pub fn obj_type(mut self, obj_type: impl ToString) -> Self {
77        self.request.obj_type = obj_type.to_string();
78        self
79    }
80
81    /// 设置为文档类型
82    pub fn as_doc(mut self) -> Self {
83        self.request.obj_type = "doc".to_string();
84        self
85    }
86
87    /// 设置为电子表格类型
88    pub fn as_sheet(mut self) -> Self {
89        self.request.obj_type = "sheet".to_string();
90        self
91    }
92
93    /// 设置为多维表格类型
94    pub fn as_bitable(mut self) -> Self {
95        self.request.obj_type = "bitable".to_string();
96        self
97    }
98
99    /// 设置为知识库类型
100    pub fn as_wiki(mut self) -> Self {
101        self.request.obj_type = "wiki".to_string();
102        self
103    }
104
105    pub fn build(mut self) -> DeletePasswordRequest {
106        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
107        self.request
108    }
109}
110
111/// 密码删除结果
112#[derive(Debug, Deserialize)]
113pub struct PasswordDeletionResult {
114    /// 删除时间
115    pub delete_time: Option<i64>,
116    /// 密码是否已删除
117    pub password_removed: bool,
118    /// 删除前的密码提示(脱敏)
119    pub previous_password_hint: Option<String>,
120    /// 删除操作ID
121    pub operation_id: Option<String>,
122}
123
124/// 关闭密码保护响应
125#[derive(Debug, Deserialize)]
126pub struct DeletePasswordResponse {
127    /// 密码删除信息
128    pub password_deletion: PasswordDeletionResult,
129}
130
131impl ApiResponseTrait for DeletePasswordResponse {
132    fn data_format() -> ResponseFormat {
133        ResponseFormat::Data
134    }
135}
136
137/// 关闭密码保护
138pub async fn delete_password(
139    request: DeletePasswordRequest,
140    config: &Config,
141    option: Option<RequestOption>,
142) -> SDKResult<BaseResponse<DeletePasswordResponse>> {
143    let mut api_req = request.api_request;
144    api_req.http_method = Method::DELETE;
145    api_req.api_path = EndpointBuilder::replace_param(
146        DRIVE_V1_PERMISSIONS_PUBLIC_PASSWORD,
147        "token",
148        &request.token,
149    );
150
151    // 添加查询参数
152    api_req
153        .query_params
154        .insert(QueryParams::TYPE, request.obj_type);
155
156    api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
157
158    let api_resp = Transport::request(api_req, config, option).await?;
159    Ok(api_resp)
160}
161
162impl PasswordDeletionResult {
163    /// 是否有删除时间
164    pub fn has_delete_time(&self) -> bool {
165        self.delete_time.is_some()
166    }
167
168    /// 是否有操作ID
169    pub fn has_operation_id(&self) -> bool {
170        self.operation_id.is_some()
171    }
172
173    /// 是否有密码提示
174    pub fn has_password_hint(&self) -> bool {
175        self.previous_password_hint.is_some()
176    }
177
178    /// 获取删除时间格式化字符串
179    pub fn delete_time_formatted(&self) -> Option<String> {
180        self.delete_time
181            .map(|timestamp| format!("删除时间: {timestamp}"))
182    }
183
184    /// 是否删除成功
185    pub fn is_successfully_removed(&self) -> bool {
186        self.password_removed
187    }
188
189    /// 获取删除状态描述
190    pub fn deletion_status(&self) -> &'static str {
191        if self.password_removed {
192            "密码保护已关闭"
193        } else {
194            "密码保护关闭失败"
195        }
196    }
197
198    /// 获取删除操作摘要
199    pub fn deletion_summary(&self) -> String {
200        let mut info = Vec::new();
201
202        info.push(self.deletion_status().to_string());
203
204        if let Some(ref hint) = self.previous_password_hint {
205            info.push(format!("原密码: {hint}"));
206        }
207
208        if let Some(ref op_id) = self.operation_id {
209            info.push(format!("操作ID: {op_id}"));
210        }
211
212        info.join(", ")
213    }
214
215    /// 获取安全影响评估
216    pub fn security_impact(&self) -> &'static str {
217        if self.password_removed {
218            "文档安全级别降低,任何有链接的人都可以访问"
219        } else {
220            "密码保护仍然有效"
221        }
222    }
223
224    /// 删除原因建议
225    pub fn deletion_reasons(&self) -> Vec<String> {
226        if self.password_removed {
227            vec![
228                "不再需要密码保护".to_string(),
229                "密码管理复杂度降低".to_string(),
230                "提高访问便利性".to_string(),
231                "转为其他安全措施".to_string(),
232            ]
233        } else {
234            vec![
235                "删除操作失败".to_string(),
236                "权限不足".to_string(),
237                "系统错误".to_string(),
238            ]
239        }
240    }
241}
242
243impl DeletePasswordResponse {
244    /// 获取密码删除信息
245    pub fn deletion_info(&self) -> &PasswordDeletionResult {
246        &self.password_deletion
247    }
248
249    /// 是否删除成功
250    pub fn is_deleted(&self) -> bool {
251        self.password_deletion.password_removed
252    }
253
254    /// 获取删除摘要
255    pub fn deletion_summary(&self) -> String {
256        self.password_deletion.deletion_summary()
257    }
258
259    /// 安全性评估
260    pub fn security_assessment(&self) -> String {
261        format!("安全影响: {}", self.password_deletion.security_impact())
262    }
263
264    /// 后续操作建议
265    pub fn follow_up_recommendations(&self) -> Vec<String> {
266        let mut recommendations = Vec::new();
267
268        if self.is_deleted() {
269            recommendations.push("考虑其他安全措施,如限制分享范围".to_string());
270            recommendations.push("定期检查文档访问权限".to_string());
271            recommendations.push("如需要,可重新设置密码保护".to_string());
272            recommendations.push("通知相关人员密码保护已关闭".to_string());
273        } else {
274            recommendations.push("检查删除权限".to_string());
275            recommendations.push("稍后重试删除操作".to_string());
276            recommendations.push("联系管理员协助处理".to_string());
277        }
278
279        recommendations
280    }
281
282    /// 安全警告
283    pub fn security_warnings(&self) -> Vec<String> {
284        let mut warnings = Vec::new();
285
286        if self.is_deleted() {
287            warnings.push("⚠️ 密码保护已关闭,文档安全性降低".to_string());
288            warnings.push("⚠️ 任何获得链接的人都可以访问文档".to_string());
289            warnings.push("⚠️ 建议评估是否需要其他安全措施".to_string());
290        }
291
292        warnings
293    }
294
295    /// 获取操作记录
296    pub fn operation_log(&self) -> String {
297        let mut log_parts = Vec::new();
298
299        log_parts.push("操作: 关闭密码保护".to_string());
300        log_parts.push(format!(
301            "状态: {}",
302            self.password_deletion.deletion_status()
303        ));
304
305        if let Some(time) = self.password_deletion.delete_time_formatted() {
306            log_parts.push(time);
307        }
308
309        if let Some(ref op_id) = self.password_deletion.operation_id {
310            log_parts.push(format!("操作ID: {op_id}"));
311        }
312
313        log_parts.join(", ")
314    }
315}
316
317#[cfg(test)]
318#[allow(unused_variables, unused_unsafe)]
319mod tests {
320    use super::*;
321
322    #[test]
323    fn test_delete_password_request_builder() {
324        let request = DeletePasswordRequest::builder()
325            .token("doccnxxxxxx")
326            .as_doc()
327            .build();
328
329        assert_eq!(request.token, "doccnxxxxxx");
330        assert_eq!(request.obj_type, "doc");
331    }
332
333    #[test]
334    fn test_convenience_methods() {
335        let request = DeletePasswordRequest::for_doc("doccnxxxxxx");
336        assert_eq!(request.obj_type, "doc");
337        assert_eq!(request.token, "doccnxxxxxx");
338
339        let request = DeletePasswordRequest::for_sheet("shtcnxxxxxx");
340        assert_eq!(request.obj_type, "sheet");
341        assert_eq!(request.token, "shtcnxxxxxx");
342
343        let request = DeletePasswordRequest::for_bitable("bblcnxxxxxx");
344        assert_eq!(request.obj_type, "bitable");
345        assert_eq!(request.token, "bblcnxxxxxx");
346
347        let request = DeletePasswordRequest::for_wiki("wikicnxxxxxx");
348        assert_eq!(request.obj_type, "wiki");
349        assert_eq!(request.token, "wikicnxxxxxx");
350    }
351
352    #[test]
353    fn test_password_deletion_result_methods() {
354        let result = PasswordDeletionResult {
355            delete_time: Some(1234567890),
356            password_removed: true,
357            previous_password_hint: Some("pass****".to_string()),
358            operation_id: Some("op123456".to_string()),
359        };
360
361        assert!(result.has_delete_time());
362        assert!(result.has_operation_id());
363        assert!(result.has_password_hint());
364        assert!(result.is_successfully_removed());
365        assert_eq!(result.deletion_status(), "密码保护已关闭");
366        assert_eq!(
367            result.security_impact(),
368            "文档安全级别降低,任何有链接的人都可以访问"
369        );
370
371        let failed_result = PasswordDeletionResult {
372            delete_time: None,
373            password_removed: false,
374            previous_password_hint: None,
375            operation_id: None,
376        };
377
378        assert!(!failed_result.is_successfully_removed());
379        assert_eq!(failed_result.deletion_status(), "密码保护关闭失败");
380        assert_eq!(failed_result.security_impact(), "密码保护仍然有效");
381    }
382
383    #[test]
384    fn test_delete_password_response_methods() {
385        let response = DeletePasswordResponse {
386            password_deletion: PasswordDeletionResult {
387                delete_time: Some(1234567890),
388                password_removed: true,
389                previous_password_hint: Some("old****".to_string()),
390                operation_id: Some("op789".to_string()),
391            },
392        };
393
394        assert!(response.is_deleted());
395        let warnings = response.security_warnings();
396        assert!(!warnings.is_empty());
397        assert!(warnings.iter().any(|w| w.contains("密码保护已关闭")));
398
399        let recommendations = response.follow_up_recommendations();
400        assert!(recommendations.iter().any(|r| r.contains("其他安全措施")));
401    }
402}