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 } ;
7
4
use serde_json:: Value ;
5
+ use time:: OffsetDateTime ;
6
+ use uuid:: Uuid ;
8
7
9
8
#[ derive( Debug , Serialize , Deserialize ) ]
10
- #[ serde( rename_all = "camelCase" ) ]
9
+ #[ serde( rename_all = "camelCase" ) ]
11
10
struct TenantTokenClaim {
12
- api_key_prefix : String ,
11
+ api_key_uid : String ,
13
12
search_rules : Value ,
14
13
#[ serde( with = "time::serde::timestamp::option" ) ]
15
14
exp : Option < OffsetDateTime > ,
16
15
}
17
16
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 ) ;
21
29
}
22
30
23
31
if expires_at. map_or ( false , |expires_at| OffsetDateTime :: now_utc ( ) > expires_at) {
24
- return Err ( Error :: TenantTokensExpiredSignature )
32
+ return Err ( Error :: TenantTokensExpiredSignature ) ;
25
33
}
26
34
27
- let key_prefix = api_key. as_ref ( ) . chars ( ) . take ( 8 ) . collect ( ) ;
28
35
let claims = TenantTokenClaim {
29
- api_key_prefix : key_prefix ,
36
+ api_key_uid ,
30
37
exp : expires_at,
31
- search_rules
38
+ search_rules,
32
39
} ;
33
40
34
41
let token = encode (
@@ -42,9 +49,9 @@ pub fn generate_tenant_token(search_rules: Value, api_key: impl AsRef<str>, expi
42
49
43
50
#[ cfg( test) ]
44
51
mod tests {
45
- use serde_json:: json;
46
52
use crate :: tenant_tokens:: * ;
47
- use jsonwebtoken:: { decode, DecodingKey , Validation , Algorithm } ;
53
+ use jsonwebtoken:: { decode, Algorithm , DecodingKey , Validation } ;
54
+ use serde_json:: json;
48
55
use std:: collections:: HashSet ;
49
56
50
57
const SEARCH_RULES : [ & str ; 1 ] = [ "*" ] ;
@@ -60,68 +67,109 @@ mod tests {
60
67
61
68
#[ test]
62
69
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 ( ) ;
64
73
65
74
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 ( ) ,
67
78
) ;
68
79
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 ( ) ,
70
83
) ;
71
84
72
85
assert ! ( valid_key. is_ok( ) ) ;
73
86
assert ! ( invalid_key. is_err( ) ) ;
74
87
}
75
88
76
89
#[ test]
77
- fn test_generate_token_without_key ( ) {
90
+ fn test_generate_token_without_uid ( ) {
91
+ let api_key_uid = "" . to_string ( ) ;
78
92
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 ) ;
80
94
81
95
assert ! ( token. is_err( ) ) ;
82
96
}
83
97
84
98
#[ test]
85
99
fn test_generate_token_with_expiration ( ) {
100
+ let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4" . to_string ( ) ;
86
101
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 ( ) ;
88
104
89
105
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 ) ,
91
109
) ;
92
110
93
111
assert ! ( decoded. is_ok( ) ) ;
94
112
}
95
113
96
114
#[ test]
97
115
fn test_generate_token_with_expires_at_in_the_past ( ) {
116
+ let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4" . to_string ( ) ;
98
117
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) ) ;
100
119
101
120
assert ! ( token. is_err( ) ) ;
102
121
}
103
122
104
123
#[ test]
105
124
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 ( ) ;
107
129
108
130
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" ) ;
111
136
112
- assert_eq ! ( decoded. claims. api_key_prefix , & VALID_KEY [ .. 8 ] ) ;
137
+ assert_eq ! ( decoded. claims. api_key_uid , api_key_uid ) ;
113
138
assert_eq ! ( decoded. claims. search_rules, json!( SEARCH_RULES ) ) ;
114
139
}
115
140
116
141
#[ test]
117
142
fn test_generate_token_with_multi_byte_chars ( ) {
143
+ let api_key_uid = "76cf8b87-fd12-4688-ad34-260d930ca4f4" . to_string ( ) ;
118
144
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 ( ) ;
120
147
121
148
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 ) ;
124
163
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( ) ) ;
126
174
}
127
175
}
0 commit comments