forked from mike-engel/jwt-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathencode.rs
139 lines (125 loc) · 5.08 KB
/
encode.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use crate::cli_config::{translate_algorithm, EncodeArgs};
use crate::translators::{Payload, PayloadItem};
use crate::utils::{slurp_file, write_file};
use atty::Stream;
use base64::engine::general_purpose::STANDARD as base64_engine;
use base64::Engine as _;
use chrono::Utc;
use jsonwebtoken::errors::Result as JWTResult;
use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
use serde_json::{from_str, Value};
use std::io;
use std::path::PathBuf;
fn create_header(alg: Algorithm, kid: Option<&String>) -> Header {
let mut header = Header::new(alg);
header.kid = kid.map(|k| k.to_owned());
header
}
pub fn encoding_key_from_secret(alg: &Algorithm, secret_string: &str) -> JWTResult<EncodingKey> {
match alg {
Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
if secret_string.starts_with('@') {
let secret = slurp_file(&secret_string.chars().skip(1).collect::<String>());
Ok(EncodingKey::from_secret(&secret))
} else if secret_string.starts_with("b64:") {
Ok(EncodingKey::from_secret(
&base64_engine
.decode(secret_string.chars().skip(4).collect::<String>())
.unwrap(),
))
} else {
Ok(EncodingKey::from_secret(secret_string.as_bytes()))
}
}
Algorithm::RS256
| Algorithm::RS384
| Algorithm::RS512
| Algorithm::PS256
| Algorithm::PS384
| Algorithm::PS512 => {
let secret = slurp_file(&secret_string.chars().skip(1).collect::<String>());
match secret_string.ends_with(".pem") {
true => EncodingKey::from_rsa_pem(&secret),
false => Ok(EncodingKey::from_rsa_der(&secret)),
}
}
Algorithm::ES256 | Algorithm::ES384 => {
let secret = slurp_file(&secret_string.chars().skip(1).collect::<String>());
match secret_string.ends_with(".pem") {
true => EncodingKey::from_ec_pem(&secret),
false => Ok(EncodingKey::from_ec_der(&secret)),
}
}
Algorithm::EdDSA => panic!("EdDSA is not implemented yet"),
}
}
pub fn encode_token(arguments: &EncodeArgs) -> JWTResult<String> {
let algorithm = translate_algorithm(&arguments.algorithm);
let header = create_header(algorithm, arguments.kid.as_ref());
let custom_payloads = arguments.payload.clone();
let custom_payload = arguments
.json
.as_ref()
.map(|value| {
if value != "-" {
return String::from(value);
}
let mut buffer = String::new();
io::stdin()
.read_line(&mut buffer)
.expect("STDIN was not valid UTF-8");
buffer
})
.map(|raw_json| match from_str(&raw_json) {
Ok(Value::Object(json_value)) => json_value
.into_iter()
.map(|(json_key, json_val)| Some(PayloadItem(json_key, json_val)))
.collect(),
_ => panic!("Invalid JSON provided!"),
});
let now = Utc::now().timestamp();
let expires = PayloadItem::from_timestamp_with_name(arguments.expires.as_ref(), "exp", now);
let not_before =
PayloadItem::from_timestamp_with_name(arguments.not_before.as_ref(), "nbf", now);
let issued_at = match arguments.no_iat {
true => None,
false => PayloadItem::from_timestamp_with_name(Some(&now.to_string()), "iat", now),
};
let issuer = PayloadItem::from_string_with_name(arguments.issuer.as_ref(), "iss");
let subject = PayloadItem::from_string_with_name(arguments.subject.as_ref(), "sub");
let audience = PayloadItem::from_string_with_name(arguments.audience.as_ref(), "aud");
let jwt_id = PayloadItem::from_string_with_name(arguments.jwt_id.as_ref(), "jti");
let mut maybe_payloads: Vec<Option<PayloadItem>> = vec![
issued_at, expires, issuer, subject, audience, jwt_id, not_before,
];
maybe_payloads.append(&mut custom_payloads.unwrap_or_default());
maybe_payloads.append(&mut custom_payload.unwrap_or_default());
let payloads = maybe_payloads.into_iter().flatten().collect();
let Payload(claims) = Payload::from_payloads(payloads);
encoding_key_from_secret(&algorithm, &arguments.secret)
.and_then(|secret| encode(&header, &claims, &secret))
}
pub fn print_encoded_token(
token: JWTResult<String>,
output_path: &Option<PathBuf>,
) -> JWTResult<()> {
match (output_path.as_ref(), token) {
(Some(path), Ok(jwt)) => {
write_file(path, jwt.as_bytes());
println!("Wrote jwt to file {}", path.display());
}
(None, Ok(jwt)) => {
if atty::is(Stream::Stdout) {
println!("{}", jwt);
} else {
print!("{}", jwt);
};
}
(_, Err(err)) => {
bunt::eprintln!("{$red+bold}Something went awry creating the jwt{/$}\n");
eprintln!("{}", err);
return Err(err);
}
}
Ok(())
}