Skip to content

Commit

Permalink
feat: Add output to file option (#221)
Browse files Browse the repository at this point in the history
* feat: Add output to file option

* Fix message

* Address unreachable code

* Make less changes

* Test saving JWT to a file

* fmt

* Address code review comments

- moved tempdir to dev dependencies
- removed old style import
- refactored exit code logic into main
- covered print_decoded_token in tests

* Remove unnecessary exit call

Co-authored-by: Marcin Lewandowski <mlewandowski@pl.ibm.com>
  • Loading branch information
lewandom and Marcin Lewandowski authored Jan 19, 2023
1 parent 0e887ec commit 065fcce
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 30 deletions.
63 changes: 63 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ chrono = "0.4"
parse_duration = "2.1.1"
atty = "0.2"
base64 = "0.21.0"

[dev-dependencies]
tempdir = "0.3.7"
11 changes: 11 additions & 0 deletions src/cli_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::translators::{PayloadItem, SupportedTypes};
use crate::utils::parse_duration_string;
use clap::{Parser, Subcommand, ValueEnum};
use jsonwebtoken::Algorithm;
use std::path::PathBuf;

#[derive(Parser, Debug)]
#[clap(name = "jwt")]
Expand Down Expand Up @@ -94,6 +95,11 @@ pub struct EncodeArgs {
#[clap(long, short = 'S')]
#[clap(value_parser)]
pub secret: String,

/// The path of the file to write the result to (suppresses default standard output)
#[clap(long = "out", short = 'o')]
#[clap(value_parser)]
pub output_path: Option<PathBuf>,
}

#[derive(Debug, Clone, Parser)]
Expand Down Expand Up @@ -130,6 +136,11 @@ pub struct DecodeArgs {
#[clap(long = "ignore-exp")]
#[clap(value_parser)]
pub ignore_exp: bool,

/// The path of the file to write the result to (suppresses default standard output, implies JSON format)
#[clap(long = "out", short = 'o')]
#[clap(value_parser)]
pub output_path: Option<PathBuf>,
}

#[allow(clippy::upper_case_acronyms)]
Expand Down
17 changes: 14 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clap::Parser;
use cli_config::{App, Commands, EncodeArgs};
use std::process::exit;
use translators::decode::{decode_token, print_decoded_token};
use translators::encode::{encode_token, print_encoded_token};

Expand All @@ -22,13 +23,23 @@ fn main() {
warn_unsupported(arguments);

let token = encode_token(arguments);
let output_path = &arguments.output_path;

print_encoded_token(token);
exit(match print_encoded_token(token, output_path) {
Ok(_) => 0,
_ => 1,
});
}
Commands::Decode(arguments) => {
let (validated_token, token_data, format) = decode_token(arguments);
let output_path = &arguments.output_path;

print_decoded_token(validated_token, token_data, format);
exit(
match print_decoded_token(validated_token, token_data, format, output_path) {
Ok(_) => 0,
_ => 1,
},
);
}
}
};
}
34 changes: 19 additions & 15 deletions src/translators/decode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::cli_config::{translate_algorithm, DecodeArgs};
use crate::translators::Payload;
use crate::utils::slurp_file;
use crate::utils::{slurp_file, write_file};
use base64::engine::general_purpose::STANDARD as base64_engine;
use base64::Engine as _;
use jsonwebtoken::errors::{ErrorKind, Result as JWTResult};
Expand All @@ -9,7 +9,7 @@ use serde_derive::{Deserialize, Serialize};
use serde_json::to_string_pretty;
use std::collections::HashSet;
use std::io;
use std::process::exit;
use std::path::PathBuf;

#[derive(Debug, PartialEq, Eq)]
pub enum OutputFormat {
Expand All @@ -18,9 +18,9 @@ pub enum OutputFormat {
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct TokenOutput {
header: Header,
payload: Payload,
pub struct TokenOutput {
pub header: Header,
pub payload: Payload,
}

impl TokenOutput {
Expand Down Expand Up @@ -147,7 +147,8 @@ pub fn print_decoded_token(
validated_token: JWTResult<TokenData<Payload>>,
token_data: JWTResult<TokenData<Payload>>,
format: OutputFormat,
) {
output_path: &Option<PathBuf>,
) -> JWTResult<()> {
if let Err(err) = &validated_token {
match err.kind() {
ErrorKind::InvalidToken => {
Expand Down Expand Up @@ -193,23 +194,26 @@ pub fn print_decoded_token(
err
),
};
return Err(validated_token.err().unwrap());
}

match (format, token_data) {
(OutputFormat::Json, Ok(token)) => {
println!("{}", to_string_pretty(&TokenOutput::new(token)).unwrap())
match (output_path.as_ref(), format, token_data) {
(Some(path), _, Ok(token)) => {
let json = to_string_pretty(&TokenOutput::new(token)).unwrap();
write_file(path, json.as_bytes());
println!("Wrote jwt to file {}", path.display());
}
(None, OutputFormat::Json, Ok(token)) => {
println!("{}", to_string_pretty(&TokenOutput::new(token)).unwrap());
}
(_, Ok(token)) => {
(None, _, Ok(token)) => {
bunt::println!("\n{$bold}Token header\n------------{/$}");
println!("{}\n", to_string_pretty(&token.header).unwrap());
bunt::println!("{$bold}Token claims\n------------{/$}");
println!("{}", to_string_pretty(&token.claims).unwrap());
}
(_, Err(_)) => exit(1),
(_, _, Err(err)) => return Err(err),
}

exit(match validated_token {
Err(_) => 1,
Ok(_) => 0,
})
Ok(())
}
25 changes: 16 additions & 9 deletions src/translators/encode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::cli_config::{translate_algorithm, EncodeArgs};
use crate::translators::{Payload, PayloadItem};
use crate::utils::slurp_file;
use crate::utils::{slurp_file, write_file};
use atty::Stream;
use base64::engine::general_purpose::STANDARD as base64_engine;
use base64::Engine as _;
Expand All @@ -9,7 +9,7 @@ use jsonwebtoken::errors::Result as JWTResult;
use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
use serde_json::{from_str, Value};
use std::io;
use std::process::exit;
use std::path::PathBuf;

fn create_header(alg: Algorithm, kid: Option<&String>) -> Header {
let mut header = Header::new(alg);
Expand Down Expand Up @@ -113,20 +113,27 @@ pub fn encode_token(arguments: &EncodeArgs) -> JWTResult<String> {
.and_then(|secret| encode(&header, &claims, &secret))
}

pub fn print_encoded_token(token: JWTResult<String>) {
match token {
Ok(jwt) => {
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);
}
exit(0);
};
}
Err(err) => {
(_, Err(err)) => {
bunt::eprintln!("{$red+bold}Something went awry creating the jwt{/$}\n");
eprintln!("{}", err);
exit(1);
return Err(err);
}
}
Ok(())
}
5 changes: 5 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use std::fs;
use std::path::Path;

pub fn slurp_file(file_name: &str) -> Vec<u8> {
fs::read(file_name).unwrap_or_else(|_| panic!("Unable to read file {}", file_name))
}

pub fn write_file(path: &Path, content: &[u8]) {
fs::write(path, content).unwrap_or_else(|_| panic!("Unable to write file {}", path.display()))
}

pub fn parse_duration_string(val: &str) -> Result<i64, String> {
let mut base_val = val.replace(" ago", "");

Expand Down
Loading

0 comments on commit 065fcce

Please sign in to comment.