Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+131
View File
@@ -0,0 +1,131 @@
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
use dever_runtime::{
adapter::Document,
auth::{Claims, Settings},
value::secret::Secret,
};
use hmac::{Hmac, Mac};
use sha2::Sha256;
#[cfg(feature = "authorization")]
mod entry;
mod metadata;
mod request;
mod tenant;
const KEY: &str = "unit-test-only-key-not-for-deployment";
fn settings() -> Settings {
let document = Document::parse(serde_json::json!({"auth":{"providers":{
"session":{"verify":"user.account.verify","jwtSecret":KEY,"ttlSeconds":60,"cookie":"session"},
"other":{"verify":"user.account.verify","jwtSecret":KEY,"ttlSeconds":60}
}}}).to_string()).unwrap();
Settings::load(&document).unwrap()
}
fn signed(header: &str, payload: &str, key: &str) -> Secret {
let signed = format!(
"{}.{}",
URL_SAFE_NO_PAD.encode(header),
URL_SAFE_NO_PAD.encode(payload)
);
let mut mac = Hmac::<Sha256>::new_from_slice(key.as_bytes()).unwrap();
mac.update(signed.as_bytes());
Secret::from_input(
format!(
"{signed}.{}",
URL_SAFE_NO_PAD.encode(mac.finalize().into_bytes())
)
.into_bytes(),
)
}
#[test]
fn signed_claims_remain_bound_to_provider_site_session_and_time() {
let settings = settings();
let provider = &settings.providers["session"];
let claims = Claims {
subject: "账号".into(),
session: "opaque-session".into(),
tenant: None,
site: "admin".into(),
};
let token = provider.issue(&claims, 100).unwrap();
assert_eq!(provider.verify_token(&token, "admin", 100).unwrap(), claims);
assert_eq!(provider.verify_token(&token, "admin", 159).unwrap(), claims);
assert!(provider.verify_token(&token, "admin", 160).is_err());
assert!(provider.verify_token(&token, "admin", 99).is_err());
assert!(provider.verify_token(&token, "front", 100).is_err());
assert!(
settings.providers["other"]
.verify_token(&token, "admin", 100)
.is_err()
);
assert!(!format!("{token:?}").contains("opaque-session"));
}
#[test]
fn token_validation_rejects_algorithm_confusion_tampering_and_ambiguous_claims() {
let settings = settings();
let provider = &settings.providers["session"];
let payload =
r#"{"iss":"session","aud":"admin","sub":"u","sid":"s","tenant":null,"iat":100,"exp":160}"#;
let header = r#"{"alg":"HS256","typ":"JWT"}"#;
assert!(
provider
.verify_token(&signed(header, payload, KEY), "admin", 100)
.is_ok()
);
for token in [
signed(r#"{"alg":"none","typ":"JWT"}"#, payload, KEY),
signed(r#"{"alg":"RS256","typ":"JWT"}"#, payload, KEY),
signed(header, payload, "different-unit-test-signing-key"),
signed(
header,
&payload.replace("\"sub\":\"u\"", "\"sub\":\"u\",\"sub\":\"v\""),
KEY,
),
signed(
header,
&payload.replace("\"sub\":\"u\"", "\"sub\":\"\""),
KEY,
),
signed(header, &payload.replace("\"exp\":160", "\"exp\":161"), KEY),
signed(header, &payload.replace("\"iat\":100", "\"iat\":101"), KEY),
signed(header, &payload.replace("\"iat\":100", "\"iat\":-1"), KEY),
signed(header, &payload.replace("\"exp\":160", "\"exp\":1e9"), KEY),
signed(
header,
&payload.replace("\"tenant\":null,", "\"permissions\":[\"*\"],"),
KEY,
),
Secret::from_input(vec![b'a'; 8193]),
] {
assert_eq!(
provider.verify_token(&token, "admin", 100).unwrap_err(),
"invalid authentication"
);
}
}
#[test]
fn authentication_settings_require_deployment_key_and_unambiguous_fields() {
for value in [
serde_json::json!({"verify":"user.account.verify","jwtSecret":"short","ttlSeconds":60}),
serde_json::json!({"verify":"user.account.verify","jwtSecret":KEY,"ttlSeconds":0}),
serde_json::json!({"verify":"user.account.verify","jwtSecret":KEY,"ttlSeconds":60,"cookie":"session; bad"}),
serde_json::json!({"verify":"user.account","jwtSecret":KEY,"ttlSeconds":60}),
serde_json::json!({"verify":"user.account.verify","jwtSecret":KEY,"ttlSeconds":60,"algorithm":"none"}),
] {
let document = Document::parse(
serde_json::json!({"auth":{"providers":{"session":value}}}).to_string(),
)
.unwrap();
assert!(Settings::load(&document).is_err());
}
assert!(
Settings::load(&Document::parse("{}".into()).unwrap())
.unwrap()
.providers
.is_empty()
);
}