1- use crate :: {
2- errors:: *
3- } ;
4- use serde:: { Serialize , Deserialize } ;
5- use jsonwebtoken:: { encode, Header , EncodingKey } ;
6- use time:: { OffsetDateTime } ;
1+ use crate :: errors:: * ;
2+ use jsonwebtoken:: { encode, EncodingKey , Header } ;
3+ use serde:: { Deserialize , Serialize } ;
74use serde_json:: Value ;
5+ use time:: OffsetDateTime ;
6+ use uuid:: Uuid ;
87
98#[ derive( Debug , Serialize , Deserialize ) ]
10- #[ serde( rename_all = "camelCase" ) ]
9+ #[ serde( rename_all = "camelCase" ) ]
1110struct TenantTokenClaim {
12- api_key_prefix : String ,
11+ api_key_uid : String ,
1312 search_rules : Value ,
1413 #[ serde( with = "time::serde::timestamp::option" ) ]
1514 exp : Option < OffsetDateTime > ,
1615}
1716
18- pub fn generate_tenant_token ( search_rules : Value , api_key : impl AsRef < str > , expires_at : Option < OffsetDateTime > ) -> Result < String , Error > {
19- if api_key. as_ref ( ) . chars ( ) . count ( ) < 8 {
20- return Err ( Error :: TenantTokensInvalidApiKey )
17+ pub fn generate_tenant_token (
18+ api_key_uid : String ,
19+ search_rules : Value ,
20+ api_key : impl AsRef < str > ,
21+ expires_at : Option < OffsetDateTime > ,
22+ ) -> Result < String , Error > {
23+ // Validate uuid format
24+ let uid = Uuid :: try_parse ( & api_key_uid) ?;
25+
26+ // Validate uuid version
27+ if uid. get_version_num ( ) != 4 {
28+ return Err ( Error :: InvalidUuid4Version ) ;
2129 }
2230
2331 if expires_at. map_or ( false , |expires_at| OffsetDateTime :: now_utc ( ) > expires_at) {
24- return Err ( Error :: TenantTokensExpiredSignature )
32+ return Err ( Error :: TenantTokensExpiredSignature ) ;
2533 }
2634
27- let key_prefix = api_key. as_ref ( ) . chars ( ) . take ( 8 ) . collect ( ) ;
2835 let claims = TenantTokenClaim {
29- api_key_prefix : key_prefix ,
36+ api_key_uid ,
3037 exp : expires_at,
31- search_rules
38+ search_rules,
3239 } ;
3340
3441 let token = encode (
@@ -42,9 +49,9 @@ pub fn generate_tenant_token(search_rules: Value, api_key: impl AsRef<str>, expi
4249
4350#[ cfg( test) ]
4451mod tests {
45- use serde_json:: json;
4652 use crate :: tenant_tokens:: * ;
47- use jsonwebtoken:: { decode, DecodingKey , Validation , Algorithm } ;
53+ use jsonwebtoken:: { decode, Algorithm , DecodingKey , Validation } ;
54+ use serde_json:: json;
4855 use std:: collections:: HashSet ;
4956
5057 const SEARCH_RULES : [ & str ; 1 ] = [ "*" ] ;
@@ -60,68 +67,109 @@ mod tests {
6067
6168 #[ test]
6269 fn test_generate_token_with_given_key ( ) {
63- let token = generate_tenant_token ( json ! ( SEARCH_RULES ) , VALID_KEY , None ) . unwrap ( ) ;
70+ let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4" . to_string ( ) ;
71+ let token =
72+ generate_tenant_token ( api_key_uid, json ! ( SEARCH_RULES ) , VALID_KEY , None ) . unwrap ( ) ;
6473
6574 let valid_key = decode :: < TenantTokenClaim > (
66- & token, & DecodingKey :: from_secret ( VALID_KEY . as_ref ( ) ) , & build_validation ( )
75+ & token,
76+ & DecodingKey :: from_secret ( VALID_KEY . as_ref ( ) ) ,
77+ & build_validation ( ) ,
6778 ) ;
6879 let invalid_key = decode :: < TenantTokenClaim > (
69- & token, & DecodingKey :: from_secret ( "not-the-same-key" . as_ref ( ) ) , & build_validation ( )
80+ & token,
81+ & DecodingKey :: from_secret ( "not-the-same-key" . as_ref ( ) ) ,
82+ & build_validation ( ) ,
7083 ) ;
7184
7285 assert ! ( valid_key. is_ok( ) ) ;
7386 assert ! ( invalid_key. is_err( ) ) ;
7487 }
7588
7689 #[ test]
77- fn test_generate_token_without_key ( ) {
90+ fn test_generate_token_without_uid ( ) {
91+ let api_key_uid = "" . to_string ( ) ;
7892 let key = String :: from ( "" ) ;
79- let token = generate_tenant_token ( json ! ( SEARCH_RULES ) , & key, None ) ;
93+ let token = generate_tenant_token ( api_key_uid , json ! ( SEARCH_RULES ) , & key, None ) ;
8094
8195 assert ! ( token. is_err( ) ) ;
8296 }
8397
8498 #[ test]
8599 fn test_generate_token_with_expiration ( ) {
100+ let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4" . to_string ( ) ;
86101 let exp = OffsetDateTime :: now_utc ( ) + time:: Duration :: HOUR ;
87- let token = generate_tenant_token ( json ! ( SEARCH_RULES ) , VALID_KEY , Some ( exp) ) . unwrap ( ) ;
102+ let token =
103+ generate_tenant_token ( api_key_uid, json ! ( SEARCH_RULES ) , VALID_KEY , Some ( exp) ) . unwrap ( ) ;
88104
89105 let decoded = decode :: < TenantTokenClaim > (
90- & token, & DecodingKey :: from_secret ( VALID_KEY . as_ref ( ) ) , & Validation :: new ( Algorithm :: HS256 )
106+ & token,
107+ & DecodingKey :: from_secret ( VALID_KEY . as_ref ( ) ) ,
108+ & Validation :: new ( Algorithm :: HS256 ) ,
91109 ) ;
92110
93111 assert ! ( decoded. is_ok( ) ) ;
94112 }
95113
96114 #[ test]
97115 fn test_generate_token_with_expires_at_in_the_past ( ) {
116+ let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4" . to_string ( ) ;
98117 let exp = OffsetDateTime :: now_utc ( ) - time:: Duration :: HOUR ;
99- let token = generate_tenant_token ( json ! ( SEARCH_RULES ) , VALID_KEY , Some ( exp) ) ;
118+ let token = generate_tenant_token ( api_key_uid , json ! ( SEARCH_RULES ) , VALID_KEY , Some ( exp) ) ;
100119
101120 assert ! ( token. is_err( ) ) ;
102121 }
103122
104123 #[ test]
105124 fn test_generate_token_contains_claims ( ) {
106- let token = generate_tenant_token ( json ! ( SEARCH_RULES ) , VALID_KEY , None ) . unwrap ( ) ;
125+ let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4" . to_string ( ) ;
126+ let token =
127+ generate_tenant_token ( api_key_uid. clone ( ) , json ! ( SEARCH_RULES ) , VALID_KEY , None )
128+ . unwrap ( ) ;
107129
108130 let decoded = decode :: < TenantTokenClaim > (
109- & token, & DecodingKey :: from_secret ( VALID_KEY . as_ref ( ) ) , & build_validation ( )
110- ) . expect ( "Cannot decode the token" ) ;
131+ & token,
132+ & DecodingKey :: from_secret ( VALID_KEY . as_ref ( ) ) ,
133+ & build_validation ( ) ,
134+ )
135+ . expect ( "Cannot decode the token" ) ;
111136
112- assert_eq ! ( decoded. claims. api_key_prefix , & VALID_KEY [ .. 8 ] ) ;
137+ assert_eq ! ( decoded. claims. api_key_uid , api_key_uid ) ;
113138 assert_eq ! ( decoded. claims. search_rules, json!( SEARCH_RULES ) ) ;
114139 }
115140
116141 #[ test]
117142 fn test_generate_token_with_multi_byte_chars ( ) {
143+ let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4" . to_string ( ) ;
118144 let key = "Ëa1ทt9bVcL-vãUทtP3OpXW5qPc%bWH5ทvw09" ;
119- let token = generate_tenant_token ( json ! ( SEARCH_RULES ) , key, None ) . unwrap ( ) ;
145+ let token =
146+ generate_tenant_token ( api_key_uid. clone ( ) , json ! ( SEARCH_RULES ) , key, None ) . unwrap ( ) ;
120147
121148 let decoded = decode :: < TenantTokenClaim > (
122- & token, & DecodingKey :: from_secret ( key. as_ref ( ) ) , & build_validation ( )
123- ) . expect ( "Cannot decode the token" ) ;
149+ & token,
150+ & DecodingKey :: from_secret ( key. as_ref ( ) ) ,
151+ & build_validation ( ) ,
152+ )
153+ . expect ( "Cannot decode the token" ) ;
154+
155+ assert_eq ! ( decoded. claims. api_key_uid, api_key_uid) ;
156+ }
157+
158+ #[ test]
159+ fn test_generate_token_with_wrongly_formated_uid ( ) {
160+ let api_key_uid = "xxx" . to_string ( ) ;
161+ let key = "Ëa1ทt9bVcL-vãUทtP3OpXW5qPc%bWH5ทvw09" ;
162+ let token = generate_tenant_token ( api_key_uid. clone ( ) , json ! ( SEARCH_RULES ) , key, None ) ;
124163
125- assert_eq ! ( decoded. claims. api_key_prefix, "Ëa1ทt9bV" ) ;
164+ assert ! ( token. is_err( ) ) ;
165+ }
166+
167+ #[ test]
168+ fn test_generate_token_with_wrong_uid_version ( ) {
169+ let api_key_uid = "6a11eb96-2485-11ed-861d-0242ac120002" . to_string ( ) ;
170+ let key = "Ëa1ทt9bVcL-vãUทtP3OpXW5qPc%bWH5ทvw09" ;
171+ let token = generate_tenant_token ( api_key_uid. clone ( ) , json ! ( SEARCH_RULES ) , key, None ) ;
172+
173+ assert ! ( token. is_err( ) ) ;
126174 }
127175}
0 commit comments