@@ -3,13 +3,15 @@ use std::{collections::HashMap, time::Duration};
33use alloy:: { hex, primitives:: FixedBytes } ;
44use cb_common:: {
55 commit:: { constants:: GET_PUBKEYS_PATH , request:: GetPubkeysResponse } ,
6+ config:: StartSignerConfig ,
67 signer:: { SignerLoader , ValidatorKeysFormat } ,
78 types:: { Chain , ModuleId } ,
89 utils:: create_jwt,
910} ;
1011use cb_signer:: service:: SigningService ;
1112use cb_tests:: utils:: { get_signer_config, get_start_signer_config, setup_test_env} ;
1213use eyre:: Result ;
14+ use reqwest:: { Response , StatusCode } ;
1315use tracing:: info;
1416
1517const JWT_MODULE : & str = "test-module" ;
@@ -18,66 +20,75 @@ const JWT_SECRET: &str = "test-jwt-secret";
1820#[ tokio:: test]
1921async fn test_signer_jwt_auth_success ( ) -> Result < ( ) > {
2022 setup_test_env ( ) ;
21- let chain = Chain :: Hoodi ;
23+ let module_id = ModuleId ( JWT_MODULE . to_string ( ) ) ;
24+ let start_config = start_server ( ) . await ?;
2225
23- // Mock JWT secrets
26+ // Run a pubkeys request
27+ let jwt = create_jwt ( & module_id, JWT_SECRET ) ?;
28+ let client = reqwest:: Client :: new ( ) ;
29+ let url = format ! ( "http://{}{}" , start_config. endpoint, GET_PUBKEYS_PATH ) ;
30+ let response = client. get ( & url) . bearer_auth ( & jwt) . send ( ) . await ?;
31+
32+ // Verify the expected pubkeys are returned
33+ verify_pubkeys ( response) . await ?;
34+
35+ Ok ( ( ) )
36+ }
37+
38+ #[ tokio:: test]
39+ async fn test_signer_jwt_auth_fail ( ) -> Result < ( ) > {
40+ setup_test_env ( ) ;
2441 let module_id = ModuleId ( JWT_MODULE . to_string ( ) ) ;
25- let mut jwts = HashMap :: new ( ) ;
26- jwts. insert ( module_id. clone ( ) , JWT_SECRET . to_string ( ) ) ;
42+ let start_config = start_server ( ) . await ?;
2743
28- // Create a signer config
29- let loader = SignerLoader :: ValidatorsDir {
30- keys_path : "data/keystores/keys" . into ( ) ,
31- secrets_path : "data/keystores/secrets" . into ( ) ,
32- format : ValidatorKeysFormat :: Lighthouse ,
33- } ;
34- let config = get_signer_config ( loader) ;
35- let host = config. host ;
36- let port = config. port ;
37- let start_config = get_start_signer_config ( config, chain, jwts) ;
44+ // Run a pubkeys request - this should fail due to invalid JWT
45+ let jwt = create_jwt ( & module_id, "incorrect secret" ) ?;
46+ let client = reqwest:: Client :: new ( ) ;
47+ let url = format ! ( "http://{}{}" , start_config. endpoint, GET_PUBKEYS_PATH ) ;
48+ let response = client. get ( & url) . bearer_auth ( & jwt) . send ( ) . await ?;
49+ assert ! ( response. status( ) == StatusCode :: UNAUTHORIZED ) ;
50+ info ! (
51+ "Server returned expected error code {} for invalid JWT: {}" ,
52+ response. status( ) ,
53+ response. text( ) . await . unwrap_or_else( |_| "No response body" . to_string( ) )
54+ ) ;
55+ Ok ( ( ) )
56+ }
3857
39- // Run the Signer
40- let server_handle = tokio:: spawn ( SigningService :: run ( start_config) ) ;
58+ #[ tokio:: test]
59+ async fn test_signer_jwt_rate_limit ( ) -> Result < ( ) > {
60+ setup_test_env ( ) ;
61+ let module_id = ModuleId ( JWT_MODULE . to_string ( ) ) ;
62+ let start_config = start_server ( ) . await ?;
4163
42- // Make sure the server is running
43- tokio :: time :: sleep ( Duration :: from_millis ( 100 ) ) . await ;
44- if server_handle . is_finished ( ) {
45- return Err ( eyre :: eyre! (
46- "Signer service failed to start: {}" ,
47- server_handle . await . unwrap_err ( )
48- ) ) ;
64+ // Run as many pubkeys requests as the fail limit
65+ let jwt = create_jwt ( & module_id , "incorrect secret" ) ? ;
66+ let client = reqwest :: Client :: new ( ) ;
67+ let url = format ! ( "http://{}{}" , start_config . endpoint , GET_PUBKEYS_PATH ) ;
68+ for _ in 0 ..start_config . jwt_auth_fail_limit {
69+ let response = client . get ( & url ) . bearer_auth ( & jwt ) . send ( ) . await ? ;
70+ assert ! ( response . status ( ) == StatusCode :: UNAUTHORIZED ) ;
4971 }
5072
51- // Create a JWT header
73+ // Run another request - this should fail due to rate limiting now
5274 let jwt = create_jwt ( & module_id, JWT_SECRET ) ?;
75+ let response = client. get ( & url) . bearer_auth ( & jwt) . send ( ) . await ?;
76+ assert ! ( response. status( ) == StatusCode :: TOO_MANY_REQUESTS ) ;
5377
54- // Run a pubkeys request
55- let client = reqwest:: Client :: new ( ) ;
56- let url = format ! ( "http://{}:{}{}" , host, port, GET_PUBKEYS_PATH ) ;
57- let response = client. get ( & url) . bearer_auth ( jwt) . send ( ) . await ?;
58- assert ! ( response. status( ) . is_success( ) , "Failed to authenticate with JWT" ) ;
59- let pubkey_json = response. json :: < GetPubkeysResponse > ( ) . await ?;
78+ // Wait for the rate limit timeout
79+ tokio:: time:: sleep ( Duration :: from_secs ( start_config. jwt_auth_fail_timeout_seconds as u64 ) )
80+ . await ;
6081
61- // Verify the expected pubkeys are returned
62- assert_eq ! ( pubkey_json. keys. len( ) , 2 ) ;
63- let expected_pubkeys = vec ! [
64- FixedBytes :: new( hex!( "883827193f7627cd04e621e1e8d56498362a52b2a30c9a1c72036eb935c4278dee23d38a24d2f7dda62689886f0c39f4" ) ) ,
65- FixedBytes :: new( hex!( "b3a22e4a673ac7a153ab5b3c17a4dbef55f7e47210b20c0cbb0e66df5b36bb49ef808577610b034172e955d2312a61b9" ) ) ,
66- ] ;
67- for expected in expected_pubkeys {
68- assert ! (
69- pubkey_json. keys. iter( ) . any( |k| k. consensus == expected) ,
70- "Expected pubkey not found: {:?}" ,
71- expected
72- ) ;
73- info ! ( "Server returned expected pubkey: {:?}" , expected) ;
74- }
82+ // Now the next request should succeed
83+ let response = client. get ( & url) . bearer_auth ( & jwt) . send ( ) . await ?;
84+ verify_pubkeys ( response) . await ?;
7585
7686 Ok ( ( ) )
7787}
7888
79- #[ tokio:: test]
80- async fn test_signer_jwt_auth_fail ( ) -> Result < ( ) > {
89+ // Starts the signer moduler server on a separate task and returns its
90+ // configuration
91+ async fn start_server ( ) -> Result < StartSignerConfig > {
8192 setup_test_env ( ) ;
8293 let chain = Chain :: Hoodi ;
8394
@@ -92,13 +103,13 @@ async fn test_signer_jwt_auth_fail() -> Result<()> {
92103 secrets_path : "data/keystores/secrets" . into ( ) ,
93104 format : ValidatorKeysFormat :: Lighthouse ,
94105 } ;
95- let config = get_signer_config ( loader) ;
96- let host = config . host ;
97- let port = config . port ;
106+ let mut config = get_signer_config ( loader) ;
107+ config . jwt_auth_fail_limit = 3 ; // Set a low fail limit for testing
108+ config . jwt_auth_fail_timeout_seconds = 3 ; // Set a short timeout for testing
98109 let start_config = get_start_signer_config ( config, chain, jwts) ;
99110
100111 // Run the Signer
101- let server_handle = tokio:: spawn ( SigningService :: run ( start_config) ) ;
112+ let server_handle = tokio:: spawn ( SigningService :: run ( start_config. clone ( ) ) ) ;
102113
103114 // Make sure the server is running
104115 tokio:: time:: sleep ( Duration :: from_millis ( 100 ) ) . await ;
@@ -108,19 +119,27 @@ async fn test_signer_jwt_auth_fail() -> Result<()> {
108119 server_handle. await . unwrap_err( )
109120 ) ) ;
110121 }
122+ Ok ( start_config)
123+ }
111124
112- // Create a JWT header
113- let jwt = create_jwt ( & module_id, "incorrect secret" ) ?;
114-
115- // Run a pubkeys request
116- let client = reqwest:: Client :: new ( ) ;
117- let url = format ! ( "http://{}:{}{}" , host, port, GET_PUBKEYS_PATH ) ;
118- let response = client. get ( & url) . bearer_auth ( jwt) . send ( ) . await ?;
119- assert ! ( response. status( ) . is_client_error( ) , "Failed to authenticate with JWT" ) ;
120- info ! (
121- "Server returned expected error code {} for invalid JWT: {}" ,
122- response. status( ) ,
123- response. text( ) . await . unwrap_or_else( |_| "No response body" . to_string( ) )
124- ) ;
125+ // Verifies that the pubkeys returned by the server match the pubkeys in the
126+ // test data
127+ async fn verify_pubkeys ( response : Response ) -> Result < ( ) > {
128+ // Verify the expected pubkeys are returned
129+ assert ! ( response. status( ) == StatusCode :: OK ) ;
130+ let pubkey_json = response. json :: < GetPubkeysResponse > ( ) . await ?;
131+ assert_eq ! ( pubkey_json. keys. len( ) , 2 ) ;
132+ let expected_pubkeys = vec ! [
133+ FixedBytes :: new( hex!( "883827193f7627cd04e621e1e8d56498362a52b2a30c9a1c72036eb935c4278dee23d38a24d2f7dda62689886f0c39f4" ) ) ,
134+ FixedBytes :: new( hex!( "b3a22e4a673ac7a153ab5b3c17a4dbef55f7e47210b20c0cbb0e66df5b36bb49ef808577610b034172e955d2312a61b9" ) ) ,
135+ ] ;
136+ for expected in expected_pubkeys {
137+ assert ! (
138+ pubkey_json. keys. iter( ) . any( |k| k. consensus == expected) ,
139+ "Expected pubkey not found: {:?}" ,
140+ expected
141+ ) ;
142+ info ! ( "Server returned expected pubkey: {:?}" , expected) ;
143+ }
125144 Ok ( ( ) )
126145}
0 commit comments