Files
rustfs/crates/crypto/src/jwt/tests.rs
houseme 5826396cd0 refactor: Restructure project layout and clean up dependencies (#30)
This commit introduces a significant reorganization of the project structure to improve maintainability and clarity.

Key changes include:
- Adjusted the directory layout for a more logical module organization.
- Removed unused crate dependencies, reducing the overall project size and potentially speeding up build times.
- Updated import paths and configuration files to reflect the structural changes.
2025-07-02 19:33:12 +08:00

355 lines
12 KiB
Rust

// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use serde_json::json;
use time::OffsetDateTime;
use super::{decode::decode, encode::encode};
#[test]
fn test_jwt_encode_decode_basic() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user123",
"iat": OffsetDateTime::now_utc().unix_timestamp(),
"role": "admin"
});
let secret = b"test_secret_key";
let jwt_token = encode(secret, &claims).expect("Failed to encode JWT");
let decoded = decode(&jwt_token, secret).expect("Failed to decode JWT");
assert_eq!(decoded.claims, claims);
}
#[test]
fn test_jwt_encode_decode_with_complex_claims() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 3600,
"sub": "user456",
"iat": OffsetDateTime::now_utc().unix_timestamp(),
"permissions": ["read", "write", "delete"],
"metadata": {
"department": "engineering",
"level": 5,
"active": true
},
"custom_field": null
});
let secret = b"complex_secret_key_123";
let jwt_token = encode(secret, &claims).expect("Failed to encode complex JWT");
let decoded = decode(&jwt_token, secret).expect("Failed to decode complex JWT");
assert_eq!(decoded.claims, claims);
}
#[test]
fn test_jwt_decode_with_wrong_secret() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user123"
});
let correct_secret = b"correct_secret";
let wrong_secret = b"wrong_secret";
let jwt_token = encode(correct_secret, &claims).expect("Failed to encode JWT");
// Decoding with wrong secret should fail
let result = decode(&jwt_token, wrong_secret);
assert!(result.is_err(), "Decoding with wrong secret should fail");
}
#[test]
fn test_jwt_decode_invalid_token_format() {
let secret = b"test_secret";
// Test various invalid token formats
let invalid_tokens = [
"", // Empty token
"invalid", // Not a JWT format
"header.payload", // Missing signature
"header.payload.signature.extra", // Too many parts
"invalid.header.signature", // Invalid base64
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.invalid.signature", // Invalid payload
];
for invalid_token in &invalid_tokens {
let result = decode(invalid_token, secret);
assert!(result.is_err(), "Invalid token '{invalid_token}' should fail to decode");
}
}
#[test]
fn test_jwt_with_expired_token() {
let expired_claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() - 1000, // Expired 1000 seconds ago
"sub": "user123",
"iat": OffsetDateTime::now_utc().unix_timestamp() - 2000
});
let secret = b"test_secret";
let jwt_token = encode(secret, &expired_claims).expect("Failed to encode expired JWT");
// Decoding expired token should fail
let result = decode(&jwt_token, secret);
assert!(result.is_err(), "Expired token should fail to decode");
}
#[test]
fn test_jwt_with_future_issued_at() {
let future_claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 3600,
"sub": "user123",
"iat": OffsetDateTime::now_utc().unix_timestamp() + 1000 // Issued in future
});
let secret = b"test_secret";
let jwt_token = encode(secret, &future_claims).expect("Failed to encode future JWT");
// Note: The current JWT implementation may not validate iat by default
// This test documents the current behavior - future iat tokens may still decode successfully
let result = decode(&jwt_token, secret);
// For now, we just verify the token can be decoded, but in a production system
// you might want to add custom validation for iat claims
assert!(
result.is_ok(),
"Token decoding should succeed, but iat validation should be handled separately"
);
}
#[test]
fn test_jwt_with_empty_claims() {
let empty_claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000, // Add required exp claim
});
let secret = b"test_secret";
let jwt_token = encode(secret, &empty_claims).expect("Failed to encode empty claims JWT");
let decoded = decode(&jwt_token, secret).expect("Failed to decode empty claims JWT");
assert_eq!(decoded.claims, empty_claims);
}
#[test]
fn test_jwt_with_different_secret_lengths() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user123"
});
// Test with various secret lengths
let secrets = [
b"a".as_slice(), // Very short
b"short_key".as_slice(), // Short
b"medium_length_secret_key".as_slice(), // Medium
b"very_long_secret_key_with_many_characters_for_testing_purposes".as_slice(), // Long
];
for secret in &secrets {
let jwt_token =
encode(secret, &claims).unwrap_or_else(|_| panic!("Failed to encode JWT with secret length {}", secret.len()));
let decoded =
decode(&jwt_token, secret).unwrap_or_else(|_| panic!("Failed to decode JWT with secret length {}", secret.len()));
assert_eq!(decoded.claims, claims);
}
}
#[test]
fn test_jwt_with_special_characters_in_claims() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user@example.com",
"name": "John Doe",
"description": "User with special chars: !@#$%^&*()_+-=[]{}|;':\",./<>?",
"unicode": "测试用户 🚀 émojis",
"newlines": "line1\nline2\r\nline3",
"quotes": "He said \"Hello\" and she replied 'Hi'"
});
let secret = b"test_secret";
let jwt_token = encode(secret, &claims).expect("Failed to encode JWT with special characters");
let decoded = decode(&jwt_token, secret).expect("Failed to decode JWT with special characters");
assert_eq!(decoded.claims, claims);
}
#[test]
fn test_jwt_with_large_payload() {
// Create a large payload to test size limits
let large_data = "x".repeat(10000); // 10KB of data
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user123",
"large_field": large_data
});
let secret = b"test_secret";
let jwt_token = encode(secret, &claims).expect("Failed to encode large JWT");
let decoded = decode(&jwt_token, secret).expect("Failed to decode large JWT");
assert_eq!(decoded.claims, claims);
}
#[test]
fn test_jwt_token_structure() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user123"
});
let secret = b"test_secret";
let jwt_token = encode(secret, &claims).expect("Failed to encode JWT");
// JWT should have exactly 3 parts separated by dots
let parts: Vec<&str> = jwt_token.split('.').collect();
assert_eq!(parts.len(), 3, "JWT should have exactly 3 parts");
// Each part should be non-empty
for (i, part) in parts.iter().enumerate() {
assert!(!part.is_empty(), "JWT part {i} should not be empty");
}
}
#[test]
fn test_jwt_deterministic_encoding() {
let claims = json!({
"exp": 1234567890, // Fixed timestamp for deterministic test
"sub": "user123",
"iat": 1234567800
});
let secret = b"test_secret";
// Encode the same claims multiple times
let token1 = encode(secret, &claims).expect("Failed to encode JWT 1");
let token2 = encode(secret, &claims).expect("Failed to encode JWT 2");
// Tokens should be identical for same input
assert_eq!(token1, token2, "JWT encoding should be deterministic");
}
#[test]
fn test_jwt_cross_compatibility() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user123"
});
let secret1 = b"secret1";
let secret2 = b"secret2";
// Encode with secret1
let token1 = encode(secret1, &claims).expect("Failed to encode with secret1");
// Decode with secret1 should work
let decoded1 = decode(&token1, secret1).expect("Failed to decode with correct secret");
assert_eq!(decoded1.claims, claims);
// Decode with secret2 should fail
let result2 = decode(&token1, secret2);
assert!(result2.is_err(), "Decoding with different secret should fail");
}
#[test]
fn test_jwt_header_algorithm() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user123"
});
let secret = b"test_secret";
let jwt_token = encode(secret, &claims).expect("Failed to encode JWT");
let decoded = decode(&jwt_token, secret).expect("Failed to decode JWT");
// Verify the algorithm in header is HS512
assert_eq!(decoded.header.alg, jsonwebtoken::Algorithm::HS512);
assert_eq!(decoded.header.typ, Some("JWT".to_string()));
}
#[test]
fn test_jwt_claims_validation() {
let now = OffsetDateTime::now_utc().unix_timestamp();
let valid_claims = json!({
"exp": now + 3600, // Expires in 1 hour
"iat": now - 60, // Issued 1 minute ago
"nbf": now - 30, // Not before 30 seconds ago
"sub": "user123"
});
let secret = b"test_secret";
let jwt_token = encode(secret, &valid_claims).expect("Failed to encode valid JWT");
let decoded = decode(&jwt_token, secret).expect("Failed to decode valid JWT");
assert_eq!(decoded.claims, valid_claims);
}
#[test]
fn test_jwt_with_numeric_claims() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user123",
"age": 25,
"score": 95.5,
"count": 0,
"negative": -10
});
let secret = b"test_secret";
let jwt_token = encode(secret, &claims).expect("Failed to encode numeric JWT");
let decoded = decode(&jwt_token, secret).expect("Failed to decode numeric JWT");
assert_eq!(decoded.claims, claims);
}
#[test]
fn test_jwt_with_boolean_claims() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user123",
"is_admin": true,
"is_active": false,
"email_verified": true
});
let secret = b"test_secret";
let jwt_token = encode(secret, &claims).expect("Failed to encode boolean JWT");
let decoded = decode(&jwt_token, secret).expect("Failed to decode boolean JWT");
assert_eq!(decoded.claims, claims);
}
#[test]
fn test_jwt_with_array_claims() {
let claims = json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"sub": "user123",
"roles": ["admin", "user", "moderator"],
"permissions": [1, 2, 3, 4, 5],
"tags": [],
"mixed_array": ["string", 123, true, null]
});
let secret = b"test_secret";
let jwt_token = encode(secret, &claims).expect("Failed to encode array JWT");
let decoded = decode(&jwt_token, secret).expect("Failed to decode array JWT");
assert_eq!(decoded.claims, claims);
}