Skip to content

Commit 5c68638

Browse files
committed
refactor: update import paths for Daemon in launch_daemon and mod modules
1 parent 696357d commit 5c68638

File tree

3 files changed

+129
-22
lines changed

3 files changed

+129
-22
lines changed

src/daemon/launch_daemon.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//! ## Usage
2121
//!
2222
//! ```no_run
23-
//! use rust_photoacoustic::{config::Config, daemon::Daemon};
23+
//! use rust_photoacoustic::{config::Config, daemon::launch_daemon::Daemon};
2424
//!
2525
//! async fn example() -> anyhow::Result<()> {
2626
//! let config = Config::from_file("config.yaml")?;
@@ -102,7 +102,7 @@ impl Daemon {
102102
/// # Examples
103103
///
104104
/// ```
105-
/// use rust_photoacoustic::daemon::Daemon;
105+
/// use rust_photoacoustic::daemon::launch_daemon::Daemon;
106106
///
107107
/// let daemon = Daemon::new();
108108
/// // Daemon is now ready to launch tasks
@@ -145,7 +145,7 @@ impl Daemon {
145145
/// # Examples
146146
///
147147
/// ```no_run
148-
/// use rust_photoacoustic::{config::Config, daemon::Daemon};
148+
/// use rust_photoacoustic::{config::Config, daemon::launch_daemon::Daemon};
149149
///
150150
/// async fn start_daemon() -> anyhow::Result<Daemon> {
151151
/// let config = Config::from_file("config.yaml")?;
@@ -514,7 +514,7 @@ impl Daemon {
514514
/// # Examples
515515
///
516516
/// ```no_run
517-
/// use rust_photoacoustic::daemon::Daemon;
517+
/// use rust_photoacoustic::daemon::launch_daemon::Daemon;
518518
///
519519
/// # async fn example() -> anyhow::Result<()> {
520520
/// # let daemon = Daemon::new();
@@ -552,7 +552,7 @@ impl Daemon {
552552
/// # Examples
553553
///
554554
/// ```no_run
555-
/// use rust_photoacoustic::daemon::Daemon;
555+
/// use rust_photoacoustic::daemon::launch_daemon::Daemon;
556556
///
557557
/// # async fn example() -> anyhow::Result<()> {
558558
/// # let daemon = Daemon::new();

src/daemon/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//! ## Usage
1313
//!
1414
//! ```no_run
15-
//! use rust_photoacoustic::{config::Config, daemon::Daemon};
15+
//! use rust_photoacoustic::{config::Config, daemon::launch_daemon::Daemon};
1616
//!
1717
//! async fn run() -> anyhow::Result<()> {
1818
//! let config = Config::from_file("config.yaml")?;

tests/server_test_only.rs

Lines changed: 123 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rocket::{
66
http::{ContentType, Status},
77
local::asynchronous::Client,
88
};
9+
use rust_photoacoustic::config::AccessConfig;
910
use serde_json::Value;
1011
use sha2::{Digest, Sha256};
1112
use std::collections::HashMap;
@@ -55,15 +56,26 @@ fn extract_params_from_url(url: &str) -> HashMap<String, String> {
5556

5657
#[rocket::async_test]
5758
async fn test_oauth2_pkce_flow() {
58-
// Configure the Rocket test client with a test HMAC secret
59+
// Initialize the logger for tests
60+
let _ = env_logger::builder()
61+
.is_test(true)
62+
.filter_level(log::LevelFilter::Debug)
63+
.try_init();
64+
65+
// Test HMAC secret
5966
let test_hmac_secret = "test-hmac-secret-key-for-testing";
60-
// Add shutdown configuration to ensure proper test cleanup
67+
68+
// Test AccessConfig with default admin user
69+
let test_access_config = AccessConfig::default();
70+
71+
// Configure the Rocket test client with additional configuration
6172
let figment = get_figment()
6273
.merge(("shutdown.ctrlc", false))
6374
.merge(("shutdown.grace", 1))
6475
.merge(("shutdown.mercy", 1))
6576
.merge(("shutdown.force", true))
66-
.merge(("hmac_secret", test_hmac_secret));
77+
.merge(("hmac_secret", test_hmac_secret))
78+
.merge(("access", test_access_config)); // Add access config
6779

6880
let rocket = rust_photoacoustic::visualization::server::build_rocket(figment).await;
6981
let client = Client::tracked(rocket)
@@ -92,37 +104,98 @@ async fn test_oauth2_pkce_flow() {
92104
assert!(response.content_type().unwrap().is_html());
93105

94106
let body = response.into_string().await.expect("Response body");
107+
108+
// Should now receive a login form instead of consent form
109+
assert!(
110+
body.contains("Username"),
111+
"The login page should contain a Username field"
112+
);
113+
assert!(
114+
body.contains("Password"),
115+
"The login page should contain a Password field"
116+
);
117+
assert!(
118+
body.contains("Login"),
119+
"The login page should contain a Login button"
120+
);
121+
122+
// Step 2: Extract form action and submit login credentials
123+
println!("Submitting login credentials...");
124+
125+
// Submit login form with default admin credentials
126+
let mut form_data = HashMap::new();
127+
form_data.insert("username", "admin");
128+
form_data.insert("password", "admin123");
129+
form_data.insert("response_type", "code");
130+
form_data.insert("client_id", "LaserSmartClient");
131+
form_data.insert("redirect_uri", "http://localhost:8080/client/");
132+
form_data.insert("scope", "openid profile email read:api write:api");
133+
form_data.insert("state", "test-state");
134+
135+
let login_response = client
136+
.post("/login")
137+
.header(ContentType::Form)
138+
.body(serde_urlencoded::to_string(&form_data).unwrap())
139+
.dispatch()
140+
.await;
141+
142+
// Should redirect back to /authorize with session established
143+
assert_eq!(login_response.status(), Status::Found);
144+
145+
let redirect_location = login_response
146+
.headers()
147+
.get_one("Location")
148+
.expect("Should have location header after login");
149+
150+
println!("Login redirect location: {}", redirect_location);
151+
152+
// Step 3: Follow redirect to get consent page
153+
let consent_response = client.get(redirect_location).dispatch().await;
154+
155+
assert_eq!(consent_response.status(), Status::Ok);
156+
assert!(consent_response.content_type().unwrap().is_html());
157+
158+
let consent_body = consent_response.into_string().await.expect("Consent page body");
95159
assert!(
96-
body.contains("Accept"),
160+
consent_body.contains("Accept"),
97161
"The consent page should contain an Accept button"
98162
);
99163
assert!(
100-
body.contains("Deny"),
164+
consent_body.contains("Deny"),
101165
"The consent page should contain a Deny button"
102166
);
103167

104-
// Step 2: Simulate user consent (accept)
168+
// Step 4: Extract consent form action and simulate user consent (accept)
105169
println!("Simulating user consent (Accept)...");
106-
let consent_response = client
107-
.post(format!("/authorize?{}&allow=true", query_params))
108-
.dispatch()
109-
.await;
170+
171+
// Extract the form action for the Accept button
172+
let accept_form_regex = regex::Regex::new(r#"<form method="post" action="([^"]*allow=true[^"]*)">"#).unwrap();
173+
let consent_action = accept_form_regex
174+
.captures(&consent_body)
175+
.and_then(|caps| caps.get(1))
176+
.map(|m| m.as_str().to_string())
177+
.expect("Should extract form action for consent acceptance");
178+
179+
println!("Consent form action: {}", consent_action);
180+
181+
// Submit the consent form
182+
let consent_submit_response = client.post(&consent_action).dispatch().await;
110183

111184
// The response should be a redirect to the callback URI with an authorization code
112-
assert_eq!(consent_response.status(), Status::Found);
185+
assert_eq!(consent_submit_response.status(), Status::Found);
113186

114187
// Extract the redirect URL and authorization code
115-
let location_header = consent_response
188+
let location_header = consent_submit_response
116189
.headers()
117190
.get_one("Location")
118191
.expect("Location header missing in response");
119-
println!("Redirect URL: {}", location_header);
192+
println!("Final redirect URL: {}", location_header);
120193

121194
let params = extract_params_from_url(location_header);
122195
let authorization_code = params.get("code").expect("Authorization code missing");
123196
println!("Authorization code: {}", authorization_code);
124197

125-
// Step 3: Exchange authorization code for tokens
198+
// Step 5: Exchange authorization code for tokens
126199
println!("Exchanging authorization code for tokens...");
127200
let token_body = format!(
128201
"grant_type=authorization_code&code={}&redirect_uri=http://localhost:8080/client/&client_id=LaserSmartClient&code_verifier={}&code_challenge_method=S256",
@@ -152,6 +225,7 @@ async fn test_oauth2_pkce_flow() {
152225
token_json.get("access_token").is_some(),
153226
"Response should contain an access_token"
154227
);
228+
155229
// Note: The token_type case is not standardized, but our implementation uses lowercase
156230
assert_eq!(
157231
token_json
@@ -187,6 +261,39 @@ async fn test_oauth2_pkce_flow() {
187261
// We could decode the payload too, but this is enough to verify it's a JWT
188262
}
189263

264+
// Step 6: Test with invalid credentials (should fail)
265+
println!("Testing with invalid credentials...");
266+
267+
// Make a new authorization request
268+
let invalid_auth_response = client
269+
.get(format!("/authorize?{}", query_params))
270+
.dispatch()
271+
.await;
272+
273+
assert_eq!(invalid_auth_response.status(), Status::Ok);
274+
275+
// Try to login with invalid credentials
276+
let mut invalid_form_data = HashMap::new();
277+
invalid_form_data.insert("username", "admin");
278+
invalid_form_data.insert("password", "wrongpassword");
279+
invalid_form_data.insert("response_type", "code");
280+
invalid_form_data.insert("client_id", "LaserSmartClient");
281+
invalid_form_data.insert("redirect_uri", "http://localhost:8080/client/");
282+
283+
let invalid_login_response = client
284+
.post("/login")
285+
.header(ContentType::Form)
286+
.body(serde_urlencoded::to_string(&invalid_form_data).unwrap())
287+
.dispatch()
288+
.await;
289+
290+
// Should return unauthorized status or redirect back to login
291+
assert!(
292+
invalid_login_response.status() == Status::Unauthorized ||
293+
invalid_login_response.status() == Status::Ok, // Might return OK with error message
294+
"Invalid credentials should be rejected"
295+
);
296+
190297
// The complete OAuth2 PKCE flow has been successfully tested
191-
println!("OAuth2 PKCE flow test completed successfully!");
192-
}
298+
println!("OAuth2 PKCE flow test with authentication completed successfully!");
299+
}

0 commit comments

Comments
 (0)