mirror of
https://github.com/rustfs/rustfs.git
synced 2026-01-17 01:30:33 +00:00
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.
355 lines
12 KiB
Rust
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);
|
|
}
|