Files
zhenyi 821537186e refactor(tests): reformat code and update dependency management
- Reorganized import statements in adapter tests for better readability
- Replaced or_insert_with(Vec::new) with or_default() in test closures
- Updated Cargo.lock with new dependency versions and checksums
- Added TLS features to tonic dependency configuration
- Included sqlx, chrono, and uuid dependencies with specific features
- Added jsonwebtoken and arc-swap as project dependencies
- Reformatted assertion statements to comply with line length limits
- Adjusted base64 import order in engine codec module
- Updated protobuf include statement formatting
2026-06-11 12:11:05 +08:00

88 lines
2.5 KiB
Rust

//! JWT claims structure — mirrors proto `TokenClaims` for local verification.
//!
//! Used as the deserialization target for `jsonwebtoken::decode`.
//! Field names match standard JWT claim names (`sub`, `iss`, `exp`, etc.).
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
/// Parsed JWT payload, matching the proto `TokenClaims` shape.
///
/// Deserialized by `jsonwebtoken` during HS256 verification.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenClaims {
/// Subject — the user UUID.
pub sub: String,
/// Issuer — expected to be `"appks"`.
pub iss: String,
/// Issued-at (unix seconds).
pub iat: i64,
/// Expiration (unix seconds).
pub exp: i64,
/// Unique token ID (used for revocation tracking via `jti`).
pub jti: String,
/// Space-separated scopes, e.g. `"im:read im:write"`.
pub scope: String,
/// Extensible metadata (workspace_id, role, etc.).
#[serde(default)]
pub extra: HashMap<String, String>,
}
impl TokenClaims {
/// Check whether this token carries a specific scope.
pub fn has_scope(&self, scope: &str) -> bool {
self.scope.split_whitespace().any(|s| s == scope)
}
/// Convert from the proto-generated `TokenClaims` (RPC verify response).
pub fn from_proto(proto: crate::pb::core::TokenClaims) -> Self {
Self {
sub: proto.sub,
iss: proto.iss,
iat: proto.iat,
exp: proto.exp,
jti: proto.jti,
scope: proto.scope,
extra: proto.extra,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_has_scope() {
let claims = TokenClaims {
sub: "user-1".into(),
iss: "appks".into(),
iat: 0,
exp: 9999999999,
jti: "tok-1".into(),
scope: "im:read im:write admin".into(),
extra: HashMap::new(),
};
assert!(claims.has_scope("im:read"));
assert!(claims.has_scope("admin"));
assert!(!claims.has_scope("im:delete"));
}
#[test]
fn test_deserialize_from_json() {
let json = r#"{
"sub": "user-1",
"iss": "appks",
"iat": 1000,
"exp": 2000,
"jti": "tok-1",
"scope": "im:read",
"extra": {"workspace_id": "ws-1"}
}"#;
let claims: TokenClaims = serde_json::from_str(json).unwrap();
assert_eq!(claims.sub, "user-1");
assert_eq!(claims.extra.get("workspace_id").unwrap(), "ws-1");
}
}