summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBasiqueEvangelist <basiqueevangelist@yandex.ru>2021-09-08 07:18:11 +0300
committerGitHub <noreply@github.com>2021-09-08 06:18:11 +0200
commit08e12380a06509e64e69a1b165f3c007373494c1 (patch)
tree6663b4d38a8d2fd5daf1b3f189a1a1bc67270e64
parentbf6dbf9855cb21d27838c56932c7536c0cd80a55 (diff)
feat(cli): Support Basic authentication in DENO_AUTH_TOKENS (#11910)
-rw-r--r--Cargo.lock1
-rw-r--r--cli/auth_tokens.rs55
-rw-r--r--cli/tests/integration/mod.rs48
-rw-r--r--test_util/Cargo.toml1
-rw-r--r--test_util/src/lib.rs41
5 files changed, 142 insertions, 4 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 34cbbf275..09eb674c0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3838,6 +3838,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-stream",
+ "base64 0.13.0",
"bytes",
"futures",
"hyper",
diff --git a/cli/auth_tokens.rs b/cli/auth_tokens.rs
index 83c97e641..c81c296f3 100644
--- a/cli/auth_tokens.rs
+++ b/cli/auth_tokens.rs
@@ -6,14 +6,26 @@ use log::error;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum AuthTokenData {
+ Bearer(String),
+ Basic { username: String, password: String },
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthToken {
host: String,
- token: String,
+ token: AuthTokenData,
}
impl fmt::Display for AuthToken {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "Bearer {}", self.token)
+ match &self.token {
+ AuthTokenData::Bearer(token) => write!(f, "Bearer {}", token),
+ AuthTokenData::Basic { username, password } => {
+ let credentials = format!("{}:{}", username, password);
+ write!(f, "Basic {}", base64::encode(credentials))
+ }
+ }
}
}
@@ -34,9 +46,22 @@ impl AuthTokens {
for token_str in tokens_str.split(';') {
if token_str.contains('@') {
let pair: Vec<&str> = token_str.rsplitn(2, '@').collect();
- let token = pair[1].to_string();
+ let token = pair[1];
let host = pair[0].to_lowercase();
- tokens.push(AuthToken { host, token });
+ if token.contains(':') {
+ let pair: Vec<&str> = token.rsplitn(2, ':').collect();
+ let username = pair[1].to_string();
+ let password = pair[0].to_string();
+ tokens.push(AuthToken {
+ host,
+ token: AuthTokenData::Basic { username, password },
+ })
+ } else {
+ tokens.push(AuthToken {
+ host,
+ token: AuthTokenData::Bearer(token.to_string()),
+ });
+ }
} else {
error!("Badly formed auth token discarded.");
}
@@ -133,4 +158,26 @@ mod tests {
"Bearer abc@123".to_string()
);
}
+
+ #[test]
+ fn test_auth_token_basic() {
+ let auth_tokens = AuthTokens::new(Some("abc:123@deno.land".to_string()));
+ let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap();
+ assert_eq!(
+ auth_tokens.get(&fixture).unwrap().to_string(),
+ "Basic YWJjOjEyMw=="
+ );
+ let fixture = resolve_url("https://www.deno.land/x/mod.ts").unwrap();
+ assert_eq!(
+ auth_tokens.get(&fixture).unwrap().to_string(),
+ "Basic YWJjOjEyMw==".to_string()
+ );
+ let fixture = resolve_url("http://127.0.0.1:8080/x/mod.ts").unwrap();
+ assert_eq!(auth_tokens.get(&fixture), None);
+ let fixture =
+ resolve_url("https://deno.land.example.com/x/mod.ts").unwrap();
+ assert_eq!(auth_tokens.get(&fixture), None);
+ let fixture = resolve_url("https://deno.land:8080/x/mod.ts").unwrap();
+ assert_eq!(auth_tokens.get(&fixture), None);
+ }
}
diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs
index 7d13c7831..4ada4ef54 100644
--- a/cli/tests/integration/mod.rs
+++ b/cli/tests/integration/mod.rs
@@ -1077,6 +1077,54 @@ fn js_unit_tests() {
assert!(status.success());
}
+#[test]
+fn basic_auth_tokens() {
+ let _g = util::http_server();
+
+ let output = util::deno_cmd()
+ .current_dir(util::root_path())
+ .arg("run")
+ .arg("http://127.0.0.1:4554/001_hello.js")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+
+ assert!(!output.status.success());
+
+ let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim();
+ assert!(stdout_str.is_empty());
+
+ let stderr_str = std::str::from_utf8(&output.stderr).unwrap().trim();
+ eprintln!("{}", stderr_str);
+
+ assert!(stderr_str.contains(
+ "Import 'http://127.0.0.1:4554/001_hello.js' failed: 404 Not Found"
+ ));
+
+ let output = util::deno_cmd()
+ .current_dir(util::root_path())
+ .arg("run")
+ .arg("http://127.0.0.1:4554/001_hello.js")
+ .env("DENO_AUTH_TOKENS", "testuser123:testpassabc@127.0.0.1:4554")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+
+ let stderr_str = std::str::from_utf8(&output.stderr).unwrap().trim();
+ eprintln!("{}", stderr_str);
+
+ assert!(output.status.success());
+
+ let stdout_str = std::str::from_utf8(&output.stdout).unwrap().trim();
+ assert_eq!(util::strip_ansi_codes(stdout_str), "Hello World");
+}
+
#[tokio::test]
async fn listen_tls_alpn() {
// TLS streams require the presence of an ambient local task set to gracefully
diff --git a/test_util/Cargo.toml b/test_util/Cargo.toml
index 5e2a41ed8..b33f389a6 100644
--- a/test_util/Cargo.toml
+++ b/test_util/Cargo.toml
@@ -14,6 +14,7 @@ path = "src/test_server.rs"
[dependencies]
anyhow = "1.0.43"
async-stream = "0.3.2"
+base64 = "0.13.0"
bytes = "1.1.0"
futures = "0.3.16"
hyper = { version = "0.14.12", features = ["server", "http1", "runtime"] }
diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs
index 46679b98d..3d9913576 100644
--- a/test_util/src/lib.rs
+++ b/test_util/src/lib.rs
@@ -51,6 +51,8 @@ pub mod lsp;
const PORT: u16 = 4545;
const TEST_AUTH_TOKEN: &str = "abcdef123456789";
+const TEST_BASIC_AUTH_USERNAME: &str = "testuser123";
+const TEST_BASIC_AUTH_PASSWORD: &str = "testpassabc";
const REDIRECT_PORT: u16 = 4546;
const ANOTHER_REDIRECT_PORT: u16 = 4547;
const DOUBLE_REDIRECTS_PORT: u16 = 4548;
@@ -58,6 +60,7 @@ const INF_REDIRECTS_PORT: u16 = 4549;
const REDIRECT_ABSOLUTE_PORT: u16 = 4550;
const AUTH_REDIRECT_PORT: u16 = 4551;
const TLS_CLIENT_AUTH_PORT: u16 = 4552;
+const BASIC_AUTH_REDIRECT_PORT: u16 = 4554;
const HTTPS_PORT: u16 = 5545;
const HTTPS_CLIENT_AUTH_PORT: u16 = 5552;
const WS_PORT: u16 = 4242;
@@ -229,6 +232,29 @@ async fn auth_redirect(req: Request<Body>) -> hyper::Result<Response<Body>> {
Ok(resp)
}
+async fn basic_auth_redirect(
+ req: Request<Body>,
+) -> hyper::Result<Response<Body>> {
+ if let Some(auth) = req
+ .headers()
+ .get("authorization")
+ .map(|v| v.to_str().unwrap())
+ {
+ let credentials =
+ format!("{}:{}", TEST_BASIC_AUTH_USERNAME, TEST_BASIC_AUTH_PASSWORD);
+ if auth == format!("Basic {}", base64::encode(credentials)) {
+ let p = req.uri().path();
+ assert_eq!(&p[0..1], "/");
+ let url = format!("http://localhost:{}{}", PORT, p);
+ return Ok(redirect_resp(url));
+ }
+ }
+
+ let mut resp = Response::new(Body::empty());
+ *resp.status_mut() = StatusCode::NOT_FOUND;
+ Ok(resp)
+}
+
async fn run_ws_server(addr: &SocketAddr) {
let listener = TcpListener::bind(addr).await.unwrap();
println!("ready: ws"); // Eye catcher for HttpServerCount
@@ -837,6 +863,19 @@ async fn wrap_auth_redirect_server() {
}
}
+async fn wrap_basic_auth_redirect_server() {
+ let basic_auth_redirect_svc = make_service_fn(|_| async {
+ Ok::<_, Infallible>(service_fn(basic_auth_redirect))
+ });
+ let basic_auth_redirect_addr =
+ SocketAddr::from(([127, 0, 0, 1], BASIC_AUTH_REDIRECT_PORT));
+ let basic_auth_redirect_server =
+ Server::bind(&basic_auth_redirect_addr).serve(basic_auth_redirect_svc);
+ if let Err(e) = basic_auth_redirect_server.await {
+ eprintln!("Basic auth redirect error: {:?}", e);
+ }
+}
+
async fn wrap_abs_redirect_server() {
let abs_redirect_svc = make_service_fn(|_| async {
Ok::<_, Infallible>(service_fn(absolute_redirect))
@@ -969,6 +1008,7 @@ pub async fn run_all_servers() {
let inf_redirects_server_fut = wrap_inf_redirect_server();
let another_redirect_server_fut = wrap_another_redirect_server();
let auth_redirect_server_fut = wrap_auth_redirect_server();
+ let basic_auth_redirect_server_fut = wrap_basic_auth_redirect_server();
let abs_redirect_server_fut = wrap_abs_redirect_server();
let ws_addr = SocketAddr::from(([127, 0, 0, 1], WS_PORT));
@@ -992,6 +1032,7 @@ pub async fn run_all_servers() {
ws_close_server_fut,
another_redirect_server_fut,
auth_redirect_server_fut,
+ basic_auth_redirect_server_fut,
inf_redirects_server_fut,
double_redirects_server_fut,
abs_redirect_server_fut,