|
| 1 | +use std::{collections::HashMap, time::Duration}; |
| 2 | + |
| 3 | +use alloy::primitives::b256; |
| 4 | +use cb_common::{ |
| 5 | + commit::constants::GET_PUBKEYS_PATH, |
| 6 | + config::{ModuleSigningConfig, load_module_signing_configs}, |
| 7 | + types::ModuleId, |
| 8 | + utils::create_jwt, |
| 9 | +}; |
| 10 | +use cb_tests::{ |
| 11 | + signer_service::start_server, |
| 12 | + utils::{self}, |
| 13 | +}; |
| 14 | +use eyre::Result; |
| 15 | +use reqwest::StatusCode; |
| 16 | + |
| 17 | +const JWT_MODULE: &str = "test-module"; |
| 18 | +const JWT_SECRET: &str = "test-jwt-secret"; |
| 19 | +const ADMIN_SECRET: &str = "test-admin-secret"; |
| 20 | + |
| 21 | +async fn create_mod_signing_configs() -> HashMap<ModuleId, ModuleSigningConfig> { |
| 22 | + let mut cfg = |
| 23 | + utils::get_commit_boost_config(utils::get_pbs_static_config(utils::get_pbs_config(0))); |
| 24 | + |
| 25 | + let module_id = ModuleId(JWT_MODULE.to_string()); |
| 26 | + let signing_id = b256!("0101010101010101010101010101010101010101010101010101010101010101"); |
| 27 | + |
| 28 | + cfg.modules = Some(vec![utils::create_module_config(module_id.clone(), signing_id)]); |
| 29 | + |
| 30 | + let jwts = HashMap::from([(module_id.clone(), JWT_SECRET.to_string())]); |
| 31 | + |
| 32 | + load_module_signing_configs(&cfg, &jwts).unwrap() |
| 33 | +} |
| 34 | + |
| 35 | +#[tokio::test] |
| 36 | +#[tracing_test::traced_test] |
| 37 | +async fn test_signer_jwt_fail_cleanup() -> Result<()> { |
| 38 | + // setup_test_env() isn't used because we want to capture logs with tracing_test |
| 39 | + let module_id = ModuleId(JWT_MODULE.to_string()); |
| 40 | + let mod_cfgs = create_mod_signing_configs().await; |
| 41 | + let start_config = start_server(20102, &mod_cfgs, ADMIN_SECRET.to_string(), false).await?; |
| 42 | + let mod_cfg = mod_cfgs.get(&module_id).expect("JWT config for test module not found"); |
| 43 | + |
| 44 | + // Run as many pubkeys requests as the fail limit |
| 45 | + let jwt = create_jwt(&module_id, "incorrect secret", GET_PUBKEYS_PATH, None)?; |
| 46 | + let client = reqwest::Client::new(); |
| 47 | + let url = format!("http://{}{}", start_config.endpoint, GET_PUBKEYS_PATH); |
| 48 | + for _ in 0..start_config.jwt_auth_fail_limit { |
| 49 | + let response = client.get(&url).bearer_auth(&jwt).send().await?; |
| 50 | + assert!(response.status() == StatusCode::UNAUTHORIZED); |
| 51 | + } |
| 52 | + |
| 53 | + // Run another request - this should fail due to rate limiting now |
| 54 | + let jwt = create_jwt(&module_id, &mod_cfg.jwt_secret, GET_PUBKEYS_PATH, None)?; |
| 55 | + let response = client.get(&url).bearer_auth(&jwt).send().await?; |
| 56 | + assert!(response.status() == StatusCode::TOO_MANY_REQUESTS); |
| 57 | + |
| 58 | + // Wait until the cleanup task should have run properly, takes a while for the |
| 59 | + // timing to work out |
| 60 | + tokio::time::sleep(Duration::from_secs( |
| 61 | + (start_config.jwt_auth_fail_timeout_seconds * 3) as u64, |
| 62 | + )) |
| 63 | + .await; |
| 64 | + |
| 65 | + // Make sure the cleanup message was logged - it's all internal state so without |
| 66 | + // refactoring or exposing it, this is the easiest way to check if it triggered |
| 67 | + assert!(logs_contain("Cleaned up 1 old JWT auth failure entries")); |
| 68 | + |
| 69 | + Ok(()) |
| 70 | +} |
0 commit comments