Skip to content

Commit

Permalink
Merge pull request #120 from wolfv/auth
Browse files Browse the repository at this point in the history
add authentication subcommand
  • Loading branch information
ruben-arts authored Jun 23, 2023
2 parents cdb021d + fd129a8 commit 7f1a928
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ tokio = { version = "1.27.0", features = ["macros", "rt-multi-thread"] }
toml_edit = { version = "0.19.10", features = ["serde"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
url = "2.4.0"

[profile.release-lto]
inherits = "release"
Expand Down
118 changes: 118 additions & 0 deletions src/cli/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use clap::Parser;
use rattler_networking::{Authentication, AuthenticationStorage};

fn default_authentication_storage() -> AuthenticationStorage {
AuthenticationStorage::new("rattler", &dirs::home_dir().unwrap().join(".rattler"))
}

#[derive(Parser, Debug)]
pub struct LoginArgs {
/// The host to authenticate with (e.g. repo.prefix.dev)
host: String,

/// The token to use (for authentication with prefix.dev)
#[clap(long)]
token: Option<String>,

/// The username to use (for basic HTTP authentication)
#[clap(long)]
username: Option<String>,

/// The password to use (for basic HTTP authentication)
#[clap(long)]
password: Option<String>,

/// The token to use on anaconda.org / quetz authentication
#[clap(long)]
conda_token: Option<String>,
}

#[derive(Parser, Debug)]
struct LogoutArgs {
/// The host to remove authentication for
host: String,
}

#[derive(Parser, Debug)]
enum Subcommand {
/// Store authentication information for a given host
Login(LoginArgs),
/// Remove authentication information for a given host
Logout(LogoutArgs),
}

/// Login to prefix.dev or anaconda.org servers to access private channels
#[derive(Parser, Debug)]
pub struct Args {
#[clap(subcommand)]
subcommand: Subcommand,
}

fn get_url(url: &str) -> anyhow::Result<String> {
// parse as url and extract host without scheme or port
let host = if url.contains("://") {
url::Url::parse(url)?.host_str().unwrap().to_string()
} else {
url.to_string()
};

let host = if host == "prefix.dev" {
"repo.prefix.dev"
} else if host == "anaconda.org" {
"conda.anaconda.org"
} else {
&host
};

Ok(host.to_string())
}

fn login(args: LoginArgs, storage: AuthenticationStorage) -> anyhow::Result<()> {
let host = get_url(&args.host)?;
println!("Authenticating with {}", host);

let auth = if let Some(conda_token) = args.conda_token {
Authentication::CondaToken(conda_token)
} else if let Some(username) = args.username {
if args.password.is_none() {
anyhow::bail!("Password must be provided when using basic authentication");
}
let password = args.password.unwrap();
Authentication::BasicHTTP { username, password }
} else if let Some(token) = args.token {
Authentication::BearerToken(token)
} else {
anyhow::bail!("No authentication method provided");
};

if host.contains("prefix.dev") && !matches!(auth, Authentication::BearerToken(_)) {
anyhow::bail!(
"Authentication with prefix.dev requires a token. Use `--token` to provide one."
);
}

if host.contains("anaconda.org") && !matches!(auth, Authentication::CondaToken(_)) {
anyhow::bail!("Authentication with anaconda.org requires a conda token. Use `--conda-token` to provide one.");
}

storage.store(&host, &auth)?;
Ok(())
}

fn logout(args: LogoutArgs, storage: AuthenticationStorage) -> anyhow::Result<()> {
let host = get_url(&args.host)?;

println!("Removing authentication for {}", host);

storage.delete(&host)?;
Ok(())
}

pub async fn execute(args: Args) -> anyhow::Result<()> {
let storage = default_authentication_storage();

match args.subcommand {
Subcommand::Login(args) => login(args, storage),
Subcommand::Logout(args) => logout(args, storage),
}
}
3 changes: 3 additions & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use anyhow::Error;
use tracing_subscriber::{filter::LevelFilter, util::SubscriberInitExt, EnvFilter};

mod add;
mod auth;
mod global;
mod init;
mod run;
Expand Down Expand Up @@ -43,6 +44,7 @@ enum Command {
Run(run::Args),
#[clap(alias = "g")]
Global(global::Args),
Auth(auth::Args),
}

fn completion(args: CompletionCommand) -> Result<(), Error> {
Expand Down Expand Up @@ -102,6 +104,7 @@ pub async fn execute() -> anyhow::Result<()> {
Some(Command::Add(cmd)) => add::execute(cmd).await,
Some(Command::Run(cmd)) => run::execute(cmd).await,
Some(Command::Global(cmd)) => global::execute(cmd).await,
Some(Command::Auth(cmd)) => auth::execute(cmd).await,
None => default().await,
}
}

0 comments on commit 7f1a928

Please sign in to comment.