diff --git a/src/cmd_signjwt.rs b/src/cmd_signjwt.rs index faec314..e62723e 100644 --- a/src/cmd_signjwt.rs +++ b/src/cmd_signjwt.rs @@ -53,14 +53,25 @@ impl Command for CommandImpl { ..Default::default() }; let mut jwt_claims = Map::new(); + if let Some(payload) = payload { + match serde_json::from_str::(payload) { + Ok(Value::Object(claims_map)) => { + claims_map.into_iter().for_each(|(k, v)| { + jwt_claims.insert(k, v); + }); + } + Ok(value) => { warning!("Not valid payload map: {}", value); } + Err(e) => { warning!("Not valid payload value: {}", e); } + }; + } + match (payload, claims) { - (Some(_), _) => {} + (Some(_), None) => {} (_, Some(claims)) => { for claim in claims { - // TODO support multiple claim types match split_claim(claim) { None => { warning!("Claim '{}' do not contains ':'", claim); } - Some((k, v)) => { jwt_claims.insert(k, Value::String(v)); } + Some((k, v)) => { jwt_claims.insert(k, v); } } } if !jwt_claims.contains_key("sub") { @@ -123,9 +134,9 @@ fn sign_jwt(slot: &str, pin_opt: &Option<&str>, mut header: Header, payload: &Op debugging!("Claims: {:?}", claims); let header = opt_result!(header.to_base64(), "Header to base64 failed: {}"); - let claims = match payload { - Some(payload) => Cow::Owned(util::base64_encode_url_safe_no_pad(payload.as_bytes())), - None => opt_result!(claims.to_base64(), "Claims to base64 failed: {}"), + let claims = match (payload, claims.is_empty()) { + (Some(payload), true) => Cow::Owned(util::base64_encode_url_safe_no_pad(payload.as_bytes())), + (_, _) => opt_result!(claims.to_base64(), "Claims to base64 failed: {}"), }; let mut tobe_signed = vec![]; @@ -148,12 +159,22 @@ fn sign_jwt(slot: &str, pin_opt: &Option<&str>, mut header: Header, payload: &Op Ok([&*header, &*claims, &signature].join(SEPARATOR)) } -fn split_claim(claim: &str) -> Option<(String, String)> { +fn split_claim(claim: &str) -> Option<(String, Value)> { let mut k = String::new(); let mut v = String::new(); + let mut claim_chars = claim.chars().peekable(); + let ty = if let Some('^') = claim_chars.peek() { + let _ = claim_chars.next(); + match claim_chars.next() { + None => return None, + Some(t) => Some(t), + } + } else { + None + }; let mut is_k = true; - for c in claim.chars() { + for c in claim_chars { if is_k { if c == ':' { is_k = false; @@ -165,5 +186,28 @@ fn split_claim(claim: &str) -> Option<(String, String)> { } } - iff!(is_k, None, Some((k, v))) + if is_k { + return None; + } + + match ty { + None | Some('s') => Some((k, Value::String(v))), + Some('b') => Some((k, Value::Bool(vec!["true", "yes", "1"].contains(&v.as_str())))), + Some('i') | Some('n') => { + if let Ok(i) = v.parse::() { + return Some((k, Value::Number(Number::from(i)))); + } + if let Ok(f) = v.parse::() { + if let Some(number_f64) = Number::from_f64(f) { + return Some((k, Value::Number(number_f64))); + } + } + warning!("Bad number: {} in claim: {}", v, claim); + None + } + _ => { + warning!("Unknown type: {} in claim: {}", ty.unwrap(), claim); + None + } + } }