Skip to content

Commit 133f6d7

Browse files
feat: add raw HTTP client support to AuthenticationClient
Add new constructor methods for creating AuthenticationClient instances that use raw HTTP clients without middleware, preventing infinite loops when used by authentication middleware. Signed-off-by: Sergio Correia <[email protected]>
1 parent 07463b8 commit 133f6d7

File tree

1 file changed

+127
-0
lines changed
  • keylime-push-model-agent/src

1 file changed

+127
-0
lines changed

keylime-push-model-agent/src/auth.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use tokio::sync::Mutex;
2222

2323
/// Configuration for the authentication client
2424
#[derive(Debug, Clone)]
25+
#[allow(dead_code)] // TODO: Remove when auth client is integrated
2526
pub struct AuthConfig {
2627
/// Base URL of the verifier (e.g., "https://verifier.example.com")
2728
pub verifier_base_url: String,
@@ -52,26 +53,30 @@ impl Default for AuthConfig {
5253

5354
/// Session token with expiration information
5455
#[derive(Debug, Clone)]
56+
#[allow(dead_code)] // TODO: Remove when auth client is integrated
5557
struct SessionToken {
5658
token: String,
5759
expires_at: DateTime<Utc>,
5860
session_id: u64,
5961
}
6062

6163
impl SessionToken {
64+
#[allow(dead_code)] // TODO: Remove when auth client is integrated
6265
fn is_valid(&self, buffer_minutes: i64) -> bool {
6366
let buffer = Duration::minutes(buffer_minutes);
6467
Utc::now() + buffer < self.expires_at
6568
}
6669
}
6770

6871
/// Mock TPM operations for testing
72+
#[allow(dead_code)] // TODO: Remove when auth client is integrated
6973
pub trait TpmOperations: Send + Sync {
7074
fn generate_proof(&self, challenge: &str) -> Result<ProofOfPossession>;
7175
}
7276

7377
/// Default mock TPM implementation
7478
#[derive(Debug, Clone)]
79+
#[allow(dead_code)] // TODO: Remove when auth client is integrated
7580
pub struct MockTpmOperations;
7681

7782
impl TpmOperations for MockTpmOperations {
@@ -92,13 +97,15 @@ impl TpmOperations for MockTpmOperations {
9297
}
9398

9499
/// Standalone authentication client implementing the challenge-response protocol
100+
#[allow(dead_code)] // TODO: Remove when auth client is integrated
95101
pub struct AuthenticationClient {
96102
config: AuthConfig,
97103
http_client: Client,
98104
session_token: Arc<Mutex<Option<SessionToken>>>,
99105
tpm_ops: Box<dyn TpmOperations>,
100106
}
101107

108+
#[allow(dead_code)] // TODO: Remove when auth client is integrated
102109
impl AuthenticationClient {
103110
/// Create a new authentication client with the given configuration
104111
pub fn new(config: AuthConfig) -> Result<Self> {
@@ -135,6 +142,42 @@ impl AuthenticationClient {
135142
})
136143
}
137144

145+
/// Create a raw authentication client with no middleware
146+
/// This is used internally by the authentication middleware to avoid infinite loops
147+
pub fn new_raw(config: AuthConfig) -> Result<Self> {
148+
let timeout = std::time::Duration::from_millis(config.timeout_ms);
149+
let http_client = Client::builder()
150+
.timeout(timeout)
151+
.danger_accept_invalid_certs(true) // For testing
152+
.build()?;
153+
154+
Ok(Self {
155+
config,
156+
http_client,
157+
session_token: Arc::new(Mutex::new(None)),
158+
tpm_ops: Box::new(MockTpmOperations),
159+
})
160+
}
161+
162+
/// Create a raw authentication client with custom TPM operations and no middleware
163+
pub fn new_raw_with_tpm_ops(
164+
config: AuthConfig,
165+
tpm_ops: Box<dyn TpmOperations>,
166+
) -> Result<Self> {
167+
let timeout = std::time::Duration::from_millis(config.timeout_ms);
168+
let http_client = Client::builder()
169+
.timeout(timeout)
170+
.danger_accept_invalid_certs(true) // For testing
171+
.build()?;
172+
173+
Ok(Self {
174+
config,
175+
http_client,
176+
session_token: Arc::new(Mutex::new(None)),
177+
tpm_ops,
178+
})
179+
}
180+
138181
/// Get a valid authentication token, performing authentication if necessary
139182
pub async fn get_auth_token(&self) -> Result<String> {
140183
let token_guard = self.session_token.lock().await;
@@ -176,6 +219,47 @@ impl AuthenticationClient {
176219
debug!("Authentication token cleared");
177220
}
178221

222+
/// Get a valid authentication token with metadata (token, expiration, session_id)
223+
/// This method is used by the authentication middleware to access token details
224+
pub async fn get_auth_token_with_metadata(
225+
&self,
226+
) -> Result<(String, DateTime<Utc>, u64)> {
227+
let token_guard = self.session_token.lock().await;
228+
229+
// Check if we have a valid token
230+
if let Some(ref token) = *token_guard {
231+
if token.is_valid(self.config.token_refresh_buffer_minutes) {
232+
debug!("Using existing valid token with metadata");
233+
return Ok((
234+
token.token.clone(),
235+
token.expires_at,
236+
token.session_id,
237+
));
238+
} else {
239+
debug!(
240+
"Token expired or expiring soon, need to re-authenticate"
241+
);
242+
}
243+
} else {
244+
debug!("No token available, need to authenticate");
245+
}
246+
247+
drop(token_guard); // Release lock before authentication
248+
249+
// Perform authentication and return metadata
250+
let _token_string = self.authenticate().await?;
251+
252+
// Get the token details from the newly stored token
253+
let token_guard = self.session_token.lock().await;
254+
if let Some(ref token) = *token_guard {
255+
Ok((token.token.clone(), token.expires_at, token.session_id))
256+
} else {
257+
Err(anyhow!(
258+
"Token was not stored properly after authentication"
259+
))
260+
}
261+
}
262+
179263
/// Perform the complete authentication flow
180264
async fn authenticate(&self) -> Result<String> {
181265
info!(
@@ -723,4 +807,47 @@ mod tests {
723807

724808
assert!(!client.has_valid_token().await);
725809
}
810+
811+
#[tokio::test]
812+
async fn test_raw_client_creation() {
813+
let config = AuthConfig {
814+
verifier_base_url: "https://127.0.0.1:8881".to_string(),
815+
agent_id: "test-agent-raw".to_string(),
816+
avoid_tpm: true,
817+
timeout_ms: 1000,
818+
token_refresh_buffer_minutes: 5,
819+
max_auth_retries: 2,
820+
};
821+
822+
let raw_client = AuthenticationClient::new_raw(config).unwrap();
823+
824+
// Verify the client was created successfully
825+
assert_eq!(raw_client.config.agent_id, "test-agent-raw");
826+
assert_eq!(raw_client.config.timeout_ms, 1000);
827+
assert!(raw_client.config.avoid_tpm);
828+
}
829+
830+
#[tokio::test]
831+
async fn test_raw_client_with_tpm_ops() {
832+
let config = AuthConfig {
833+
verifier_base_url: "https://127.0.0.1:8881".to_string(),
834+
agent_id: "test-agent-raw-tpm".to_string(),
835+
avoid_tpm: false,
836+
timeout_ms: 2000,
837+
token_refresh_buffer_minutes: 10,
838+
max_auth_retries: 1,
839+
};
840+
841+
let custom_tpm_ops = Box::new(MockTpmOperations);
842+
let raw_client = AuthenticationClient::new_raw_with_tpm_ops(
843+
config,
844+
custom_tpm_ops,
845+
)
846+
.unwrap();
847+
848+
// Verify the client was created successfully
849+
assert_eq!(raw_client.config.agent_id, "test-agent-raw-tpm");
850+
assert_eq!(raw_client.config.timeout_ms, 2000);
851+
assert!(!raw_client.config.avoid_tpm);
852+
}
726853
}

0 commit comments

Comments
 (0)