1
+ extern crate chrono;
1
2
#[ macro_use]
2
3
extern crate clap;
3
4
extern crate jsonwebtoken as jwt;
4
- extern crate term_painter;
5
5
extern crate serde;
6
6
#[ macro_use]
7
7
extern crate serde_derive;
8
+ #[ macro_use]
8
9
extern crate serde_json;
10
+ extern crate term_painter;
9
11
10
- use std :: collections :: BTreeMap ;
12
+ use chrono :: { Duration , Utc } ;
11
13
use clap:: { App , Arg , ArgMatches , SubCommand } ;
12
14
use jwt:: { Algorithm , decode, encode, Header , TokenData , Validation } ;
13
- use jwt:: errors:: { self , Error , ErrorKind } ;
14
- use serde :: de :: DeserializeOwned ;
15
- use serde_json :: to_string_pretty ;
15
+ use jwt:: errors:: { Error , ErrorKind , Result as JWTResult } ;
16
+ use serde_json :: { to_string_pretty , to_value , Value } ;
17
+ use std :: collections :: BTreeMap ;
16
18
use term_painter:: ToStyle ;
17
19
use term_painter:: Color :: * ;
18
20
use term_painter:: Attr :: * ;
19
21
20
22
#[ derive( Debug , Serialize , Deserialize , PartialEq ) ]
21
- struct PayloadItem ( String , String ) ;
23
+ struct PayloadItem ( String , Value ) ;
22
24
23
25
#[ derive( Debug , Serialize , Deserialize , PartialEq ) ]
24
- struct Payload ( BTreeMap < String , String > ) ;
26
+ struct Payload ( BTreeMap < String , Value > ) ;
25
27
26
28
arg_enum ! {
27
29
#[ derive( Debug , PartialEq ) ]
@@ -51,28 +53,58 @@ impl PayloadItem {
51
53
}
52
54
53
55
fn from_string_with_name ( val : Option < & str > , name : & str ) -> Option < PayloadItem > {
54
- if val. is_some ( ) {
55
- Some ( PayloadItem ( name. to_string ( ) , val. unwrap ( ) . to_string ( ) ) )
56
- } else {
57
- None
56
+ match val {
57
+ Some ( value) => {
58
+ match to_value ( value) {
59
+ Ok ( json_value) => Some ( PayloadItem ( name. to_string ( ) , json_value) ) ,
60
+ _ => None ,
61
+ }
62
+ }
63
+ _ => None ,
64
+ }
65
+ }
66
+
67
+ fn from_int_with_name ( val : Option < & str > , name : & str ) -> Option < PayloadItem > {
68
+ match val {
69
+ Some ( value) => {
70
+ match i64:: from_str_radix ( & value, 10 ) {
71
+ Ok ( int_value) => {
72
+ match to_value ( int_value) {
73
+ Ok ( json_value) => Some ( PayloadItem ( name. to_string ( ) , json_value) ) ,
74
+ _ => None ,
75
+ }
76
+ }
77
+ _ => None ,
78
+ }
79
+ }
80
+ _ => None ,
58
81
}
59
82
}
60
83
61
84
fn split_payload_item ( p : & str ) -> PayloadItem {
62
85
let split: Vec < & str > = p. split ( '=' ) . collect ( ) ;
86
+ let ( name, value) = ( split[ 0 ] , split[ 1 ] ) ;
63
87
64
- PayloadItem ( split [ 0 ] . to_string ( ) , split [ 1 ] . to_string ( ) )
88
+ PayloadItem ( name . to_string ( ) , to_value ( value ) . unwrap_or ( Value :: Null ) )
65
89
}
66
90
}
67
91
68
92
impl Payload {
69
93
fn from_payloads ( payloads : Vec < PayloadItem > ) -> Payload {
70
94
let mut payload = BTreeMap :: new ( ) ;
95
+ let iat = json ! ( Utc :: now( ) . timestamp( ) ) ;
96
+ let exp = json ! ( ( Utc :: now( ) + Duration :: minutes( 30 ) ) . timestamp( ) ) ;
71
97
72
98
for PayloadItem ( k, v) in payloads {
73
99
payload. insert ( k, v) ;
74
100
}
75
101
102
+ payload. insert ( "iat" . to_string ( ) , iat) ;
103
+
104
+ if !payload. contains_key ( "exp" ) {
105
+ payload. insert ( "exp" . to_string ( ) , exp) ;
106
+ }
107
+
76
108
Payload ( payload)
77
109
}
78
110
}
@@ -269,26 +301,24 @@ fn create_validations(alg: Algorithm) -> Validation {
269
301
}
270
302
}
271
303
272
- fn encode_token ( matches : & ArgMatches ) -> errors :: Result < String > {
304
+ fn encode_token ( matches : & ArgMatches ) -> JWTResult < String > {
273
305
let algorithm = translate_algorithm ( SupportedAlgorithms :: from_string (
274
306
matches. value_of ( "algorithm" ) . unwrap ( ) ,
275
307
) ) ;
276
308
let kid = matches. value_of ( "kid" ) ;
277
309
let header = create_header ( & algorithm, kid) ;
278
- let custom_payloads: Option < Vec < Option < PayloadItem > > > =
279
- matches. values_of ( "payload" ) . map ( |maybe_payloads| {
280
- maybe_payloads
281
- . map ( |p| PayloadItem :: from_string ( Some ( p) ) )
282
- . collect ( )
283
- } ) ;
284
- let expires = PayloadItem :: from_string_with_name ( matches. value_of ( "expires" ) , "exp" ) ;
310
+ let custom_payloads: Option < Vec < Option < PayloadItem > > > = matches. values_of ( "payload" ) . map ( |maybe_payloads| {
311
+ maybe_payloads
312
+ . map ( |p| PayloadItem :: from_string ( Some ( p) ) )
313
+ . collect ( )
314
+ } ) ;
315
+ let expires = PayloadItem :: from_int_with_name ( matches. value_of ( "expires" ) , "exp" ) ;
285
316
let issuer = PayloadItem :: from_string_with_name ( matches. value_of ( "issuer" ) , "iss" ) ;
286
317
let subject = PayloadItem :: from_string_with_name ( matches. value_of ( "subject" ) , "sub" ) ;
287
318
let audience = PayloadItem :: from_string_with_name ( matches. value_of ( "audience" ) , "aud" ) ;
288
319
let principal = PayloadItem :: from_string_with_name ( matches. value_of ( "principal" ) , "prn" ) ;
289
- let not_before = PayloadItem :: from_string_with_name ( matches. value_of ( "not_before" ) , "nbf" ) ;
290
- let mut maybe_payloads: Vec < Option < PayloadItem > > =
291
- vec ! [ expires, issuer, subject, audience, principal, not_before] ;
320
+ let not_before = PayloadItem :: from_int_with_name ( matches. value_of ( "not_before" ) , "nbf" ) ;
321
+ let mut maybe_payloads: Vec < Option < PayloadItem > > = vec ! [ expires, issuer, subject, audience, principal, not_before] ;
292
322
293
323
maybe_payloads. append ( & mut custom_payloads. unwrap_or ( Vec :: new ( ) ) ) ;
294
324
@@ -303,18 +333,18 @@ fn encode_token(matches: &ArgMatches) -> errors::Result<String> {
303
333
encode ( & header, & claims, secret. as_ref ( ) )
304
334
}
305
335
306
- fn decode_token < T : DeserializeOwned > ( matches : & ArgMatches ) -> errors :: Result < TokenData < T > > {
336
+ fn decode_token ( matches : & ArgMatches ) -> JWTResult < TokenData < Payload > > {
307
337
let algorithm = translate_algorithm ( SupportedAlgorithms :: from_string (
308
338
matches. value_of ( "algorithm" ) . unwrap ( ) ,
309
339
) ) ;
310
340
let secret = matches. value_of ( "secret" ) . unwrap ( ) . as_bytes ( ) ;
311
341
let jwt = matches. value_of ( "jwt" ) . unwrap ( ) . to_string ( ) ;
312
342
let validations = create_validations ( algorithm) ;
313
343
314
- decode :: < T > ( & jwt, secret. as_ref ( ) , & validations)
344
+ decode :: < Payload > ( & jwt, secret. as_ref ( ) , & validations)
315
345
}
316
346
317
- fn print_encoded_token ( token : errors :: Result < String > ) {
347
+ fn print_encoded_token ( token : JWTResult < String > ) {
318
348
match token {
319
349
Ok ( jwt) => {
320
350
println ! ( "{}" , Cyan . bold( ) . paint( "Success! Here's your token\n " ) ) ;
@@ -330,7 +360,7 @@ fn print_encoded_token(token: errors::Result<String>) {
330
360
}
331
361
}
332
362
333
- fn print_decoded_token ( token_data : errors :: Result < TokenData < BTreeMap < String , String > > > ) {
363
+ fn print_decoded_token ( token_data : JWTResult < TokenData < Payload > > ) {
334
364
match token_data {
335
365
Ok ( TokenData { header, claims } ) => {
336
366
println ! ( "{}\n " , Cyan . bold( ) . paint( "Looks like a valid JWT!" ) ) ;
@@ -342,24 +372,14 @@ fn print_decoded_token(token_data: errors::Result<TokenData<BTreeMap<String, Str
342
372
Err ( Error ( err, _) ) => {
343
373
match err {
344
374
ErrorKind :: InvalidToken => println ! ( "The JWT provided is invalid" ) ,
345
- ErrorKind :: InvalidSignature => {
346
- println ! ( "The JWT provided has an invalid signature" )
347
- }
375
+ ErrorKind :: InvalidSignature => println ! ( "The JWT provided has an invalid signature" ) ,
348
376
ErrorKind :: InvalidKey => println ! ( "The secret provided isn't a valid RSA key" ) ,
349
377
ErrorKind :: ExpiredSignature => println ! ( "The token has expired" ) ,
350
378
ErrorKind :: InvalidIssuer => println ! ( "The token issuer is invalid" ) ,
351
- ErrorKind :: InvalidAudience => {
352
- println ! ( "The token audience doesn't match the subject" )
353
- }
354
- ErrorKind :: InvalidSubject => {
355
- println ! ( "The token subject doesn't match the audience" )
356
- }
357
- ErrorKind :: InvalidIssuedAt => {
358
- println ! ( "The issued at claim is in the future which isn't allowed" )
359
- }
360
- ErrorKind :: ImmatureSignature => {
361
- println ! ( "The `nbf` claim is in the future which isn't allowed" )
362
- }
379
+ ErrorKind :: InvalidAudience => println ! ( "The token audience doesn't match the subject" ) ,
380
+ ErrorKind :: InvalidSubject => println ! ( "The token subject doesn't match the audience" ) ,
381
+ ErrorKind :: InvalidIssuedAt => println ! ( "The issued at claim is in the future which isn't allowed" ) ,
382
+ ErrorKind :: ImmatureSignature => println ! ( "The `nbf` claim is in the future which isn't allowed" ) ,
363
383
ErrorKind :: InvalidAlgorithm => {
364
384
println ! (
365
385
"The JWT provided has a different signing algorithm than the one you \
0 commit comments