Merge branch 'R2024' into 'main' indicateur Rust 2024 See merge request ebii/rustwarden!5

Eric BOUHANA <eric@ebii.fr> committé le 2026-05-09 10:43

e66c998030eca13b8a475bd499a013a0602f9a52

2 parent(s)

16 fichiers modifiés +61 -35
M Cargo.toml
+4 -0
@@ -7,6 +7,10 @@ members = [
7 7 ]
8 8 resolver = "2"
9 9
10 +[workspace.package]
11 +edition = "2024"
12 +rust-version = "1.85"
13 +
10 14 [workspace.lints.clippy]
11 15 empty_line_after_doc_comments = "allow"
12 16
M app/Cargo.toml
+2 -1
@@ -1,7 +1,8 @@
1 1 [package]
2 2 name = "rustwarden-app"
3 3 version = "0.1.0"
4 -edition = "2021"
4 +edition.workspace = true
5 +rust-version.workspace = true
5 6 description = "Reference application using the Rustwarden framework"
6 7
7 8 [[bin]]
M crates/rustwarden-core/Cargo.toml
+2 -1
@@ -1,7 +1,8 @@
1 1 [package]
2 2 name = "rustwarden-core"
3 3 version = "0.1.0"
4 -edition = "2021"
4 +edition.workspace = true
5 +rust-version.workspace = true
5 6 description = "Core library for Rustwarden – authentication, RBAC, user management framework"
6 7
7 8 [dependencies]
M crates/rustwarden-core/src/app.rs
+6 -1
@@ -261,7 +261,12 @@ impl RustwardenBuilder {
261 261 db
262 262 } else {
263 263 if let Some(ref url) = self.database_url {
264 - std::env::set_var("DATABASE_URL", url);
264 + // SAFETY: `build()` runs during single-threaded application
265 + // initialization, before any worker tasks or background threads
266 + // are spawned, so mutating the process environment is sound.
267 + unsafe {
268 + std::env::set_var("DATABASE_URL", url);
269 + }
265 270 }
266 271 database::create_connection().await?
267 272 };
M crates/rustwarden-core/src/auth/oauth_handlers.rs
+3 -0
@@ -96,6 +96,9 @@ pub async fn oauth_redirect(
96 96 /// Handles the OAuth callback from the provider.
97 97 /// Validates state, exchanges code for tokens, finds or creates user,
98 98 /// generates JWT + refresh token, sets cookies, redirects to /home.
99 +// Axum handler: each argument is a distinct extractor; factoring them into a
100 +// combined extractor would obscure the contract for marginal benefit.
101 +#[allow(clippy::too_many_arguments)]
99 102 pub async fn oauth_callback(
100 103 State(db): State<DatabaseConnection>,
101 104 headers: HeaderMap,
M crates/rustwarden-core/src/middleware/admin_helper.rs
+1 -1
@@ -53,7 +53,7 @@ pub async fn extract_admin_claims_from_parts(
53 53 AppError::Config("Auth configuration not available".to_string())
54 54 })?;
55 55
56 - let claims = JwtService::validate_token(&*auth_config, &jwt_token).map_err(|_| {
56 + let claims = JwtService::validate_token(&auth_config, &jwt_token).map_err(|_| {
57 57 tracing::warn!("Invalid JWT token for admin route");
58 58 AppError::Unauthorized
59 59 })?;
M crates/rustwarden-core/src/middleware/admin_service.rs
+1 -1
@@ -101,7 +101,7 @@ where
101 101 }
102 102 };
103 103
104 - let claims = match JwtService::validate_token(&*auth_config, &jwt_token) {
104 + let claims = match JwtService::validate_token(&auth_config, &jwt_token) {
105 105 Ok(claims) => claims,
106 106 Err(_) => {
107 107 let redirect_url = auth_service::sanitize_redirect(uri.path());
M crates/rustwarden-core/src/middleware/auth_required.rs
+3 -3
@@ -70,7 +70,7 @@ async fn extract_user_from_jwt_impl(db: &DatabaseConnection, request: &Request)
70 70 }
71 71
72 72 // Validate JWT token
73 - match JwtService::validate_token(&*auth_config, &jwt_token) {
73 + match JwtService::validate_token(&auth_config, &jwt_token) {
74 74 Ok(claims) => {
75 75 tracing::debug!("User extracted from JWT: {}", claims.sub);
76 76 Some(claims)
@@ -150,7 +150,7 @@ pub async fn auth_required_impl(
150 150 }
151 151
152 152 // Validate JWT token
153 - let claims = match JwtService::validate_token(&*auth_config, &jwt_token) {
153 + let claims = match JwtService::validate_token(&auth_config, &jwt_token) {
154 154 Ok(claims) => claims,
155 155 Err(e) => {
156 156 tracing::warn!("Invalid JWT token for {}: {}", uri, e);
@@ -224,7 +224,7 @@ pub async fn optional_auth(
224 224 .await
225 225 .is_ok_and(|b| !b)
226 226 {
227 - if let Ok(claims) = JwtService::validate_token(&*auth_config, &jwt_token) {
227 + if let Ok(claims) = JwtService::validate_token(&auth_config, &jwt_token) {
228 228 tracing::debug!("User authenticated: {} (from JWT)", claims.sub);
229 229 }
230 230 }
M crates/rustwarden-core/src/middleware/auth_service.rs
+2 -2
@@ -107,7 +107,7 @@ where
107 107 }
108 108 };
109 109
110 - let claims = match JwtService::validate_token(&*auth_config, &jwt_token) {
110 + let claims = match JwtService::validate_token(&auth_config, &jwt_token) {
111 111 Ok(claims) => claims,
112 112 Err(_) => {
113 113 tracing::warn!("AuthService: Invalid JWT token, redirecting to login");
@@ -232,7 +232,7 @@ where
232 232
233 233 if !is_blacklisted {
234 234 if let Some(auth_config) = parts.extensions.get::<std::sync::Arc<AuthConfig>>().cloned() {
235 - if let Ok(claims) = JwtService::validate_token(&*auth_config, &jwt_token)
235 + if let Ok(claims) = JwtService::validate_token(&auth_config, &jwt_token)
236 236 {
237 237 parts.extensions.insert(claims.clone());
238 238 if let Ok(json) = serde_json::to_string(&claims) {
M crates/rustwarden-core/src/middleware/error_handler.rs
+4 -7
@@ -141,13 +141,10 @@ impl ErrorCode {
141 141 ///
142 142 /// Standardized error response with error code and message
143 143 pub async fn error_handler(request: Request, next: Next) -> Response {
144 - let response = next.run(request).await;
145 -
146 - // If response is an error, we can enhance it here
147 - // For now, errors are handled by AppError::IntoResponse
148 - // This middleware can be extended to add error codes to responses
149 -
150 - response
144 + // Errors are currently handled by `AppError::IntoResponse`. This middleware
145 + // is the extension point if we ever want to enrich error responses (codes,
146 + // headers, telemetry) without modifying every handler.
147 + next.run(request).await
151 148 }
152 149
153 150 #[cfg(test)]
M crates/rustwarden-core/src/services/crypto_service.rs
+9 -4
@@ -101,10 +101,15 @@ mod tests {
101 101
102 102 #[test]
103 103 fn test_roundtrip() {
104 - env::set_var(
105 - "OAUTH_ENCRYPTION_KEY",
106 - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
107 - );
104 + // SAFETY: test fixture writing a stable constant key; concurrent tests
105 + // in this binary either set the same value or do not read this var,
106 + // so the race is benign in practice.
107 + unsafe {
108 + env::set_var(
109 + "OAUTH_ENCRYPTION_KEY",
110 + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
111 + );
112 + }
108 113 let secret = "my-super-secret-client-key";
109 114 let encrypted = CryptoService::encrypt(secret).unwrap();
110 115 assert!(encrypted.starts_with("enc:"));
M crates/rustwarden-core/src/services/oauth/service.rs
+2 -3
@@ -177,10 +177,9 @@ impl OAuthService {
177 177 UserService::assign_role_to_user(db, user.id, role.id).await?;
178 178 }
179 179
180 - let refreshed = UserService::get_user_by_id(db, user.id)
180 + UserService::get_user_by_id(db, user.id)
181 181 .await?
182 - .ok_or_else(|| AppError::Internal("User not found after creation".to_string()))?;
183 - refreshed
182 + .ok_or_else(|| AppError::Internal("User not found after creation".to_string()))?
184 183 };
185 184
186 185 // 4. Link OAuth account to user
M crates/rustwarden-core/src/services/totp_service.rs
+16 -8
@@ -476,10 +476,14 @@ mod tests {
476 476
477 477 #[test]
478 478 fn test_generate_backup_codes() {
479 - std::env::set_var(
480 - "ENCRYPTION_KEY",
481 - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
482 - );
479 + // SAFETY: test fixture writing a stable constant key; concurrent tests
480 + // in this binary either set the same value or do not read this var.
481 + unsafe {
482 + std::env::set_var(
483 + "ENCRYPTION_KEY",
484 + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
485 + );
486 + }
483 487
484 488 let (plaintext, encrypted) = TotpService::generate_backup_codes_internal().unwrap();
485 489
@@ -504,10 +508,14 @@ mod tests {
504 508
505 509 #[test]
506 510 fn test_backup_codes_are_unique() {
507 - std::env::set_var(
508 - "ENCRYPTION_KEY",
509 - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
510 - );
511 + // SAFETY: test fixture writing a stable constant key; concurrent tests
512 + // in this binary either set the same value or do not read this var.
513 + unsafe {
514 + std::env::set_var(
515 + "ENCRYPTION_KEY",
516 + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
517 + );
518 + }
511 519
512 520 let (codes, _) = TotpService::generate_backup_codes_internal().unwrap();
513 521 let mut unique = std::collections::HashSet::new();
M crates/rustwarden-ui/Cargo.toml
+2 -1
@@ -1,7 +1,8 @@
1 1 [package]
2 2 name = "rustwarden-ui"
3 3 version = "0.1.0"
4 -edition = "2021"
4 +edition.workspace = true
5 +rust-version.workspace = true
5 6 description = "Default UI for Rustwarden – HTML templates, handlers, admin panel"
6 7
7 8 [dependencies]
M template/Cargo.toml
+2 -1
@@ -1,7 +1,8 @@
1 1 [package]
2 2 name = "{{project-name}}"
3 3 version = "0.1.0"
4 -edition = "2021"
4 +edition = "2024"
5 +rust-version = "1.85"
5 6
6 7 [[bin]]
7 8 name = "{{project-name}}"
M tools/rustwarden-cli/Cargo.toml
+2 -1
@@ -1,7 +1,8 @@
1 1 [package]
2 2 name = "rustwarden-cli"
3 3 version = "0.1.0"
4 -edition = "2021"
4 +edition.workspace = true
5 +rust-version.workspace = true
5 6 description = "CLI wizard to scaffold new Rustwarden applications"
6 7
7 8 [[bin]]