Skip to content

Commit

Permalink
allow auto renew of ID token
Browse files Browse the repository at this point in the history
  • Loading branch information
bck01215 committed Jul 17, 2024
1 parent 6afeaa1 commit a63f9d4
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 11 deletions.
71 changes: 70 additions & 1 deletion Cargo.lock

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

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "elasticnow"
version = "0.4.2"
version = "0.5.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand All @@ -24,3 +24,6 @@ urlencoding = "2.1.3"
openssl = { version = "0.10", features = ["vendored"] }
clap_complete = "4.5.6"
textplots = "0.8.6"
tiny_http = "0.12.0"
open = "5.3.0"
url = "2.5.2"
2 changes: 1 addition & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This project simplifies timetracking in servicenow for those who find it difficu

### Authentication

To initialize the repo you will need to run `elasticnow setup`. This will generate a config.toml. The location is dependent on the operating system but can be found at the top of the help output.
To initialize the repo you will need to run `elasticnow setup`. This will generate a config.toml. The location is dependent on the operating system but can be found at the top of the help output. After initialization, the id field will automatically update using the browser.

| Flag | Description |
| ----------------------------- | --------------------------------------------------------------------------- |
Expand Down
4 changes: 4 additions & 0 deletions src/cli/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ impl Config {
file.write_all(toml_string.as_bytes())?;
Ok(())
}
pub fn set_new_id(&mut self, _id: &str) -> bool {
self.id = _id.to_string();
self.to_toml_file().is_ok()
}
}

pub fn get_config_dir() -> PathBuf {
Expand Down
17 changes: 15 additions & 2 deletions src/elasticnow/elasticnow.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use reqwest::Client;
use tracing::debug;
use tracing::{debug, Instrument};

use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -68,6 +68,17 @@ impl ElasticNow {
client,
}
}
pub async fn check_auth(&self) -> Result<(), Box<dyn std::error::Error>> {
let resp = self
.client
.get(self.instance.to_owned() + "/cli/login")
.send()
.await?;
if !resp.status().is_success() {
return Err(format!("HTTP Error while querying ElasticNow: {}", resp.status()).into());
}
Ok(())
}
async fn post_json(
&self,
path: &str,
Expand Down Expand Up @@ -106,7 +117,9 @@ impl ElasticNow {
.await?;

if !resp.status().is_success() {
return Err(format!("HTTP Error while querying ElasticNow: {}", resp.status()).into());
return Err(
format!("HTTP Error while querying ElasticNow: {:?}", resp.status()).into(),
);
}

let search_results: Vec<SearchResult> = resp.json().await?;
Expand Down
81 changes: 75 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use elasticnow::elasticnow::elasticnow::ChooseOptions;
use elasticnow::elasticnow::elasticnow::{ElasticNow, SearchResult};
use elasticnow::elasticnow::servicenow::ServiceNow;
use elasticnow::elasticnow::servicenow_structs::TimeWorked;
use open::that;
use std::collections::HashMap;
use std::net::TcpListener;
use tiny_http::{Response, Server};
use tracing_subscriber::{fmt, prelude::*, EnvFilter};

struct ValueOption {
Expand All @@ -15,7 +18,13 @@ struct ValueOption {
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(fmt::layer())
.with(
fmt::layer().event_format(
tracing_subscriber::fmt::format()
.with_file(true)
.with_line_number(true),
),
)
.with(EnvFilter::from_env("ELASTICNOW_LOG_LEVEL"))
.init();
let args = cli::args::get_args();
Expand Down Expand Up @@ -77,7 +86,7 @@ async fn run_timetrack(
no_tkt: bool,
all: bool,
) {
let (config, sn_client) = check_config();
let (mut config, sn_client) = check_config();
tracing::debug!("New: {:?}", new);
tracing::debug!("Comment: {:?}", comment);
tracing::debug!("Time Worked: {:?}", time_worked);
Expand Down Expand Up @@ -108,7 +117,18 @@ async fn run_timetrack(
tkt_options = generic_options_to_value_option(&tkt_options_generic);
tkt_options_string = search_results_to_string(&tkt_options_generic);
} else {
let es_now_client = ElasticNow::new(&config.id, &config.instance);
let mut es_now_client = ElasticNow::new(&config.id, &config.instance);
if es_now_client.check_auth().await.is_err() {
tracing::error!("Unable to authenticate to ElasticNow trying to log in");
let _cookie = get_cookie_from_browser(&config.instance);
config.set_new_id(&_cookie);
es_now_client = ElasticNow::new(&config.id, &config.instance);
let err = es_now_client.check_auth().await;
if err.is_err() {
tracing::error!("login attempt failed");
std::process::exit(1);
}
}
let keywords = search.clone().unwrap_or("".to_string());
let tkt_options_generic = search_tickets(es_now_client, &tkt_bin, &keywords).await;
tkt_options = generic_options_to_value_option(&tkt_options_generic);
Expand Down Expand Up @@ -146,13 +166,13 @@ async fn run_timetrack(
std::process::exit(2);
}
let time_worked_msg = ansi_term::Colour::Green.paint(time_worked);
println!("Tracking {} of time", time_worked_msg);
tracing::info!("Tracking {} of time", time_worked_msg);
if !no_tkt {
let ticket_url = ansi_term::Colour::Blue.paint(format!(
"https://{}.service-now.com/task.do?sys_id={}",
&config.sn_instance, sys_id
));
println!("Link to ticket: {}", ticket_url);
tracing::info!("Link to ticket: {}", ticket_url);
}
std::process::exit(0);
}
Expand Down Expand Up @@ -288,7 +308,7 @@ async fn run_stdchg(search: String, bin: Option<String>, template_id: Option<Str
"https://{}.service-now.com/change_request.do?sys_id={}",
&config.sn_instance, sys_id
));
println!("Link to CHG: {}", ticket_url);
tracing::info!("Link to CHG: {}", ticket_url);

std::process::exit(0);
}
Expand Down Expand Up @@ -373,3 +393,52 @@ fn get_total(tasks: &Vec<TimeWorked>) -> i64 {
.map(|t| t.time_in_seconds.parse::<i64>().unwrap_or_default())
.sum()
}

fn get_cookie_from_browser(elasticnow_url: &str) -> String {
let mut chosen_port = 0;
let mut server = None;
for port in 8000..20000 {
match TcpListener::bind(("0.0.0.0", port)) {
Ok(listener) => {
server = Some(Server::from_listener(listener, None).unwrap());
chosen_port = port;
println!("Server running on port {}", port);
break;
}
Err(_) => {
println!("Port {} is in use, trying next port...", port);
}
}
}

let server = match server {
Some(s) => s,
None => {
tracing::error!(
"Failed to bind to any port in the range 8000..20000 when attempting auth with elasticnow"
);
std::process::exit(1);
}
};

// Define the login URL
let login_url = format!("{}/cli/login/redirect/{}", elasticnow_url, chosen_port);

// Open the browser for user to login
match that(login_url) {
Ok(_) => tracing::info!("Opened browser for to login to ElasticNow"),
Err(e) => tracing::info!("Failed to open browser: {}", e),
}

// Set up the local server to capture the cookie
let mut _id = "".to_string();
for request in server.incoming_requests() {
let response = Response::from_string("Login successful. You can close this window.");
_id = request.url().to_string();
_id.remove(0);
request.respond(response).unwrap();
break;
}
tracing::info!("Got cookie: {}", _id);
_id.to_string()
}

0 comments on commit a63f9d4

Please sign in to comment.