1use uuid::Uuid;
9use crate::config::{TokenStyle, SaTokenConfig};
10use crate::token::TokenValue;
11use crate::token::jwt::{JwtManager, JwtClaims, JwtAlgorithm};
12use chrono::Utc;
13use sha2::{Sha256, Sha512, Digest};
14
15pub struct TokenGenerator;
16
17impl TokenGenerator {
18 pub fn generate_with_login_id(config: &SaTokenConfig, login_id: &str) -> TokenValue {
25 match config.token_style {
26 TokenStyle::Uuid => Self::generate_uuid(),
27 TokenStyle::SimpleUuid => Self::generate_simple_uuid(),
28 TokenStyle::Random32 => Self::generate_random(32),
29 TokenStyle::Random64 => Self::generate_random(64),
30 TokenStyle::Random128 => Self::generate_random(128),
31 TokenStyle::Jwt => Self::generate_jwt(config, login_id),
32 TokenStyle::Hash => Self::generate_hash(login_id),
33 TokenStyle::Timestamp => Self::generate_timestamp(),
34 TokenStyle::Tik => Self::generate_tik(),
35 }
36 }
37
38 pub fn generate_with_login_id_and_extra(
49 config: &SaTokenConfig,
50 login_id: &str,
51 extra_data: &serde_json::Value,
52 ) -> TokenValue {
53 match config.token_style {
54 TokenStyle::Jwt => Self::generate_jwt_with_extra(config, login_id, extra_data),
55 _ => Self::generate_with_login_id(config, login_id),
57 }
58 }
59
60 pub fn generate(config: &SaTokenConfig) -> TokenValue {
62 Self::generate_with_login_id(config, "")
63 }
64
65 pub fn generate_uuid() -> TokenValue {
67 TokenValue::new(Uuid::new_v4().to_string())
68 }
69
70 pub fn generate_simple_uuid() -> TokenValue {
72 TokenValue::new(Uuid::new_v4().simple().to_string())
73 }
74
75 pub fn generate_random(length: usize) -> TokenValue {
77 let uuid = Uuid::new_v4();
78 let random_bytes = uuid.as_bytes();
79 let hash = Sha512::digest(random_bytes);
80 let hex_string = hex::encode(hash);
81 TokenValue::new(hex_string[..length.min(hex_string.len())].to_string())
82 }
83
84 pub fn generate_jwt(config: &SaTokenConfig, login_id: &str) -> TokenValue {
91 let effective_login_id = if login_id.is_empty() {
93 Utc::now().timestamp_millis().to_string()
94 } else {
95 login_id.to_string()
96 };
97
98 let secret = config.jwt_secret_key.as_ref()
100 .expect("JWT secret key is required when using JWT token style");
101
102 let algorithm = config.jwt_algorithm.as_ref()
104 .and_then(|alg| Self::parse_jwt_algorithm(alg))
105 .unwrap_or(JwtAlgorithm::HS256);
106
107 let mut jwt_manager = JwtManager::with_algorithm(secret, algorithm);
109
110 if let Some(ref issuer) = config.jwt_issuer {
111 jwt_manager = jwt_manager.set_issuer(issuer);
112 }
113
114 if let Some(ref audience) = config.jwt_audience {
115 jwt_manager = jwt_manager.set_audience(audience);
116 }
117
118 let mut claims = JwtClaims::new(effective_login_id);
120
121 if config.timeout > 0 {
123 claims.set_expiration(config.timeout);
124 }
125
126 match jwt_manager.generate(&claims) {
128 Ok(token) => TokenValue::new(token),
129 Err(e) => {
130 eprintln!("Failed to generate JWT token: {:?}", e);
131 Self::generate_uuid()
133 }
134 }
135 }
136
137 pub fn generate_jwt_with_extra(
148 config: &SaTokenConfig,
149 login_id: &str,
150 extra_data: &serde_json::Value,
151 ) -> TokenValue {
152 let effective_login_id = if login_id.is_empty() {
153 Utc::now().timestamp_millis().to_string()
154 } else {
155 login_id.to_string()
156 };
157
158 let secret = config.jwt_secret_key.as_ref()
159 .expect("JWT secret key is required when using JWT token style");
160
161 let algorithm = config.jwt_algorithm.as_ref()
162 .and_then(|alg| Self::parse_jwt_algorithm(alg))
163 .unwrap_or(JwtAlgorithm::HS256);
164
165 let mut jwt_manager = JwtManager::with_algorithm(secret, algorithm);
166
167 if let Some(ref issuer) = config.jwt_issuer {
168 jwt_manager = jwt_manager.set_issuer(issuer);
169 }
170
171 if let Some(ref audience) = config.jwt_audience {
172 jwt_manager = jwt_manager.set_audience(audience);
173 }
174
175 let mut claims = JwtClaims::new(effective_login_id);
176
177 if config.timeout > 0 {
178 claims.set_expiration(config.timeout);
179 }
180
181 match extra_data {
185 serde_json::Value::Object(map) => {
186 for (key, value) in map {
187 claims.add_claim(key.clone(), value.clone());
188 }
189 }
190 serde_json::Value::Null => {
191 }
193 other => {
194 claims.add_claim("extra", other.clone());
195 }
196 }
197
198 match jwt_manager.generate(&claims) {
199 Ok(token) => TokenValue::new(token),
200 Err(e) => {
201 eprintln!("Failed to generate JWT token with extra: {:?}", e);
202 Self::generate_uuid()
203 }
204 }
205 }
206
207 fn parse_jwt_algorithm(alg: &str) -> Option<JwtAlgorithm> {
209 match alg.to_uppercase().as_str() {
210 "HS256" => Some(JwtAlgorithm::HS256),
211 "HS384" => Some(JwtAlgorithm::HS384),
212 "HS512" => Some(JwtAlgorithm::HS512),
213 "RS256" => Some(JwtAlgorithm::RS256),
214 "RS384" => Some(JwtAlgorithm::RS384),
215 "RS512" => Some(JwtAlgorithm::RS512),
216 "ES256" => Some(JwtAlgorithm::ES256),
217 "ES384" => Some(JwtAlgorithm::ES384),
218 _ => None,
219 }
220 }
221
222 pub fn generate_hash(login_id: &str) -> TokenValue {
231 let login_id_value = if login_id.is_empty() {
233 Utc::now().timestamp_millis().to_string()
234 } else {
235 login_id.to_string()
236 };
237
238 let timestamp = Utc::now().timestamp_millis();
239 let uuid = Uuid::new_v4();
240 let data = format!("{}{}{}", login_id_value, timestamp, uuid);
241
242 let mut hasher = Sha256::new();
243 hasher.update(data.as_bytes());
244 let result = hasher.finalize();
245 let hash = hex::encode(result);
246
247 TokenValue::new(hash)
248 }
249
250 pub fn generate_timestamp() -> TokenValue {
258 use chrono::Utc;
259 use sha2::{Sha256, Digest};
260
261 let timestamp = Utc::now().timestamp_millis();
262 let uuid = Uuid::new_v4();
263
264 let mut hasher = Sha256::new();
266 hasher.update(uuid.as_bytes());
267 let result = hasher.finalize();
268 let suffix = hex::encode(&result[..8]); TokenValue::new(format!("{}_{}", timestamp, suffix))
271 }
272
273 pub fn generate_tik() -> TokenValue {
284 use sha2::{Sha256, Digest};
285
286 const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
287 const TOKEN_LENGTH: usize = 8;
288
289 let uuid = Uuid::new_v4();
290 let mut hasher = Sha256::new();
291 hasher.update(uuid.as_bytes());
292 let hash = hasher.finalize();
293
294 let mut token = String::with_capacity(TOKEN_LENGTH);
295 for i in 0..TOKEN_LENGTH {
296 let idx = (hash[i] as usize) % CHARSET.len();
297 token.push(CHARSET[idx] as char);
298 }
299
300 TokenValue::new(token)
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307 use crate::config::{SaTokenConfig, TokenStyle};
308 use crate::token::jwt::JwtManager;
309
310 fn jwt_config() -> SaTokenConfig {
311 SaTokenConfig {
312 token_style: TokenStyle::Jwt,
313 jwt_secret_key: Some("test-secret-key-for-jwt".to_string()),
314 timeout: 3600,
315 ..SaTokenConfig::default()
316 }
317 }
318
319 #[test]
320 fn test_generate_jwt_with_extra_object() {
321 let config = jwt_config();
322 let extra = serde_json::json!({
323 "role": "admin",
324 "tenant_id": 42,
325 "permissions": ["read", "write"]
326 });
327
328 let token = TokenGenerator::generate_jwt_with_extra(&config, "user_123", &extra);
329 assert!(!token.as_str().is_empty());
330
331 let jwt_manager = JwtManager::new("test-secret-key-for-jwt");
333 let claims = jwt_manager.validate(token.as_str()).unwrap();
334
335 assert_eq!(claims.login_id, "user_123");
336 assert_eq!(claims.get_claim("role"), Some(&serde_json::json!("admin")));
337 assert_eq!(claims.get_claim("tenant_id"), Some(&serde_json::json!(42)));
338 assert_eq!(
339 claims.get_claim("permissions"),
340 Some(&serde_json::json!(["read", "write"]))
341 );
342 }
343
344 #[test]
345 fn test_generate_jwt_with_extra_non_object() {
346 let config = jwt_config();
347 let extra = serde_json::json!("simple_string_value");
348
349 let token = TokenGenerator::generate_jwt_with_extra(&config, "user_456", &extra);
350
351 let jwt_manager = JwtManager::new("test-secret-key-for-jwt");
352 let claims = jwt_manager.validate(token.as_str()).unwrap();
353
354 assert_eq!(claims.login_id, "user_456");
355 assert_eq!(
356 claims.get_claim("extra"),
357 Some(&serde_json::json!("simple_string_value"))
358 );
359 }
360
361 #[test]
362 fn test_generate_jwt_with_extra_null() {
363 let config = jwt_config();
364 let extra = serde_json::Value::Null;
365
366 let token = TokenGenerator::generate_jwt_with_extra(&config, "user_789", &extra);
367
368 let jwt_manager = JwtManager::new("test-secret-key-for-jwt");
369 let claims = jwt_manager.validate(token.as_str()).unwrap();
370
371 assert_eq!(claims.login_id, "user_789");
372 assert!(claims.extra.is_empty());
373 }
374
375 #[test]
376 fn test_generate_with_login_id_and_extra_jwt_style() {
377 let config = jwt_config();
378 let extra = serde_json::json!({"key": "value"});
379
380 let token = TokenGenerator::generate_with_login_id_and_extra(&config, "user_jwt", &extra);
381
382 assert!(token.as_str().contains('.'));
384
385 let jwt_manager = JwtManager::new("test-secret-key-for-jwt");
386 let claims = jwt_manager.validate(token.as_str()).unwrap();
387 assert_eq!(claims.get_claim("key"), Some(&serde_json::json!("value")));
388 }
389
390 #[test]
391 fn test_generate_with_login_id_and_extra_non_jwt_style() {
392 let config = SaTokenConfig {
393 token_style: TokenStyle::Uuid,
394 ..SaTokenConfig::default()
395 };
396 let extra = serde_json::json!({"key": "value"});
397
398 let token = TokenGenerator::generate_with_login_id_and_extra(&config, "user_uuid", &extra);
400 assert!(!token.as_str().is_empty());
401 assert!(!token.as_str().contains('.'));
403 }
404
405
406 #[test]
407 fn test_random_32_length() {
408 let config = SaTokenConfig {
409 token_style: TokenStyle::Random32,
410 ..SaTokenConfig::default()
411 };
412 let token = TokenGenerator::generate_with_login_id(&config, "user_random");
413 assert!(!token.as_str().is_empty());
414 assert_eq!(token.as_str().len(), 32);
415 }
416
417 #[test]
418 fn test_random_64_length() {
419 let config = SaTokenConfig {
420 token_style: TokenStyle::Random64,
421 ..SaTokenConfig::default()
422 };
423 let token = TokenGenerator::generate_with_login_id(&config, "user_random");
424 assert!(!token.as_str().is_empty());
425 assert_eq!(token.as_str().len(), 64);
426 }
427
428 #[test]
429 fn test_random_128_length() {
430 let config = SaTokenConfig {
431 token_style: TokenStyle::Random128,
432 ..SaTokenConfig::default()
433 };
434 let token = TokenGenerator::generate_with_login_id(&config, "user_random");
435 assert!(!token.as_str().is_empty());
436 assert_eq!(token.as_str().len(), 128);
437 }
438}