openlark_core/auth/
token_provider.rs1use crate::{constants::AccessTokenType, SDKResult};
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 if !key.is_empty() {
82 req = req.tenant_key(key);
83 }
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(
113 format!(
114 "token_provider: NoOpTokenProvider 未实现 token 获取逻辑(请求:{:?}),请在 Config 中设置 TokenProvider(建议使用 openlark-auth 提供的实现)。",
115 request
116 ),
117 ))
118 })
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_token_request_app() {
128 let req = TokenRequest::app();
129 assert_eq!(req.token_type, AccessTokenType::App);
130 assert!(req.tenant_key.is_none());
131 assert!(req.app_ticket.is_none());
132 }
133
134 #[test]
135 fn test_token_request_tenant() {
136 let req = TokenRequest::tenant();
137 assert_eq!(req.token_type, AccessTokenType::Tenant);
138 }
139
140 #[test]
141 fn test_token_request_user() {
142 let req = TokenRequest::user();
143 assert_eq!(req.token_type, AccessTokenType::User);
144 }
145
146 #[test]
147 fn test_token_request_with_tenant_key() {
148 let req = TokenRequest::tenant().tenant_key("test_tenant");
149 assert_eq!(req.tenant_key, Some("test_tenant".to_string()));
150 }
151
152 #[test]
153 fn test_token_request_with_app_ticket() {
154 let req = TokenRequest::app().app_ticket("test_ticket");
155 assert_eq!(req.app_ticket, Some("test_ticket".to_string()));
156 }
157
158 #[test]
159 fn test_token_request_default() {
160 let req = TokenRequest::default();
161 assert_eq!(req.token_type, AccessTokenType::None);
162 }
163
164 #[test]
165 fn test_token_request_debug() {
166 let req = TokenRequest::app();
167 let debug_str = format!("{:?}", req);
168 assert!(debug_str.contains("TokenRequest"));
169 }
170
171 #[tokio::test]
172 async fn test_no_op_token_provider_returns_error() {
173 let provider = NoOpTokenProvider;
174 let result = provider.get_token(TokenRequest::app()).await;
175 assert!(result.is_err());
176 }
177
178 #[test]
179 fn test_no_op_token_provider_debug() {
180 let provider = NoOpTokenProvider;
181 let debug_str = format!("{:?}", provider);
182 assert!(debug_str.contains("NoOpTokenProvider"));
183 }
184}