mirror of
https://github.com/qdrant/qdrant.git
synced 2026-08-04 17:10:59 -05:00
122 lines
3.5 KiB
Rust
122 lines
3.5 KiB
Rust
use jsonwebtoken::errors::Error;
|
|
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
|
|
|
use super::claims::Claims;
|
|
|
|
#[derive(Clone)]
|
|
pub struct JwtParser {
|
|
key: DecodingKey,
|
|
validation: Validation,
|
|
}
|
|
|
|
impl JwtParser {
|
|
const ALGORITHM: Algorithm = Algorithm::HS256;
|
|
|
|
pub fn new(secret: &str) -> Self {
|
|
let key = DecodingKey::from_secret(secret.as_bytes());
|
|
let mut validation = Validation::new(Self::ALGORITHM);
|
|
|
|
// Qdrant server is the only audience
|
|
validation.validate_aud = false;
|
|
|
|
// Expiration time leeway to account for clock skew
|
|
validation.leeway = 30;
|
|
|
|
// All claims are optional
|
|
validation.required_spec_claims = Default::default();
|
|
|
|
JwtParser { key, validation }
|
|
}
|
|
|
|
/// Decode the token and return the claims, this already validates the `exp` claim with some leeway
|
|
pub fn decode(&self, token: &str) -> Result<Claims, Error> {
|
|
let claims = decode::<Claims>(token, &self.key, &self.validation)?.claims;
|
|
|
|
Ok(claims)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use segment::types::ValueVariants;
|
|
use storage::rbac::access::Access;
|
|
|
|
use super::*;
|
|
|
|
pub fn create_token(claims: &Claims) -> String {
|
|
use jsonwebtoken::{encode, EncodingKey, Header};
|
|
|
|
let key = EncodingKey::from_secret("secret".as_ref());
|
|
let header = Header::new(JwtParser::ALGORITHM);
|
|
encode(&header, claims, &key).unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn test_jwt_parser() {
|
|
let exp = std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.expect("Time went backwards")
|
|
.as_secs();
|
|
let claims = Claims {
|
|
exp: Some(exp),
|
|
w: Some(true),
|
|
access: Access {
|
|
collections: Some(vec!["collection".to_string()]),
|
|
payload: Some(
|
|
vec![
|
|
(
|
|
"field1".parse().unwrap(),
|
|
ValueVariants::Keyword("value".to_string()),
|
|
),
|
|
("field2".parse().unwrap(), ValueVariants::Integer(42)),
|
|
("field2".parse().unwrap(), ValueVariants::Bool(true)),
|
|
]
|
|
.into_iter()
|
|
.collect(),
|
|
),
|
|
},
|
|
value_exists: None,
|
|
};
|
|
let token = create_token(&claims);
|
|
|
|
let secret = "secret";
|
|
let parser = JwtParser::new(secret);
|
|
let decoded_claims = parser.decode(&token).unwrap();
|
|
|
|
assert_eq!(claims, decoded_claims);
|
|
}
|
|
|
|
#[test]
|
|
fn test_exp_validation() {
|
|
let exp = std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.expect("Time went backwards")
|
|
.as_secs()
|
|
- 31; // 31 seconds in the past, bigger than the 30 seconds leeway
|
|
|
|
let mut claims = Claims {
|
|
exp: Some(exp),
|
|
w: Some(false),
|
|
access: Access {
|
|
collections: None,
|
|
payload: None,
|
|
},
|
|
value_exists: None,
|
|
};
|
|
|
|
let token = create_token(&claims);
|
|
|
|
let secret = "secret";
|
|
let parser = JwtParser::new(secret);
|
|
assert!(parser.decode(&token).is_err());
|
|
|
|
// Remove the exp claim and it should work
|
|
claims.exp = None;
|
|
let token = create_token(&claims);
|
|
|
|
let decoded_claims = parser.decode(&token).unwrap();
|
|
|
|
assert_eq!(claims, decoded_claims);
|
|
}
|
|
}
|