mirror of
https://github.com/CloudNebulaProject/barycenter.git
synced 2026-04-10 13:10:42 +00:00
Format
Signed-off-by: Till Wegmueller <toasterson@gmail.com>
This commit is contained in:
parent
ecd6b00a1e
commit
a949a3cbdb
6 changed files with 144 additions and 86 deletions
|
|
@ -341,7 +341,10 @@ mod tests {
|
|||
|
||||
assert!(execution.completed_at.is_some());
|
||||
assert_eq!(execution.success, Some(0));
|
||||
assert_eq!(execution.error_message, Some("Test error message".to_string()));
|
||||
assert_eq!(
|
||||
execution.error_message,
|
||||
Some("Test error message".to_string())
|
||||
);
|
||||
assert!(execution.records_processed.is_none());
|
||||
}
|
||||
|
||||
|
|
|
|||
37
src/jwks.rs
37
src/jwks.rs
|
|
@ -102,8 +102,8 @@ fn random_kid() -> String {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::TempDir;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::TempDir;
|
||||
|
||||
/// Helper to create test Keys config
|
||||
fn test_keys_config(temp_dir: &TempDir) -> Keys {
|
||||
|
|
@ -156,10 +156,9 @@ mod tests {
|
|||
assert!(cfg.private_key_path.exists());
|
||||
|
||||
// Verify it contains valid JSON JWK
|
||||
let content = fs::read_to_string(&cfg.private_key_path)
|
||||
.expect("Failed to read private key");
|
||||
let jwk: Jwk = serde_json::from_str(&content)
|
||||
.expect("Failed to parse private key JSON");
|
||||
let content =
|
||||
fs::read_to_string(&cfg.private_key_path).expect("Failed to read private key");
|
||||
let jwk: Jwk = serde_json::from_str(&content).expect("Failed to parse private key JSON");
|
||||
|
||||
assert_eq!(jwk.key_type(), "RSA");
|
||||
assert!(jwk.parameter("d").is_some()); // Private exponent exists
|
||||
|
|
@ -178,10 +177,8 @@ mod tests {
|
|||
assert!(cfg.jwks_path.exists());
|
||||
|
||||
// Verify it contains valid JWKS structure
|
||||
let content = fs::read_to_string(&cfg.jwks_path)
|
||||
.expect("Failed to read JWKS");
|
||||
let jwks: Value = serde_json::from_str(&content)
|
||||
.expect("Failed to parse JWKS JSON");
|
||||
let content = fs::read_to_string(&cfg.jwks_path).expect("Failed to read JWKS");
|
||||
let jwks: Value = serde_json::from_str(&content).expect("Failed to parse JWKS JSON");
|
||||
|
||||
assert!(jwks.get("keys").is_some());
|
||||
assert!(jwks["keys"].is_array());
|
||||
|
|
@ -220,10 +217,7 @@ mod tests {
|
|||
let jwk1 = manager1.private_jwk();
|
||||
let jwk2 = manager2.private_jwk();
|
||||
|
||||
assert_eq!(
|
||||
jwk1.parameter("n"),
|
||||
jwk2.parameter("n")
|
||||
);
|
||||
assert_eq!(jwk1.parameter("n"), jwk2.parameter("n"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -239,11 +233,13 @@ mod tests {
|
|||
let mut payload = JwtPayload::new();
|
||||
payload.set_issuer("https://example.com");
|
||||
payload.set_subject("user123");
|
||||
let exp_time: std::time::SystemTime = (chrono::Utc::now() + chrono::Duration::hours(1)).into();
|
||||
let exp_time: std::time::SystemTime =
|
||||
(chrono::Utc::now() + chrono::Duration::hours(1)).into();
|
||||
payload.set_expires_at(&exp_time);
|
||||
|
||||
// Sign the JWT
|
||||
let token = manager.sign_jwt_rs256(&payload)
|
||||
let token = manager
|
||||
.sign_jwt_rs256(&payload)
|
||||
.expect("Failed to sign JWT");
|
||||
|
||||
// Verify token is not empty and has 3 parts (header.payload.signature)
|
||||
|
|
@ -252,10 +248,9 @@ mod tests {
|
|||
assert_eq!(parts.len(), 3);
|
||||
|
||||
// Decode and verify header contains kid
|
||||
let header_json = base64ct::Base64UrlUnpadded::decode_vec(parts[0])
|
||||
.expect("Failed to decode header");
|
||||
let header: Value = serde_json::from_slice(&header_json)
|
||||
.expect("Failed to parse header");
|
||||
let header_json =
|
||||
base64ct::Base64UrlUnpadded::decode_vec(parts[0]).expect("Failed to decode header");
|
||||
let header: Value = serde_json::from_slice(&header_json).expect("Failed to parse header");
|
||||
|
||||
assert_eq!(header["alg"], "RS256");
|
||||
assert_eq!(header["kid"], "test-kid-123");
|
||||
|
|
@ -311,7 +306,9 @@ mod tests {
|
|||
|
||||
// Verify all are valid base64url
|
||||
for kid in [kid1, kid2, kid3] {
|
||||
assert!(kid.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_'));
|
||||
assert!(kid
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,8 +160,8 @@ mod tests {
|
|||
let config_path = temp_dir.path().join("nonexistent.toml");
|
||||
|
||||
// Load settings with nonexistent file - should use defaults
|
||||
let settings = Settings::load(config_path.to_str().unwrap())
|
||||
.expect("Failed to load settings");
|
||||
let settings =
|
||||
Settings::load(config_path.to_str().unwrap()).expect("Failed to load settings");
|
||||
|
||||
assert_eq!(settings.server.host, "0.0.0.0");
|
||||
assert_eq!(settings.server.port, 8080);
|
||||
|
|
@ -194,8 +194,8 @@ private_key_path = "test_private.pem"
|
|||
fs::write(&config_path, config_content).expect("Failed to write config");
|
||||
|
||||
// Load settings
|
||||
let settings = Settings::load(config_path.to_str().unwrap())
|
||||
.expect("Failed to load settings");
|
||||
let settings =
|
||||
Settings::load(config_path.to_str().unwrap()).expect("Failed to load settings");
|
||||
|
||||
assert_eq!(settings.server.host, "127.0.0.1");
|
||||
assert_eq!(settings.server.port, 9090);
|
||||
|
|
@ -204,7 +204,10 @@ private_key_path = "test_private.pem"
|
|||
Some("https://idp.example.com".to_string())
|
||||
);
|
||||
assert_eq!(settings.server.allow_public_registration, true);
|
||||
assert_eq!(settings.database.url, "postgresql://user:pass@localhost/testdb");
|
||||
assert_eq!(
|
||||
settings.database.url,
|
||||
"postgresql://user:pass@localhost/testdb"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -225,8 +228,8 @@ port = 8080
|
|||
env::set_var("BARYCENTER__SERVER__HOST", "192.168.1.1");
|
||||
|
||||
// Load settings - env should override file
|
||||
let settings = Settings::load(config_path.to_str().unwrap())
|
||||
.expect("Failed to load settings");
|
||||
let settings =
|
||||
Settings::load(config_path.to_str().unwrap()).expect("Failed to load settings");
|
||||
|
||||
assert_eq!(settings.server.host, "192.168.1.1");
|
||||
assert_eq!(settings.server.port, 9999);
|
||||
|
|
@ -287,8 +290,8 @@ private_key_path = "relative/private.pem"
|
|||
"#;
|
||||
fs::write(&config_path, config_content).expect("Failed to write config");
|
||||
|
||||
let settings = Settings::load(config_path.to_str().unwrap())
|
||||
.expect("Failed to load settings");
|
||||
let settings =
|
||||
Settings::load(config_path.to_str().unwrap()).expect("Failed to load settings");
|
||||
|
||||
// Paths should be normalized to absolute
|
||||
assert!(settings.keys.jwks_path.is_absolute());
|
||||
|
|
@ -296,7 +299,10 @@ private_key_path = "relative/private.pem"
|
|||
|
||||
// Should end with the relative path components
|
||||
assert!(settings.keys.jwks_path.ends_with("relative/jwks.json"));
|
||||
assert!(settings.keys.private_key_path.ends_with("relative/private.pem"));
|
||||
assert!(settings
|
||||
.keys
|
||||
.private_key_path
|
||||
.ends_with("relative/private.pem"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
125
src/storage.rs
125
src/storage.rs
|
|
@ -5,7 +5,8 @@ use base64ct::Encoding;
|
|||
use chrono::Utc;
|
||||
use rand::RngCore;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, Database, DatabaseConnection, EntityTrait, QueryFilter, QueryOrder, Set,
|
||||
ActiveModelTrait, ColumnTrait, Database, DatabaseConnection, EntityTrait, QueryFilter,
|
||||
QueryOrder, Set,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
|
@ -884,10 +885,7 @@ pub async fn update_passkey_name(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_passkey(
|
||||
db: &DatabaseConnection,
|
||||
credential_id: &str,
|
||||
) -> Result<(), CrabError> {
|
||||
pub async fn delete_passkey(db: &DatabaseConnection, credential_id: &str) -> Result<(), CrabError> {
|
||||
use entities::passkey::{Column, Entity};
|
||||
|
||||
Entity::delete_many()
|
||||
|
|
@ -1204,7 +1202,7 @@ mod tests {
|
|||
Some("test_nonce".to_string()),
|
||||
"challenge_string",
|
||||
"S256",
|
||||
300, // 5 minutes TTL
|
||||
300, // 5 minutes TTL
|
||||
None, // auth_time
|
||||
)
|
||||
.await
|
||||
|
|
@ -1258,7 +1256,7 @@ mod tests {
|
|||
None,
|
||||
"",
|
||||
"",
|
||||
300, // TTL
|
||||
300, // TTL
|
||||
None, // auth_time
|
||||
)
|
||||
.await
|
||||
|
|
@ -1292,7 +1290,7 @@ mod tests {
|
|||
None,
|
||||
"",
|
||||
"",
|
||||
300, // TTL
|
||||
300, // TTL
|
||||
None, // auth_time
|
||||
)
|
||||
.await
|
||||
|
|
@ -1306,7 +1304,10 @@ mod tests {
|
|||
let past_timestamp = chrono::Utc::now().timestamp() - 600; // 10 minutes ago
|
||||
|
||||
Entity::update_many()
|
||||
.col_expr(Column::ExpiresAt, sea_orm::sea_query::Expr::value(past_timestamp))
|
||||
.col_expr(
|
||||
Column::ExpiresAt,
|
||||
sea_orm::sea_query::Expr::value(past_timestamp),
|
||||
)
|
||||
.filter(Column::Code.eq(&code.code))
|
||||
.exec(db)
|
||||
.await
|
||||
|
|
@ -1358,11 +1359,15 @@ mod tests {
|
|||
let test_db = TestDb::new().await;
|
||||
let db = test_db.connection();
|
||||
|
||||
let token = issue_access_token(&db, "test_client_id", "test_subject", "openid profile",
|
||||
let token = issue_access_token(
|
||||
&db,
|
||||
"test_client_id",
|
||||
"test_subject",
|
||||
"openid profile",
|
||||
3600, // TTL
|
||||
)
|
||||
.await
|
||||
.expect("Failed to issue access token");
|
||||
.await
|
||||
.expect("Failed to issue access token");
|
||||
|
||||
assert!(!token.token.is_empty());
|
||||
}
|
||||
|
|
@ -1372,11 +1377,15 @@ mod tests {
|
|||
let test_db = TestDb::new().await;
|
||||
let db = test_db.connection();
|
||||
|
||||
let token = issue_access_token(&db, "test_client_id", "test_subject", "openid profile",
|
||||
let token = issue_access_token(
|
||||
&db,
|
||||
"test_client_id",
|
||||
"test_subject",
|
||||
"openid profile",
|
||||
3600, // TTL
|
||||
)
|
||||
.await
|
||||
.expect("Failed to issue access token");
|
||||
.await
|
||||
.expect("Failed to issue access token");
|
||||
|
||||
let access_token = get_access_token(&db, &token.token)
|
||||
.await
|
||||
|
|
@ -1393,11 +1402,15 @@ mod tests {
|
|||
let test_db = TestDb::new().await;
|
||||
let db = test_db.connection();
|
||||
|
||||
let token = issue_access_token(&db, "test_client_id", "test_subject", "openid profile",
|
||||
let token = issue_access_token(
|
||||
&db,
|
||||
"test_client_id",
|
||||
"test_subject",
|
||||
"openid profile",
|
||||
3600, // TTL
|
||||
)
|
||||
.await
|
||||
.expect("Failed to issue access token");
|
||||
.await
|
||||
.expect("Failed to issue access token");
|
||||
|
||||
// Manually expire the token
|
||||
use entities::access_token::{Column, Entity};
|
||||
|
|
@ -1406,7 +1419,10 @@ mod tests {
|
|||
let past_timestamp = chrono::Utc::now().timestamp() - 7200; // 2 hours ago
|
||||
|
||||
Entity::update_many()
|
||||
.col_expr(Column::ExpiresAt, sea_orm::sea_query::Expr::value(past_timestamp))
|
||||
.col_expr(
|
||||
Column::ExpiresAt,
|
||||
sea_orm::sea_query::Expr::value(past_timestamp),
|
||||
)
|
||||
.filter(Column::Token.eq(&token.token))
|
||||
.exec(db)
|
||||
.await
|
||||
|
|
@ -1425,11 +1441,15 @@ mod tests {
|
|||
let test_db = TestDb::new().await;
|
||||
let db = test_db.connection();
|
||||
|
||||
let token = issue_access_token(&db, "test_client_id", "test_subject", "openid profile",
|
||||
let token = issue_access_token(
|
||||
&db,
|
||||
"test_client_id",
|
||||
"test_subject",
|
||||
"openid profile",
|
||||
3600, // TTL
|
||||
)
|
||||
.await
|
||||
.expect("Failed to issue access token");
|
||||
.await
|
||||
.expect("Failed to issue access token");
|
||||
|
||||
// Manually revoke the token
|
||||
use entities::access_token::{Column, Entity};
|
||||
|
|
@ -1456,17 +1476,28 @@ mod tests {
|
|||
let db = test_db.connection();
|
||||
|
||||
// Create initial refresh token
|
||||
let token1 = issue_refresh_token(&db, "test_subject", "test_client_id", "openid profile",
|
||||
let token1 = issue_refresh_token(
|
||||
&db,
|
||||
"test_subject",
|
||||
"test_client_id",
|
||||
"openid profile",
|
||||
86400, // TTL
|
||||
None, // parent_token
|
||||
None, // parent_token
|
||||
)
|
||||
.await
|
||||
.expect("Failed to issue refresh token");
|
||||
.await
|
||||
.expect("Failed to issue refresh token");
|
||||
|
||||
// Rotate to new token
|
||||
let token2 = issue_refresh_token(&db, "test_client_id", "test_subject", "openid profile", 86400, Some(token1.token.clone()))
|
||||
.await
|
||||
.expect("Failed to rotate refresh token");
|
||||
let token2 = issue_refresh_token(
|
||||
&db,
|
||||
"test_client_id",
|
||||
"test_subject",
|
||||
"openid profile",
|
||||
86400,
|
||||
Some(token1.token.clone()),
|
||||
)
|
||||
.await
|
||||
.expect("Failed to rotate refresh token");
|
||||
|
||||
// Verify parent chain
|
||||
let rt2 = get_refresh_token(&db, &token2.token)
|
||||
|
|
@ -1482,12 +1513,16 @@ mod tests {
|
|||
let test_db = TestDb::new().await;
|
||||
let db = test_db.connection();
|
||||
|
||||
let token = issue_refresh_token(&db, "test_subject", "test_client_id", "openid profile",
|
||||
let token = issue_refresh_token(
|
||||
&db,
|
||||
"test_subject",
|
||||
"test_client_id",
|
||||
"openid profile",
|
||||
86400, // TTL
|
||||
None, // parent_token
|
||||
None, // parent_token
|
||||
)
|
||||
.await
|
||||
.expect("Failed to issue refresh token");
|
||||
.await
|
||||
.expect("Failed to issue refresh token");
|
||||
|
||||
revoke_refresh_token(&db, &token.token)
|
||||
.await
|
||||
|
|
@ -1604,9 +1639,15 @@ mod tests {
|
|||
.await
|
||||
.expect("Failed to create user");
|
||||
|
||||
update_user(&db, &user.subject, false, Some("test@example.com".to_string()), Some(true))
|
||||
.await
|
||||
.expect("Failed to update user");
|
||||
update_user(
|
||||
&db,
|
||||
&user.subject,
|
||||
false,
|
||||
Some("test@example.com".to_string()),
|
||||
Some(true),
|
||||
)
|
||||
.await
|
||||
.expect("Failed to update user");
|
||||
|
||||
let updated = get_user_by_subject(&db, &user.subject)
|
||||
.await
|
||||
|
|
@ -1627,9 +1668,15 @@ mod tests {
|
|||
.await
|
||||
.expect("Failed to create user");
|
||||
|
||||
update_user(&db, &user.subject, true, Some("new@example.com".to_string()), None)
|
||||
.await
|
||||
.expect("Failed to update email");
|
||||
update_user(
|
||||
&db,
|
||||
&user.subject,
|
||||
true,
|
||||
Some("new@example.com".to_string()),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("Failed to update email");
|
||||
|
||||
let updated = get_user_by_subject(&db, &user.subject)
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -134,7 +134,11 @@ async fn sync_user(db: &DatabaseConnection, user_def: &UserDefinition) -> Result
|
|||
db,
|
||||
&existing_user.subject,
|
||||
user_def.enabled,
|
||||
if !email_matches { user_def.email.clone() } else { None },
|
||||
if !email_matches {
|
||||
user_def.email.clone()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
None, // requires_2fa not set during sync
|
||||
)
|
||||
.await
|
||||
|
|
|
|||
31
src/web.rs
31
src/web.rs
|
|
@ -1723,15 +1723,16 @@ async fn login_submit(
|
|||
.map(String::from);
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let session = match storage::create_session(&state.db, &subject, now, 3600, user_agent, None).await {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
let return_to = urlencoded(&form.return_to.unwrap_or_default());
|
||||
let error = urlencoded("Failed to create session");
|
||||
return Redirect::temporary(&format!("/login?error={error}&return_to={return_to}"))
|
||||
.into_response();
|
||||
}
|
||||
};
|
||||
let session =
|
||||
match storage::create_session(&state.db, &subject, now, 3600, user_agent, None).await {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
let return_to = urlencoded(&form.return_to.unwrap_or_default());
|
||||
let error = urlencoded("Failed to create session");
|
||||
return Redirect::temporary(&format!("/login?error={error}&return_to={return_to}"))
|
||||
.into_response();
|
||||
}
|
||||
};
|
||||
|
||||
// Set cookie
|
||||
let cookie = SessionCookie::new(session.session_id);
|
||||
|
|
@ -2126,12 +2127,12 @@ async fn passkey_register_finish(
|
|||
&cred_id_b64,
|
||||
&session.subject,
|
||||
&passkey_json,
|
||||
counter, // Extracted from passkey
|
||||
None, // aaguid
|
||||
backup_eligible, // Extracted from passkey
|
||||
backup_state, // Extracted from passkey
|
||||
None, // transports
|
||||
req.name.as_deref(), // Name from request
|
||||
counter, // Extracted from passkey
|
||||
None, // aaguid
|
||||
backup_eligible, // Extracted from passkey
|
||||
backup_state, // Extracted from passkey
|
||||
None, // transports
|
||||
req.name.as_deref(), // Name from request
|
||||
)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue