diff --git a/src/app/jwt_encoder.rs b/src/app/jwt_encoder.rs index e3c5eac..780e2c7 100644 --- a/src/app/jwt_encoder.rs +++ b/src/app/jwt_encoder.rs @@ -71,15 +71,16 @@ pub fn encode_jwt_token(app: &mut App) { app.handle_error(String::from("Header should not be empty").into()); return; } + let payload = app.data.encoder.payload.input.lines().join("\n"); + if payload.is_empty() { + app.handle_error(String::from("Payload should not be empty").into()); + return; + } let header: Result = serde_json::from_str(&header); match header { Ok(header) => { let alg = header.alg; - let payload = app.data.encoder.payload.input.lines().join("\n"); - if payload.is_empty() { - app.handle_error(String::from("Payload should not be empty").into()); - return; - } + let payload: Result = serde_json::from_str(&payload); match payload { Ok(payload) => { @@ -130,3 +131,129 @@ pub fn encoding_key_from_secret(alg: &Algorithm, secret_string: &str) -> JWTResu }, } } + +#[cfg(test)] +mod tests { + use jsonwebtoken::{DecodingKey, Validation}; + use tui_textarea::TextArea; + + use super::*; + use crate::app::utils::slurp_file; + + #[test] + fn test_encode_jwt_token_with_valid_payload_and_defaults() { + let mut app = App::new(250, None, "secrets".into()); + + app.data.encoder.payload.input = vec![ + "{", + r#" "sub": "1234567890","#, + r#" "name": "John Doe","#, + r#" "iat": 1516239022"#, + "}", + ] + .into(); + + encode_jwt_token(&mut app); + + assert_eq!(app + .data + .encoder + .encoded + .get_txt(), "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.TggX4VlPVD-2G5eUT5AhzepyMCx_nuzfiQ_YkdXsMKI"); + assert!(app.data.encoder.signature_verified); + } + + #[test] + fn test_encode_jwt_token_with_valid_payload_and_header_rs256() { + let mut app = App::new(250, None, "".into()); + + let header = vec!["{", r#" "alg": "RS256","#, r#" "typ": "JWT""#, "}"]; + app.data.encoder.header.input = header.clone().into(); + + let claims = vec![ + "{", + r#" "sub": "1234567890","#, + r#" "name": "John Doe","#, + r#" "iat": 1516239022"#, + "}", + ]; + app.data.encoder.payload.input = claims.clone().into(); + + app.data.encoder.secret.input = "@./test_data/test_rsa_private.pem".into(); + + encode_jwt_token(&mut app); + assert_eq!(app.data.error, ""); + assert_eq!(app + .data + .encoder + .encoded + .get_txt(), "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm5hbWUiOiJKb2huIERvZSIsInN1YiI6IjEyMzQ1Njc4OTAifQ.a6yeSQkIfGD1Va9TgdImZUZ1AKO0OgP15ZFV4JPpZy8TpeByQpqUA3r2kJHNeUlETyEeYMKsDbZI5dYOEa_ZfF9xY6eslV1xmawOPkJYzf8IK3Lb42GEykn9qBWSvHzh5xFs2U1dYjJ9GW7bqhyPVaRVRKh1EBw8AbXmEYT42xSDnzkVUHhPpGM8_2anJNXvnexCQKlVRVVzZC04eHNsRIl5_n50irg7bQCO4z24kwViMTuCQTalV9LXCfdxp7_3Pp4Av_iJtkKHDXWs9GrrD6ttq1J6jOXDSbxn42XrPlxirr0pNtdvbk58W2LqYz4_G9q0HTRz_WO3FmaSxIxyqQ"); + assert!(app.data.encoder.signature_verified); + + // decode the key and verify + let mut secret_validator = Validation::new(Algorithm::RS256); + secret_validator.leeway = 1000; + secret_validator + .required_spec_claims + .retain(|claim| claim != "exp"); + secret_validator.validate_exp = false; + + let secret_string = "@./test_data/test_rsa_public.pem"; + + let secret = slurp_file(&secret_string.chars().skip(1).collect::()).unwrap(); + + let decoded = jsonwebtoken::decode::( + &app.data.encoder.encoded.get_txt(), + &DecodingKey::from_rsa_pem(&secret).unwrap(), + &secret_validator, + ) + .unwrap(); + + assert_eq!( + decoded.header, + serde_json::from_str(header.join("\n").as_str()).unwrap() + ); + assert_eq!( + decoded.claims, + serde_json::from_str(claims.join("\n").as_str()).unwrap() + ); + } + + #[test] + fn test_encode_jwt_token_with_empty_header() { + let mut app = App::new(250, None, "".into()); + + app.data.encoder.header.input = TextArea::default(); + + encode_jwt_token(&mut app); + + assert_eq!(app.data.error, "Header should not be empty"); + } + + #[test] + fn test_encode_jwt_token_with_empty_payload() { + let mut app = App::new(250, None, "".into()); + + app.data.encoder.payload.input = TextArea::default(); + + encode_jwt_token(&mut app); + + assert_eq!(app.data.error, "Payload should not be empty"); + } + + #[test] + fn test_encode_jwt_token_with_invalid_header() { + let mut app = App::new(250, None, "".into()); + + app.data.encoder.header.input = vec!["{", r#" "sub": "1234567890""#, "}"].into(); + + app.data.encoder.payload.input = vec!["{", r#" "sub": "1234567890""#, "}"].into(); + + encode_jwt_token(&mut app); + + assert_eq!( + app.data.error, + "Error parsing header: missing field `alg` at line 3 column 1" + ); + } +} diff --git a/test_data/test_rsa_private.pem b/test_data/test_rsa_private.pem new file mode 100644 index 0000000..3314ab6 --- /dev/null +++ b/test_data/test_rsa_private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj +MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu +NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ +qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg +p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR +ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi +VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV +laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8 +sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H +mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY +dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw +ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ +DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T +N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t +0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv +t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU +AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk +48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL +DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK +xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA +mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh +2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz +et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr +VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD +TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc +dn/RsYEONbwQSjIfMPkvxF+8HQ== +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/test_data/test_rsa_public.pem b/test_data/test_rsa_public.pem new file mode 100644 index 0000000..1c9b622 --- /dev/null +++ b/test_data/test_rsa_public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo +4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u ++qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh +kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ +0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg +cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc +mwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file