@@ -74,31 +74,117 @@ pub async fn claims_layer(
7474#[ cfg( test) ]
7575mod test {
7676
77+ use std:: sync:: { Arc , Once } ;
78+
79+ use async_trait:: async_trait;
80+ use axum:: { Router , body:: Body , middleware, response:: Response , routing:: get} ;
81+ use contextforge_gateway_rs_apis:: { User , user_store:: UserConfig } ;
82+ use http:: { HeaderMap , Request , StatusCode } ;
7783 use jsonwebtoken:: { DecodingKey , Validation } ;
84+ use tower:: ServiceExt ;
7885
7986 use crate :: {
80- common:: ContextForgeClaims ,
87+ Config ,
88+ common:: { ContextForgeClaims , ContextForgeGatewayAppState , JwtTokenDecoders } ,
8189 const_values:: { CONTEXT_FORGE_GATEWAY_AUDIENCE , CONTEXT_FORGE_GATEWAY_ISSUER } ,
90+ layers:: claims_id:: claims_layer,
91+ tests,
92+ user_config_store:: { ConfigStoreError , UserConfigStore } ,
8293 } ;
8394
84- #[ test]
85- fn claim_test ( ) {
86- rustls:: crypto:: ring:: default_provider ( ) . install_default ( ) . expect ( "Failed to install rustls crypto provider" ) ;
95+ static CRYPTO : Once = Once :: new ( ) ;
96+
97+ struct MockedUserConfigStore ;
98+ #[ async_trait]
99+ impl UserConfigStore for MockedUserConfigStore {
100+ async fn get_config < ' a > ( & self , _: & ' a User ) -> Result < UserConfig , ConfigStoreError > {
101+ Err ( ConfigStoreError :: InvalidConnection )
102+ }
103+
104+ async fn set_config < ' a > ( & self , _: & ' a User , _: & ' a UserConfig ) -> Result < ( ) , ConfigStoreError > {
105+ Err ( ConfigStoreError :: InvalidConnection )
106+ }
107+ }
108+
109+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
110+ #[ allow( clippy:: items_after_statements) ]
111+ async fn claim_test_valid_hmac ( ) {
112+ CRYPTO . call_once ( || {
113+ rustls:: crypto:: ring:: default_provider ( )
114+ . install_default ( )
115+ . expect ( "Failed to install rustls crypto provider" ) ;
116+ } ) ;
117+
118+ async fn handle ( _: HeaderMap ) -> Response {
119+ Response :: builder ( ) . status ( StatusCode :: OK ) . body ( Body :: empty ( ) ) . expect ( "Expecting this to work" )
120+ }
87121
88122 let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbkBleGFtcGxlLmNvbSIsImp0aSI6Ijc1ZWYwZTZjLTZkZWMtNGExNy1hNzU3LWFlYmYzZjk1N2Q1NSIsInRva2VuX3VzZSI6ImFwaSIsImlhdCI6MTc3ODg2NTE2OCwiaXNzIjoibWNwZ2F0ZXdheSIsImF1ZCI6Im1jcGdhdGV3YXktYXBpIiwidXNlciI6eyJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwiZnVsbF9uYW1lIjoiQVBJIFRva2VuIFVzZXIiLCJpc19hZG1pbiI6dHJ1ZSwiYXV0aF9wcm92aWRlciI6ImFwaV90b2tlbiJ9LCJ0ZWFtcyI6bnVsbCwic2NvcGVzIjp7InNlcnZlcl9pZCI6bnVsbCwicGVybWlzc2lvbnMiOltdLCJpcF9yZXN0cmljdGlvbnMiOltdLCJ0aW1lX3Jlc3RyaWN0aW9ucyI6e319LCJleHAiOjE3ODE0NTcxNjh9.9d2-iLOHL2dJRFTSbOxHzuD6zLxupqK0ZkCG-3GZABU" ;
89123
90- let Ok ( header) = jsonwebtoken:: decode_header ( token) else {
91- panic ! ( ) ;
124+ let mut validation = Validation :: new ( jsonwebtoken:: Algorithm :: HS256 ) ;
125+ validation. set_audience ( & [ CONTEXT_FORGE_GATEWAY_AUDIENCE ] ) ;
126+ validation. set_issuer ( & [ CONTEXT_FORGE_GATEWAY_ISSUER ] ) ;
127+ validation. validate_exp = false ;
128+
129+ let decoding_key = DecodingKey :: from_secret ( "my-test-key-but-now-longer-than-32-bytes" . as_bytes ( ) ) ;
130+
131+ let state = ContextForgeGatewayAppState {
132+ jwt_token_decoding_keys : JwtTokenDecoders { rs : None , hmac_sha : Some ( decoding_key) } ,
133+ config_store : Arc :: new ( MockedUserConfigStore { } ) ,
134+ config : Config :: default ( ) ,
92135 } ;
136+ let http_requst = Request :: builder ( )
137+ . header ( "Authorization" , format ! ( "Bearer {token}" ) )
138+ . method ( "GET" )
139+ . body ( Body :: empty ( ) )
140+ . expect ( "This should work" ) ;
141+
142+ let app =
143+ Router :: new ( ) . route ( "/" , get ( handle) ) . layer ( middleware:: from_fn_with_state ( state. clone ( ) , claims_layer) ) ;
144+
145+ let res = app. oneshot ( http_requst) . await . unwrap ( ) ;
146+ assert_eq ! ( res. status( ) , StatusCode :: OK ) ;
147+ }
148+
149+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
150+ #[ allow( clippy:: items_after_statements) ]
151+ async fn claim_test_expired_token ( ) {
152+ CRYPTO . call_once ( || {
153+ rustls:: crypto:: ring:: default_provider ( )
154+ . install_default ( )
155+ . expect ( "Failed to install rustls crypto provider" ) ;
156+ } ) ;
157+
158+ async fn handle ( _: HeaderMap ) -> Response {
159+ Response :: builder ( ) . status ( StatusCode :: OK ) . body ( Body :: empty ( ) ) . expect ( "Expecting this to work" )
160+ }
93161
94- let mut validation = Validation :: new ( header. alg ) ;
162+ let mut claims = ContextForgeClaims :: new ( "blah@blah.com" ) ;
163+ claims. exp = 0 ;
164+ let token = tests:: gateway_end_to_end:: get_token_for_claims ( & claims) ;
165+
166+ let mut validation = Validation :: new ( jsonwebtoken:: Algorithm :: RS256 ) ;
95167 validation. set_audience ( & [ CONTEXT_FORGE_GATEWAY_AUDIENCE ] ) ;
96168 validation. set_issuer ( & [ CONTEXT_FORGE_GATEWAY_ISSUER ] ) ;
97- validation. validate_exp = false ;
169+ validation. validate_exp = true ;
98170
99171 let decoding_key = DecodingKey :: from_secret ( "my-test-key-but-now-longer-than-32-bytes" . as_bytes ( ) ) ;
100172
101- let maybe_valid = jsonwebtoken:: decode :: < ContextForgeClaims > ( token, & decoding_key, & validation) ;
102- maybe_valid. expect ( "token invalid" ) ;
173+ let state = ContextForgeGatewayAppState {
174+ jwt_token_decoding_keys : JwtTokenDecoders { rs : None , hmac_sha : Some ( decoding_key) } ,
175+ config_store : Arc :: new ( MockedUserConfigStore { } ) ,
176+ config : Config :: default ( ) ,
177+ } ;
178+ let http_requst = Request :: builder ( )
179+ . header ( "Authorization" , format ! ( "Bearer {token}" ) )
180+ . method ( "GET" )
181+ . body ( Body :: empty ( ) )
182+ . expect ( "This should work" ) ;
183+
184+ let app =
185+ Router :: new ( ) . route ( "/" , get ( handle) ) . layer ( middleware:: from_fn_with_state ( state. clone ( ) , claims_layer) ) ;
186+
187+ let res = app. oneshot ( http_requst) . await . unwrap ( ) ;
188+ assert_eq ! ( res. status( ) , StatusCode :: UNAUTHORIZED ) ;
103189 }
104190}
0 commit comments