feat(cors): implement CORS configuration with From trait

Implement From<CorsSettings> for Cors trait to configure CORS middleware
with production-ready security validation.

- Move CorsSettings to backend/src/settings/cors.rs module
- Validate wildcard + credentials constraint (browser security policy)
- Configure allowed methods, headers, credentials, and max_age
- Add structured logging for CORS configuration
- Move tests from settings/mod.rs and startup.rs to cors module

Ref: T014
This commit is contained in:
2026-01-03 17:42:24 +01:00
parent e577fb5095
commit 5d6c3208cc
5 changed files with 222 additions and 242 deletions

View File

@@ -225,131 +225,4 @@ mod tests {
assert_eq!(app.host(), "127.0.0.1");
assert_eq!(app.port(), 8080);
}
// T013: Tests for build_cors() function (TDD - write tests FIRST)
mod cors_tests {
use super::*;
use crate::settings::CorsSettings;
#[test]
#[should_panic(expected = "CORS misconfiguration")]
fn build_cors_with_credentials_and_wildcard_panics() {
// GIVEN a CORS configuration with wildcard origin AND credentials enabled
let settings = CorsSettings {
allowed_origins: vec!["*".to_string()],
allow_credentials: true,
max_age_secs: 3600,
};
// WHEN build_cors() is called
// THEN it should panic with a clear error message
let _cors = build_cors(&settings);
}
#[test]
fn build_cors_with_wildcard_origin_creates_permissive_cors() {
// GIVEN a CORS configuration with wildcard origin
let settings = CorsSettings {
allowed_origins: vec!["*".to_string()],
allow_credentials: false,
max_age_secs: 3600,
};
// WHEN build_cors() is called
let _cors = build_cors(&settings);
// THEN it should create a Cors middleware that allows any origin
// Note: We can't directly test Cors behavior without integration tests
// This test verifies that build_cors() completes without panicking
}
#[test]
fn build_cors_with_specific_origin_creates_restrictive_cors() {
// GIVEN a CORS configuration with specific origins
let settings = CorsSettings {
allowed_origins: vec![
"https://sta.example.com".to_string(),
"http://localhost:5173".to_string(),
],
allow_credentials: true,
max_age_secs: 3600,
};
// WHEN build_cors() is called
let _cors = build_cors(&settings);
// THEN it should create a Cors middleware that only allows specified origins
// Note: We can't directly test Cors behavior without integration tests
// This test verifies that build_cors() completes without panicking
}
#[test]
fn build_cors_sets_correct_methods() {
// GIVEN a CORS configuration
let settings = CorsSettings {
allowed_origins: vec!["https://example.com".to_string()],
allow_credentials: false,
max_age_secs: 3600,
};
// WHEN build_cors() is called
let _cors = build_cors(&settings);
// THEN it should configure the following methods:
// GET, POST, PUT, PATCH, DELETE, OPTIONS
// Note: Direct method verification requires integration tests
// This test ensures build_cors() completes without errors
}
#[test]
fn build_cors_sets_correct_headers() {
// GIVEN a CORS configuration
let settings = CorsSettings {
allowed_origins: vec!["https://example.com".to_string()],
allow_credentials: false,
max_age_secs: 3600,
};
// WHEN build_cors() is called
let _cors = build_cors(&settings);
// THEN it should configure the following headers:
// content-type, authorization
// Note: Direct header verification requires integration tests
// This test ensures build_cors() completes without errors
}
#[test]
fn build_cors_sets_max_age_from_settings() {
// GIVEN a CORS configuration with custom max_age
let settings = CorsSettings {
allowed_origins: vec!["https://example.com".to_string()],
allow_credentials: false,
max_age_secs: 7200, // 2 hours
};
// WHEN build_cors() is called
let _cors = build_cors(&settings);
// THEN it should configure max_age to 7200 seconds
// Note: Direct max_age verification requires integration tests
// This test ensures build_cors() completes without errors
}
#[test]
fn build_cors_with_empty_origins() {
// GIVEN a CORS configuration with no allowed origins (restrictive fail-safe)
let settings = CorsSettings {
allowed_origins: vec![],
allow_credentials: false,
max_age_secs: 3600,
};
// WHEN build_cors() is called
let _cors = build_cors(&settings);
// THEN it should create a Cors middleware that denies all origins
// This test ensures build_cors() handles the fail-safe case
}
}
}