mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
feat(cli): Support Basic authentication in DENO_AUTH_TOKENS (#11910)
This commit is contained in:
parent
bf6dbf9855
commit
08e12380a0
5 changed files with 142 additions and 4 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3838,6 +3838,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
"base64 0.13.0",
|
||||
"bytes",
|
||||
"futures",
|
||||
"hyper",
|
||||
|
|
|
@ -5,15 +5,27 @@ use log::debug;
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue