Skip to content

Commit 2774595

Browse files
Add command to add Artifactory Auth (#91)
We can already read Artifactory tokens, but Foreman has no way of adding the tokens them itself. This PR looks to add the command `artifactory-auth`, that places the tokens in the expected format in the expected location. This PR also includes a minor version bump since we are adding behavior.
1 parent 75deeeb commit 2774595

File tree

6 files changed

+139
-5
lines changed

6 files changed

+139
-5
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ default-members = [".", "artiaa_auth"]
66
[package]
77
name = "foreman"
88
description = "Toolchain manager for simple binary tools"
9-
version = "1.5.0"
9+
version = "1.6.0"
1010
authors = [
1111
"Lucien Greathouse <me@lpghatguy.com>",
1212
"Matt Hargett <plaztiksyke@gmail.com>",

src/artifactory_auth_store.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use crate::error::ForemanError;
2+
use crate::{error::ForemanResult, fs};
3+
use artiaa_auth::{error::ArtifactoryAuthError, Credentials};
4+
use serde::{Deserialize, Serialize};
5+
use std::collections::HashMap;
6+
7+
use std::{
8+
ops::{Deref, DerefMut},
9+
path::Path,
10+
};
11+
/// Contains stored user tokens that Foreman can use to download tools.
12+
#[derive(Debug, Default, Serialize, Deserialize)]
13+
pub struct ArtifactoryAuthStore {
14+
tokens: HashMap<String, Credentials>,
15+
}
16+
17+
impl Deref for ArtifactoryAuthStore {
18+
type Target = HashMap<String, Credentials>;
19+
20+
fn deref(&self) -> &Self::Target {
21+
&self.tokens
22+
}
23+
}
24+
25+
impl DerefMut for ArtifactoryAuthStore {
26+
fn deref_mut(&mut self) -> &mut Self::Target {
27+
&mut self.tokens
28+
}
29+
}
30+
31+
impl ArtifactoryAuthStore {
32+
pub fn set_token(auth_file: &Path, key: &str, token: &str) -> ForemanResult<()> {
33+
let contents = fs::try_read_to_string(auth_file)?;
34+
35+
let mut store: ArtifactoryAuthStore = if let Some(contents) = contents {
36+
serde_json::from_str(&contents).map_err(|err: serde_json::Error| {
37+
ForemanError::ArtiAAError {
38+
error: ArtifactoryAuthError::auth_parsing(auth_file, err.to_string()),
39+
}
40+
})?
41+
} else {
42+
ArtifactoryAuthStore::default()
43+
};
44+
45+
store.insert(
46+
key.to_owned(),
47+
Credentials {
48+
username: "".to_owned(),
49+
token: token.to_owned(),
50+
},
51+
);
52+
53+
let serialized =
54+
serde_json::to_string_pretty(&store).map_err(|err: serde_json::Error| {
55+
ForemanError::ArtiAAError {
56+
error: ArtifactoryAuthError::auth_parsing(auth_file, err.to_string()),
57+
}
58+
})?;
59+
60+
if let Some(dir) = auth_file.parent() {
61+
fs::create_dir_all(dir)?;
62+
fs::write(auth_file, serialized)
63+
} else {
64+
Err(ForemanError::ArtiAAError {
65+
error: ArtifactoryAuthError::auth_parsing(
66+
auth_file,
67+
"Could not find parent directory of auth file".to_owned(),
68+
),
69+
})
70+
}
71+
}
72+
}

src/main.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod aliaser;
22
mod artifact_choosing;
3+
mod artifactory_auth_store;
34
mod artifactory_path;
45
mod auth_store;
56
mod ci_string;
@@ -11,8 +12,13 @@ mod process;
1112
mod tool_cache;
1213
mod tool_provider;
1314

14-
use std::{env, ffi::OsStr};
15+
use std::{
16+
env,
17+
ffi::OsStr,
18+
io::{stdout, Write},
19+
};
1520

21+
use artifactory_auth_store::ArtifactoryAuthStore;
1622
use paths::ForemanPaths;
1723
use structopt::StructOpt;
1824

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

162+
/// Set the Artifactory Token that Foreman should use with the
163+
/// Artifactory API.
164+
#[structopt(name = "artifactory-auth")]
165+
ArtifactoryAuth(ArtifactoryAuthCommand),
166+
156167
/// Create a path to publish to artifactory
157168
///
158169
/// 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.
@@ -176,6 +187,12 @@ struct GitLabAuthCommand {
176187
token: Option<String>,
177188
}
178189

190+
#[derive(Debug, StructOpt)]
191+
struct ArtifactoryAuthCommand {
192+
url: Option<String>,
193+
token: Option<String>,
194+
}
195+
179196
#[derive(Debug, StructOpt)]
180197
struct GenerateArtifactoryPathCommand {
181198
repo: String,
@@ -297,11 +314,54 @@ fn actual_main(paths: ForemanPaths) -> ForemanResult<()> {
297314
)?;
298315
println!("{}", artifactory_path);
299316
}
317+
Subcommand::ArtifactoryAuth(subcommand) => {
318+
let url = prompt_url(subcommand.url)?;
319+
320+
let token = prompt_auth_token(
321+
subcommand.token,
322+
"Artifactory",
323+
"https://jfrog.com/help/r/jfrog-platform-administration-documentation/access-tokens",
324+
)?;
325+
326+
ArtifactoryAuthStore::set_token(&paths.artiaa_path()?, &url, &token)?;
327+
}
300328
}
301329

302330
Ok(())
303331
}
304332

333+
fn prompt_url(url: Option<String>) -> Result<String, ForemanError> {
334+
match url {
335+
Some(url) => Ok(url),
336+
None => {
337+
println!("Artifactory auth saved successfully.");
338+
println!("Foreman requires a specific URL to authenticate to Artifactory.");
339+
println!();
340+
341+
loop {
342+
let mut input = String::new();
343+
344+
print!("Artifactory URL: ");
345+
stdout().flush().map_err(|err| {
346+
ForemanError::io_error_with_context(
347+
err,
348+
"an error happened trying to flush stdout",
349+
)
350+
})?;
351+
std::io::stdin().read_line(&mut input).map_err(|err| {
352+
ForemanError::io_error_with_context(err, "an error happened trying to read url")
353+
})?;
354+
355+
if input.is_empty() {
356+
println!("Token must be non-empty.");
357+
} else {
358+
break Ok(input);
359+
}
360+
}
361+
}
362+
}
363+
}
364+
305365
fn prompt_auth_token(
306366
token: Option<String>,
307367
provider: &str,

src/tool_provider/artifactory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Slice of GitHub's API that Foreman consumes.
1+
//! Slice of Artifactory's API that Foreman consumes.
22
33
use super::{Release, ReleaseAsset, ToolProviderImpl};
44
use crate::{

tests/snapshots/help_command.snap

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
---
22
source: tests/cli.rs
3+
assertion_line: 100
34
expression: content
45
---
5-
foreman 1.5.0
6+
foreman 1.6.0
67

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

1516
SUBCOMMANDS:
17+
artifactory-auth Set the Artifactory Token that Foreman should use with the Artifactory API
1618
generate-artifactory-path Create a path to publish to artifactory
1719
github-auth Set the GitHub Personal Access Token that Foreman should use with the GitHub API
1820
gitlab-auth Set the GitLab Personal Access Token that Foreman should use with the GitLab API

0 commit comments

Comments
 (0)