openlark_core/auth/
token_provider.rs1use crate::{SDKResult, constants::AccessTokenType};
8use std::{future::Future, pin::Pin};
9
10#[derive(Debug, Clone, Default)]
14pub struct TokenRequest {
15 pub token_type: AccessTokenType,
17 pub tenant_key: Option<String>,
19 pub app_ticket: Option<String>,
21}
22
23impl TokenRequest {
24 pub fn app() -> Self {
26 Self {
27 token_type: AccessTokenType::App,
28 ..Default::default()
29 }
30 }
31
32 pub fn tenant() -> Self {
34 Self {
35 token_type: AccessTokenType::Tenant,
36 ..Default::default()
37 }
38 }
39
40 pub fn user() -> Self {
42 Self {
43 token_type: AccessTokenType::User,
44 ..Default::default()
45 }
46 }
47
48 pub fn tenant_key(mut self, tenant_key: impl Into<String>) -> Self {
50 self.tenant_key = Some(tenant_key.into());
51 self
52 }
53
54 pub fn app_ticket(mut self, app_ticket: impl Into<String>) -> Self {
56 self.app_ticket = Some(app_ticket.into());
57 self
58 }
59}
60
61pub trait TokenProvider: Send + Sync + std::fmt::Debug {
66 fn get_token(
68 &self,
69 request: TokenRequest,
70 ) -> Pin<Box<dyn Future<Output = SDKResult<String>> + Send + '_>>;
71
72 fn get_tenant_token(
74 &self,
75 tenant_key: Option<&str>,
76 ) -> Pin<Box<dyn Future<Output = SDKResult<String>> + Send + '_>> {
77 let tenant_key = tenant_key.map(str::to_owned);
78 Box::pin(async move {
79 let mut req = TokenRequest::tenant();
80 if let Some(key) = tenant_key.as_deref()
81 && !key.is_empty()
82 {
83 req = req.tenant_key(key);
84 }
85 self.get_token(req).await
86 })
87 }
88
89 fn get_app_token(&self) -> Pin<Box<dyn Future<Output = SDKResult<String>> + Send + '_>> {
91 Box::pin(async move { self.get_token(TokenRequest::app()).await })
92 }
93
94 fn get_user_token(&self) -> Pin<Box<dyn Future<Output = SDKResult<String>> + Send + '_>> {
96 Box::pin(async move { self.get_token(TokenRequest::user()).await })
97 }
98}
99
100#[derive(Debug)]
104pub struct NoOpTokenProvider;
105
106impl TokenProvider for NoOpTokenProvider {
107 fn get_token(
108 &self,
109 request: TokenRequest,
110 ) -> Pin<Box<dyn Future<Output = SDKResult<String>> + Send + '_>> {
111 Box::pin(async move {
112 Err(crate::error::configuration_error(format!(
113 "token_provider: NoOpTokenProvider 未实现 token 获取逻辑(请求:{request:?}),请在 Config 中设置 TokenProvider(建议使用 openlark-auth 提供的实现)。"
114 )))
115 })
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_token_request_app() {
125 let req = TokenRequest::app();
126 assert_eq!(req.token_type, AccessTokenType::App);
127 assert!(req.tenant_key.is_none());
128 assert!(req.app_ticket.is_none());
129 }
130
131 #[test]
132 fn test_token_request_tenant() {
133 let req = TokenRequest::tenant();
134 assert_eq!(req.token_type, AccessTokenType::Tenant);
135 }
136
137 #[test]
138 fn test_token_request_user() {
139 let req = TokenRequest::user();
140 assert_eq!(req.token_type, AccessTokenType::User);
141 }
142
143 #[test]
144 fn test_token_request_with_tenant_key() {
145 let req = TokenRequest::tenant().tenant_key("test_tenant");
146 assert_eq!(req.tenant_key, Some("test_tenant".to_string()));
147 }
148
149 #[test]
150 fn test_token_request_with_app_ticket() {
151 let req = TokenRequest::app().app_ticket("test_ticket");
152 assert_eq!(req.app_ticket, Some("test_ticket".to_string()));
153 }
154
155 #[test]
156 fn test_token_request_default() {
157 let req = TokenRequest::default();
158 assert_eq!(req.token_type, AccessTokenType::None);
159 }
160
161 #[test]
162 fn test_token_request_debug() {
163 let req = TokenRequest::app();
164 let debug_str = format!("{req:?}");
165 assert!(debug_str.contains("TokenRequest"));
166 }
167
168 #[tokio::test]
169 async fn test_no_op_token_provider_returns_error() {
170 let provider = NoOpTokenProvider;
171 let result = provider.get_token(TokenRequest::app()).await;
172 assert!(result.is_err());
173 }
174
175 #[test]
176 fn test_no_op_token_provider_debug() {
177 let provider = NoOpTokenProvider;
178 let debug_str = format!("{provider:?}");
179 assert!(debug_str.contains("NoOpTokenProvider"));
180 }
181}