Skip to content

Commit f4c04f7

Browse files
committed
Add foreman github-auth.
Closes #14.
1 parent ab3db10 commit f4c04f7

File tree

8 files changed

+187
-7
lines changed

8 files changed

+187
-7
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Foreman Changelog
22

3+
## [Unreleased]
4+
- Added `foreman github-auth` command for authenticating with GitHub.
5+
36
## 0.5.1
47
- On Unix systems, tools now always have permissions of 777.
58
- This ensures that they're executable, even when the containing archives fail to preserve permissions.

Cargo.lock

Lines changed: 105 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ log = "0.4.8"
1717
reqwest = { version = "0.10.1", features = ["json", "blocking"] }
1818
semver = { version = "0.9.0", features = ["serde"] }
1919
serde = { version = "1.0.104", features = ["derive"] }
20+
rpassword = "4.0.5"
2021
serde_json = "1.0.47"
2122
structopt = "0.3.9"
2223
toml = "0.5.6"
24+
toml_edit = "0.1.5"
2325
zip = "0.5"

resources/default-auth.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
# `github` key. This is useful if you hit GitHub API rate limits or if you need
55
# to access private tools.
66

7-
# In order to use sources from private repositories, this token will need "repo"
8-
# permissions granted to it and potentially SSO authorization.
7+
# You can also run `foreman github-auth` to update this file, optionally passing
8+
# the token as the first argument.
99

10-
# github = "<PAC HERE>"
10+
# In order to use sources from private repositories, this token will need "repo"
11+
# permissions granted to it and potentially SSO authorization.

src/auth_store.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use std::io;
22

33
use serde::{Deserialize, Serialize};
4+
use toml_edit::{value, Document};
45

56
use crate::{fs, paths};
67

8+
pub static DEFAULT_AUTH_CONFIG: &str = include_str!("../resources/default-auth.toml");
9+
710
/// Contains stored user tokens that Foreman can use to download tools.
811
#[derive(Debug, Default, Serialize, Deserialize)]
912
pub struct AuthStore {
@@ -35,4 +38,23 @@ impl AuthStore {
3538
}
3639
}
3740
}
41+
42+
pub fn set_github_token(token: &str) -> io::Result<()> {
43+
let contents = match fs::read_to_string(paths::auth_store()) {
44+
Ok(contents) => contents,
45+
Err(err) => {
46+
if err.kind() == io::ErrorKind::NotFound {
47+
DEFAULT_AUTH_CONFIG.to_owned()
48+
} else {
49+
return Err(err);
50+
}
51+
}
52+
};
53+
54+
let mut store: Document = contents.parse().unwrap();
55+
store["github"] = value(token);
56+
57+
let serialized = store.to_string();
58+
fs::write(paths::auth_store(), serialized)
59+
}
3860
}

src/fs.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ pub fn read<P: AsRef<Path>>(path: P) -> Result<Vec<u8>> {
2121
fs::read(path).map_err(|source| Error::new(source, path))
2222
}
2323

24+
/// A wrapper around std::fs::read_to_string.
25+
pub fn read_to_string<P: AsRef<Path>>(path: P) -> Result<String> {
26+
let path = path.as_ref();
27+
28+
fs::read_to_string(path).map_err(|source| Error::new(source, path))
29+
}
30+
2431
/// A wrapper around std::fs::write.
2532
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
2633
let path = path.as_ref();

src/main.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ use std::{env, error::Error, io};
1212

1313
use structopt::StructOpt;
1414

15-
use crate::{aliaser::add_self_alias, config::ConfigFile, tool_cache::ToolCache};
15+
use crate::{
16+
aliaser::add_self_alias, auth_store::AuthStore, config::ConfigFile, tool_cache::ToolCache,
17+
};
1618

1719
#[derive(Debug)]
1820
struct ToolInvocation {
@@ -88,6 +90,21 @@ enum Subcommand {
8890

8991
/// List installed tools.
9092
List,
93+
94+
/// Set the GitHub Personal Access Token that Foreman should use with the
95+
/// GitHub API.
96+
///
97+
/// This token can also be configured by editing ~/.foreman/auth.toml.
98+
#[structopt(name = "github-auth")]
99+
GitHubAuth(GitHubAuthCommand),
100+
}
101+
102+
#[derive(Debug, StructOpt)]
103+
struct GitHubAuthCommand {
104+
/// GitHub personal access token that Foreman should use.
105+
///
106+
/// If not specified, Foreman will prompt for it.
107+
token: Option<String>,
91108
}
92109

93110
fn actual_main() -> io::Result<()> {
@@ -117,6 +134,30 @@ fn actual_main() -> io::Result<()> {
117134
}
118135
}
119136
}
137+
Subcommand::GitHubAuth(subcommand) => {
138+
let token = match subcommand.token {
139+
Some(token) => token,
140+
None => {
141+
println!("Foreman authenticates to GitHub using Personal Access Tokens.");
142+
println!("https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line");
143+
println!();
144+
145+
loop {
146+
let token = rpassword::read_password_from_tty(Some("GitHub Token: "))?;
147+
148+
if token.is_empty() {
149+
println!("Token must be non-empty.");
150+
} else {
151+
break token;
152+
}
153+
}
154+
}
155+
};
156+
157+
AuthStore::set_github_token(&token)?;
158+
159+
println!("GitHub auth saved successfully.");
160+
}
120161
}
121162

122163
Ok(())

src/paths.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
33
use std::{io, path::PathBuf};
44

5-
use crate::fs;
5+
use crate::{auth_store::DEFAULT_AUTH_CONFIG, fs};
66

77
static DEFAULT_USER_CONFIG: &str = include_str!("../resources/default-foreman.toml");
8-
static DEFAULT_AUTH_STORE: &str = include_str!("../resources/default-auth.toml");
98

109
pub fn base_dir() -> PathBuf {
1110
let mut dir = dirs::home_dir().unwrap();
@@ -54,7 +53,7 @@ pub fn create() -> io::Result<()> {
5453
let auth = auth_store();
5554
if let Err(err) = fs::metadata(&auth) {
5655
if err.kind() == io::ErrorKind::NotFound {
57-
fs::write(&auth, DEFAULT_AUTH_STORE)?;
56+
fs::write(&auth, DEFAULT_AUTH_CONFIG)?;
5857
} else {
5958
return Err(err);
6059
}

0 commit comments

Comments
 (0)