use reqwest::Method;
use serde::{Deserialize, Serialize};
use crate::core::{
api_req::ApiRequest,
api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
config::Config,
constants::AccessTokenType,
endpoints::{cloud_docs::*, EndpointBuilder},
http::Transport,
query_params::QueryParams,
req_option::RequestOption,
SDKResult,
};
#[derive(Debug, Serialize, Default, Clone)]
pub struct DeletePasswordRequest {
#[serde(skip)]
api_request: ApiRequest,
#[serde(skip)]
token: String,
#[serde(skip)]
obj_type: String,
}
impl DeletePasswordRequest {
pub fn builder() -> DeletePasswordRequestBuilder {
DeletePasswordRequestBuilder::default()
}
pub fn new(token: impl ToString, obj_type: impl ToString) -> Self {
Self {
token: token.to_string(),
obj_type: obj_type.to_string(),
..Default::default()
}
}
pub fn for_doc(token: impl ToString) -> Self {
Self::new(token, "doc")
}
pub fn for_sheet(token: impl ToString) -> Self {
Self::new(token, "sheet")
}
pub fn for_bitable(token: impl ToString) -> Self {
Self::new(token, "bitable")
}
pub fn for_wiki(token: impl ToString) -> Self {
Self::new(token, "wiki")
}
}
#[derive(Default)]
pub struct DeletePasswordRequestBuilder {
request: DeletePasswordRequest,
}
impl DeletePasswordRequestBuilder {
pub fn token(mut self, token: impl ToString) -> Self {
self.request.token = token.to_string();
self
}
pub fn obj_type(mut self, obj_type: impl ToString) -> Self {
self.request.obj_type = obj_type.to_string();
self
}
pub fn as_doc(mut self) -> Self {
self.request.obj_type = "doc".to_string();
self
}
pub fn as_sheet(mut self) -> Self {
self.request.obj_type = "sheet".to_string();
self
}
pub fn as_bitable(mut self) -> Self {
self.request.obj_type = "bitable".to_string();
self
}
pub fn as_wiki(mut self) -> Self {
self.request.obj_type = "wiki".to_string();
self
}
pub fn build(mut self) -> DeletePasswordRequest {
self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
self.request
}
}
#[derive(Debug, Deserialize)]
pub struct PasswordDeletionResult {
pub delete_time: Option<i64>,
pub password_removed: bool,
pub previous_password_hint: Option<String>,
pub operation_id: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct DeletePasswordResponse {
pub password_deletion: PasswordDeletionResult,
}
impl ApiResponseTrait for DeletePasswordResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
pub async fn delete_password(
request: DeletePasswordRequest,
config: &Config,
option: Option<RequestOption>,
) -> SDKResult<BaseResponse<DeletePasswordResponse>> {
let mut api_req = request.api_request;
api_req.http_method = Method::DELETE;
api_req.api_path = EndpointBuilder::replace_param(
DRIVE_V1_PERMISSIONS_PUBLIC_PASSWORD,
"token",
&request.token,
);
api_req
.query_params
.insert(QueryParams::TYPE, request.obj_type);
api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
let api_resp = Transport::request(api_req, config, option).await?;
Ok(api_resp)
}
impl PasswordDeletionResult {
pub fn has_delete_time(&self) -> bool {
self.delete_time.is_some()
}
pub fn has_operation_id(&self) -> bool {
self.operation_id.is_some()
}
pub fn has_password_hint(&self) -> bool {
self.previous_password_hint.is_some()
}
pub fn delete_time_formatted(&self) -> Option<String> {
self.delete_time
.map(|timestamp| format!("删除时间: {timestamp}"))
}
pub fn is_successfully_removed(&self) -> bool {
self.password_removed
}
pub fn deletion_status(&self) -> &'static str {
if self.password_removed {
"密码保护已关闭"
} else {
"密码保护关闭失败"
}
}
pub fn deletion_summary(&self) -> String {
let mut info = Vec::new();
info.push(self.deletion_status().to_string());
if let Some(ref hint) = self.previous_password_hint {
info.push(format!("原密码: {hint}"));
}
if let Some(ref op_id) = self.operation_id {
info.push(format!("操作ID: {op_id}"));
}
info.join(", ")
}
pub fn security_impact(&self) -> &'static str {
if self.password_removed {
"文档安全级别降低,任何有链接的人都可以访问"
} else {
"密码保护仍然有效"
}
}
pub fn deletion_reasons(&self) -> Vec<String> {
if self.password_removed {
vec![
"不再需要密码保护".to_string(),
"密码管理复杂度降低".to_string(),
"提高访问便利性".to_string(),
"转为其他安全措施".to_string(),
]
} else {
vec![
"删除操作失败".to_string(),
"权限不足".to_string(),
"系统错误".to_string(),
]
}
}
}
impl DeletePasswordResponse {
pub fn deletion_info(&self) -> &PasswordDeletionResult {
&self.password_deletion
}
pub fn is_deleted(&self) -> bool {
self.password_deletion.password_removed
}
pub fn deletion_summary(&self) -> String {
self.password_deletion.deletion_summary()
}
pub fn security_assessment(&self) -> String {
format!("安全影响: {}", self.password_deletion.security_impact())
}
pub fn follow_up_recommendations(&self) -> Vec<String> {
let mut recommendations = Vec::new();
if self.is_deleted() {
recommendations.push("考虑其他安全措施,如限制分享范围".to_string());
recommendations.push("定期检查文档访问权限".to_string());
recommendations.push("如需要,可重新设置密码保护".to_string());
recommendations.push("通知相关人员密码保护已关闭".to_string());
} else {
recommendations.push("检查删除权限".to_string());
recommendations.push("稍后重试删除操作".to_string());
recommendations.push("联系管理员协助处理".to_string());
}
recommendations
}
pub fn security_warnings(&self) -> Vec<String> {
let mut warnings = Vec::new();
if self.is_deleted() {
warnings.push("⚠️ 密码保护已关闭,文档安全性降低".to_string());
warnings.push("⚠️ 任何获得链接的人都可以访问文档".to_string());
warnings.push("⚠️ 建议评估是否需要其他安全措施".to_string());
}
warnings
}
pub fn operation_log(&self) -> String {
let mut log_parts = Vec::new();
log_parts.push("操作: 关闭密码保护".to_string());
log_parts.push(format!(
"状态: {}",
self.password_deletion.deletion_status()
));
if let Some(time) = self.password_deletion.delete_time_formatted() {
log_parts.push(time);
}
if let Some(ref op_id) = self.password_deletion.operation_id {
log_parts.push(format!("操作ID: {op_id}"));
}
log_parts.join(", ")
}
}
#[cfg(test)]
#[allow(unused_variables, unused_unsafe)]
mod tests {
use super::*;
#[test]
fn test_delete_password_request_builder() {
let request = DeletePasswordRequest::builder()
.token("doccnxxxxxx")
.as_doc()
.build();
assert_eq!(request.token, "doccnxxxxxx");
assert_eq!(request.obj_type, "doc");
}
#[test]
fn test_convenience_methods() {
let request = DeletePasswordRequest::for_doc("doccnxxxxxx");
assert_eq!(request.obj_type, "doc");
assert_eq!(request.token, "doccnxxxxxx");
let request = DeletePasswordRequest::for_sheet("shtcnxxxxxx");
assert_eq!(request.obj_type, "sheet");
assert_eq!(request.token, "shtcnxxxxxx");
let request = DeletePasswordRequest::for_bitable("bblcnxxxxxx");
assert_eq!(request.obj_type, "bitable");
assert_eq!(request.token, "bblcnxxxxxx");
let request = DeletePasswordRequest::for_wiki("wikicnxxxxxx");
assert_eq!(request.obj_type, "wiki");
assert_eq!(request.token, "wikicnxxxxxx");
}
#[test]
fn test_password_deletion_result_methods() {
let result = PasswordDeletionResult {
delete_time: Some(1234567890),
password_removed: true,
previous_password_hint: Some("pass****".to_string()),
operation_id: Some("op123456".to_string()),
};
assert!(result.has_delete_time());
assert!(result.has_operation_id());
assert!(result.has_password_hint());
assert!(result.is_successfully_removed());
assert_eq!(result.deletion_status(), "密码保护已关闭");
assert_eq!(
result.security_impact(),
"文档安全级别降低,任何有链接的人都可以访问"
);
let failed_result = PasswordDeletionResult {
delete_time: None,
password_removed: false,
previous_password_hint: None,
operation_id: None,
};
assert!(!failed_result.is_successfully_removed());
assert_eq!(failed_result.deletion_status(), "密码保护关闭失败");
assert_eq!(failed_result.security_impact(), "密码保护仍然有效");
}
#[test]
fn test_delete_password_response_methods() {
let response = DeletePasswordResponse {
password_deletion: PasswordDeletionResult {
delete_time: Some(1234567890),
password_removed: true,
previous_password_hint: Some("old****".to_string()),
operation_id: Some("op789".to_string()),
},
};
assert!(response.is_deleted());
let warnings = response.security_warnings();
assert!(!warnings.is_empty());
assert!(warnings.iter().any(|w| w.contains("密码保护已关闭")));
let recommendations = response.follow_up_recommendations();
assert!(recommendations.iter().any(|r| r.contains("其他安全措施")));
}
}