Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add command to add Artifactory Auth #91

Merged
merged 2 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ default-members = [".", "artiaa_auth"]
[package]
name = "foreman"
description = "Toolchain manager for simple binary tools"
version = "1.5.0"
version = "1.6.0"
authors = [
"Lucien Greathouse <me@lpghatguy.com>",
"Matt Hargett <plaztiksyke@gmail.com>",
Expand Down
72 changes: 72 additions & 0 deletions src/artifactory_auth_store.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use crate::error::ForemanError;
use crate::{error::ForemanResult, fs};
use artiaa_auth::{error::ArtifactoryAuthError, Credentials};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use std::{
ops::{Deref, DerefMut},
path::Path,
};
/// Contains stored user tokens that Foreman can use to download tools.
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ArtifactoryAuthStore {
tokens: HashMap<String, Credentials>,
}

impl Deref for ArtifactoryAuthStore {
type Target = HashMap<String, Credentials>;

fn deref(&self) -> &Self::Target {
&self.tokens
}
}

impl DerefMut for ArtifactoryAuthStore {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.tokens
}
}

impl ArtifactoryAuthStore {
pub fn set_token(auth_file: &Path, key: &str, token: &str) -> ForemanResult<()> {
let contents = fs::try_read_to_string(auth_file)?;

let mut store: ArtifactoryAuthStore = if let Some(contents) = contents {
serde_json::from_str(&contents).map_err(|err: serde_json::Error| {
ForemanError::ArtiAAError {
error: ArtifactoryAuthError::auth_parsing(auth_file, err.to_string()),
}
})?
} else {
ArtifactoryAuthStore::default()
};

store.insert(
key.to_owned(),
Credentials {
username: "".to_owned(),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The artifactory auth format assumes a username field exists, but it isn't actually used to authenticate to Artifactory

token: token.to_owned(),
},
);

let serialized =
serde_json::to_string_pretty(&store).map_err(|err: serde_json::Error| {
ForemanError::ArtiAAError {
error: ArtifactoryAuthError::auth_parsing(auth_file, err.to_string()),
}
})?;

if let Some(dir) = auth_file.parent() {
fs::create_dir_all(dir)?;
fs::write(auth_file, serialized)
} else {
Err(ForemanError::ArtiAAError {
error: ArtifactoryAuthError::auth_parsing(
auth_file,
"Could not find parent directory of auth file".to_owned(),
),
})
}
}
}
62 changes: 61 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod aliaser;
mod artifact_choosing;
mod artifactory_auth_store;
mod artifactory_path;
mod auth_store;
mod ci_string;
Expand All @@ -11,8 +12,13 @@ mod process;
mod tool_cache;
mod tool_provider;

use std::{env, ffi::OsStr};
use std::{
env,
ffi::OsStr,
io::{stdout, Write},
};

use artifactory_auth_store::ArtifactoryAuthStore;
use paths::ForemanPaths;
use structopt::StructOpt;

Expand Down Expand Up @@ -153,6 +159,11 @@ enum Subcommand {
#[structopt(name = "gitlab-auth")]
GitLabAuth(GitLabAuthCommand),

/// Set the Artifactory Token that Foreman should use with the
/// Artifactory API.
#[structopt(name = "artifactory-auth")]
ArtifactoryAuth(ArtifactoryAuthCommand),

/// Create a path to publish to artifactory
///
/// Foreman does not support uploading binaries to artifactory directly, but it can generate the path where it would expect to find a given artifact. Use this command to generate paths that can be input to generic artifactory upload solutions.
Expand All @@ -176,6 +187,12 @@ struct GitLabAuthCommand {
token: Option<String>,
}

#[derive(Debug, StructOpt)]
struct ArtifactoryAuthCommand {
url: Option<String>,
token: Option<String>,
}

#[derive(Debug, StructOpt)]
struct GenerateArtifactoryPathCommand {
repo: String,
Expand Down Expand Up @@ -297,11 +314,54 @@ fn actual_main(paths: ForemanPaths) -> ForemanResult<()> {
)?;
println!("{}", artifactory_path);
}
Subcommand::ArtifactoryAuth(subcommand) => {
let url = prompt_url(subcommand.url)?;

let token = prompt_auth_token(
subcommand.token,
"Artifactory",
"https://jfrog.com/help/r/jfrog-platform-administration-documentation/access-tokens",
)?;

ArtifactoryAuthStore::set_token(&paths.artiaa_path()?, &url, &token)?;
}
}

Ok(())
}

fn prompt_url(url: Option<String>) -> Result<String, ForemanError> {
match url {
Some(url) => Ok(url),
None => {
println!("Artifactory auth saved successfully.");
println!("Foreman requires a specific URL to authenticate to Artifactory.");
println!();

loop {
let mut input = String::new();

print!("Artifactory URL: ");
stdout().flush().map_err(|err| {
ForemanError::io_error_with_context(
err,
"an error happened trying to flush stdout",
)
})?;
std::io::stdin().read_line(&mut input).map_err(|err| {
ForemanError::io_error_with_context(err, "an error happened trying to read url")
})?;

if input.is_empty() {
println!("Token must be non-empty.");
} else {
break Ok(input);
}
}
}
}
}

fn prompt_auth_token(
token: Option<String>,
provider: &str,
Expand Down
2 changes: 1 addition & 1 deletion src/tool_provider/artifactory.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Slice of GitHub's API that Foreman consumes.
//! Slice of Artifactory's API that Foreman consumes.

use super::{Release, ReleaseAsset, ToolProviderImpl};
use crate::{
Expand Down
4 changes: 3 additions & 1 deletion tests/snapshots/help_command.snap
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
---
source: tests/cli.rs
assertion_line: 100
expression: content
---
foreman 1.5.0
foreman 1.6.0

USAGE:
foreman [FLAGS] <SUBCOMMAND>
Expand All @@ -13,6 +14,7 @@ FLAGS:
-v Logging verbosity. Supply multiple for more verbosity, up to -vvv

SUBCOMMANDS:
artifactory-auth Set the Artifactory Token that Foreman should use with the Artifactory API
generate-artifactory-path Create a path to publish to artifactory
github-auth Set the GitHub Personal Access Token that Foreman should use with the GitHub API
gitlab-auth Set the GitLab Personal Access Token that Foreman should use with the GitLab API
Expand Down
Loading