Skip to content

Commit

Permalink
Merge pull request #5 from spencerwooo:dm-logout
Browse files Browse the repository at this point in the history
Extra `--dm` logout parameter
  • Loading branch information
spencerwooo committed Dec 3, 2023
2 parents b352313 + c7b305a commit 90ef99c
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "bitsrun"
description = "A headless login and logout CLI app for 10.0.0.55 at BIT"
version = "0.2.1"
version = "0.3.0"
edition = "2021"
license = "MIT"
homepage = "https://github.com/spencerwooo/bitsrun-rs"
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,20 @@ Options:
> [!TIP]
> Use environment variable `NO_COLOR=true` to disable colored output.
## Credentials
## Config and credentials

To save your credentials, create config file `bit-user.json` under an available config path as:
To save your credentials and configurations, create config file `bit-user.json` under an available config path as:

```json
{
"username": "<username>",
"password": "<password>"
"password": "<password>",
"dm": true
}
```

**`dm` is for specifying whether the current device is a dumb terminal, and requires logging out through the alternative endpoint. Set to `true` (no quotes!) if the device you are working with is a dumb terminal.**

Available config file paths can be listed with:

```console
Expand Down
6 changes: 5 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ pub struct ClientArgs {
pub password: Option<String>,

/// Manually specify IP address (IPv4)
#[arg(short, long)]
#[arg(long)]
pub ip: Option<IpAddr>,

/// Use alternative `dm` logout endpoint for registered dumb terminals
#[arg(long)]
pub dm: bool,

/// Optionally provide path to the config file
#[arg(short, long)]
pub config: Option<String>,
Expand Down
50 changes: 43 additions & 7 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ pub struct SrunClient {
// srun portal info
pub ip: IpAddr,
pub ac_id: String,
pub dm: bool, // whether the device is authenticated with its mac address
pub login_state: SrunLoginState,
}

Expand All @@ -197,23 +198,28 @@ impl SrunClient {
/// * `username` - The username of the SRUN account (student id)
/// * `password` - The password of the SRUN account
/// * `ip` - The IP address (`online_ip` from the login portal if not specified)
/// * `dm` - Whether the device is authenticated through the campus login portal with its mac
/// address (important for dumb terminals!!!)
/// * `http_client` - The http client to be used (a new one will be created if not specified)
pub async fn new(
username: String,
password: String,
http_client: Option<Client>,
ip: Option<IpAddr>,
dm: Option<bool>,
) -> Result<SrunClient> {
let http_client = http_client.unwrap_or_default();
let ac_id = get_acid(&http_client).await?;
let login_state = get_login_state(&http_client).await?;
let ip = ip.unwrap_or(login_state.online_ip);
let dm = dm.unwrap_or(false);
Ok(SrunClient {
http_client,
username,
password,
ip,
ac_id,
dm,
login_state,
})
}
Expand Down Expand Up @@ -333,14 +339,44 @@ impl SrunClient {
}

// perform logout action
let params = [
("callback", "jsonp"),
("action", "logout"),
("ip", &self.ip.to_string()),
("ac_id", self.ac_id.as_str()),
("username", logged_in_username.as_str()),
let url = {
// dumb terminals use a different endpoint (dm logout)
match self.dm {
true => format!("{}/cgi-bin/rad_user_dm", SRUN_PORTAL),
false => format!("{}/cgi-bin/srun_portal", SRUN_PORTAL),
}
};

let ip_str = self.ip.to_string();
let mut params = vec![
("callback", String::from("jsonp")),
("ip", self.ip.to_string()),
("username", logged_in_username.clone()),
];
let url = format!("{}/cgi-bin/srun_portal", SRUN_PORTAL);

if self.dm {
use chrono::Utc;
let timestamp = Utc::now().timestamp().to_string();
let unbind = String::from("1");

let sign = {
let mut hasher = Sha1::new();
let sn = format!(
"{0}{1}{2}{3}{0}",
timestamp, logged_in_username, ip_str, unbind
);

hasher.update(sn);
format!("{:x}", hasher.finalize())
};

params.push(("time", timestamp));
params.push(("unbind", unbind));
params.push(("sign", sign));
} else {
params.push(("action", String::from("logout")));
params.push(("ac_id", self.ac_id.clone()));
}

let resp = self
.http_client
Expand Down
4 changes: 3 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ async fn cli() -> Result<()> {
let bit_user = user::get_bit_user(
&client_args.username,
&client_args.password,
client_args.dm,
&client_args.config,
matches!(args.command, Some(Commands::Login(_))),
)
Expand All @@ -92,6 +93,7 @@ async fn cli() -> Result<()> {
bit_user.password,
Some(http_client),
client_args.ip,
Some(client_args.dm),
)
.await?;

Expand Down Expand Up @@ -126,7 +128,7 @@ async fn cli() -> Result<()> {
} else if matches!(args.command, Some(Commands::Logout(_))) {
let resp = srun_client.logout().await?;
match resp.error.as_str() {
"ok" => println!(
"ok" | "logout_ok" => println!(
"{} {} logged out",
"bitsrun:".if_supports_color(Stdout, |t| t.green()),
resp.online_ip
Expand Down
25 changes: 22 additions & 3 deletions src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,23 @@ use serde::Serialize;
pub struct BitUser {
pub username: String,
pub password: String,
pub dm: bool,
}

/// Partial campus network user credentials
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct BitUserPartial {
pub username: Option<String>,
pub password: Option<String>,
pub dm: Option<bool>,
}

impl BitUserPartial {
pub fn new(username: &Option<String>, password: &Option<String>) -> Self {
pub fn new(username: &Option<String>, password: &Option<String>, dm: Option<bool>) -> Self {
Self {
username: username.clone(),
password: password.clone(),
dm,
}
}
}
Expand Down Expand Up @@ -132,7 +135,7 @@ fn parse_config_file(config_path: &Option<String>) -> Result<BitUserPartial> {

#[cfg(windows)]
#[allow(unused)]
fn check_permissions(_config: &String, _meta: &std::fs::Metadata) -> Result<(), anyhow::Error> {
fn check_permissions(_config: &str, _meta: &std::fs::Metadata) -> Result<(), anyhow::Error> {
// Windows doesn't support Unix-style permissions, so we'll just return Ok here.
Ok(())
}
Expand Down Expand Up @@ -171,10 +174,11 @@ fn parse_config_file(config_path: &Option<String>) -> Result<BitUserPartial> {
pub fn get_bit_user(
username: &Option<String>,
password: &Option<String>,
dm: bool,
config_path: &Option<String>,
require_password: bool,
) -> Result<BitUser> {
let mut bit_user = BitUserPartial::new(username, password);
let mut bit_user = BitUserPartial::new(username, password, Some(dm));

// username and password priority: command line > config file > prompt
if bit_user.username.is_none() | (require_password & bit_user.password.is_none()) {
Expand All @@ -188,6 +192,20 @@ pub fn get_bit_user(
),
}

if user_from_file.dm.is_none() & !dm {
println!(
"{} logout endpoint not specified in config file! \
logging out may encounter unexpected results",
"warning:".if_supports_color(Stdout, |t| t.yellow()),
);
println!(
"{} if this device is a '{}', explicity specify `{}` to use alternative logout endpoint",
"warning:".if_supports_color(Stdout, |t| t.yellow()),
"registered dumb terminal".if_supports_color(Stdout, |t| t.on_yellow()),
"--dm".if_supports_color(Stdout, |t| t.underline())
);
}

match user_from_file.username {
Some(username) => bit_user.username.get_or_insert(username),
None => bit_user.username.get_or_insert_with(|| {
Expand Down Expand Up @@ -219,5 +237,6 @@ pub fn get_bit_user(
Ok(BitUser {
username: bit_user.username.unwrap_or_default(),
password: bit_user.password.unwrap_or_default(),
dm: bit_user.dm.unwrap_or_default(),
})
}

0 comments on commit 90ef99c

Please sign in to comment.