diff --git a/cli/npm/common.rs b/cli/npm/common.rs index 34835216c9..a3a828e745 100644 --- a/cli/npm/common.rs +++ b/cli/npm/common.rs @@ -1,25 +1,54 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use base64::prelude::BASE64_STANDARD; +use base64::Engine; +use deno_core::anyhow::bail; +use deno_core::error::AnyError; use deno_npm::npm_rc::RegistryConfig; use http::header; // TODO(bartlomieju): support more auth methods besides token and basic auth pub fn maybe_auth_header_for_npm_registry( registry_config: &RegistryConfig, -) -> Option<(header::HeaderName, header::HeaderValue)> { +) -> Result, AnyError> { if let Some(token) = registry_config.auth_token.as_ref() { - return Some(( + return Ok(Some(( header::AUTHORIZATION, header::HeaderValue::from_str(&format!("Bearer {}", token)).unwrap(), - )); + ))); } if let Some(auth) = registry_config.auth.as_ref() { - return Some(( + return Ok(Some(( header::AUTHORIZATION, header::HeaderValue::from_str(&format!("Basic {}", auth)).unwrap(), - )); + ))); } - None + let (username, password) = ( + registry_config.username.as_ref(), + registry_config.password.as_ref(), + ); + if (username.is_some() && password.is_none()) + || (username.is_none() && password.is_some()) + { + bail!("Both the username and password must be provided for basic auth") + } + + if username.is_some() && password.is_some() { + return Ok(Some(( + header::AUTHORIZATION, + header::HeaderValue::from_str(&format!( + "Basic {}", + BASE64_STANDARD.encode(&format!( + "{}:{}", + username.unwrap(), + password.unwrap() + )) + )) + .unwrap(), + ))); + } + + Ok(None) } diff --git a/cli/npm/managed/cache/registry_info.rs b/cli/npm/managed/cache/registry_info.rs index d7675a34fa..28b19373e9 100644 --- a/cli/npm/managed/cache/registry_info.rs +++ b/cli/npm/managed/cache/registry_info.rs @@ -192,7 +192,13 @@ impl RegistryInfoDownloader { let downloader = self.clone(); let package_url = self.get_package_url(name); let registry_config = self.npmrc.get_registry_config(name); - let maybe_auth_header = maybe_auth_header_for_npm_registry(registry_config); + let maybe_auth_header = + match maybe_auth_header_for_npm_registry(registry_config) { + Ok(maybe_auth_header) => maybe_auth_header, + Err(err) => { + return std::future::ready(Err(Arc::new(err))).boxed_local() + } + }; let guard = self.progress_bar.update(package_url.as_str()); let name = name.to_string(); async move { diff --git a/cli/npm/managed/cache/tarball.rs b/cli/npm/managed/cache/tarball.rs index eec890bed2..4bcee38ea8 100644 --- a/cli/npm/managed/cache/tarball.rs +++ b/cli/npm/managed/cache/tarball.rs @@ -167,7 +167,7 @@ impl TarballCache { let tarball_uri = Url::parse(&dist.tarball)?; let maybe_registry_config = tarball_cache.npmrc.tarball_config(&tarball_uri); - let maybe_auth_header = maybe_registry_config.and_then(|c| maybe_auth_header_for_npm_registry(c)); + let maybe_auth_header = maybe_registry_config.and_then(|c| maybe_auth_header_for_npm_registry(c).ok()?); let guard = tarball_cache.progress_bar.update(&dist.tarball); let result = tarball_cache.http_client_provider diff --git a/tests/specs/npm/npmrc_password_no_username/.npmrc b/tests/specs/npm/npmrc_password_no_username/.npmrc new file mode 100644 index 0000000000..e2d7b09e28 --- /dev/null +++ b/tests/specs/npm/npmrc_password_no_username/.npmrc @@ -0,0 +1,2 @@ +@denotest:registry=http://localhost:4261/ +//localhost:4261/:_password=land diff --git a/tests/specs/npm/npmrc_password_no_username/__test__.jsonc b/tests/specs/npm/npmrc_password_no_username/__test__.jsonc new file mode 100644 index 0000000000..a36537cb76 --- /dev/null +++ b/tests/specs/npm/npmrc_password_no_username/__test__.jsonc @@ -0,0 +1,11 @@ +{ + "envs": { + "DENO_FUTURE": "1" + }, + "tempDir": true, + "steps": [{ + "args": "install", + "output": "install.out", + "exitCode": 1 + }] +} diff --git a/tests/specs/npm/npmrc_password_no_username/install.out b/tests/specs/npm/npmrc_password_no_username/install.out new file mode 100644 index 0000000000..d49a2ba0d8 --- /dev/null +++ b/tests/specs/npm/npmrc_password_no_username/install.out @@ -0,0 +1,3 @@ +[UNORDERED_START] +error: Error getting response at http://localhost:4261/@denotest/basic for package "@denotest/basic": Both the username and password must be provided for basic auth +[UNORDERED_END] diff --git a/tests/specs/npm/npmrc_password_no_username/package.json b/tests/specs/npm/npmrc_password_no_username/package.json new file mode 100644 index 0000000000..f3757170a5 --- /dev/null +++ b/tests/specs/npm/npmrc_password_no_username/package.json @@ -0,0 +1,7 @@ +{ + "name": "npmrc_test", + "version": "0.0.1", + "dependencies": { + "@denotest/basic": "^=1.0.0" + } +} diff --git a/tests/specs/npm/npmrc_username_no_password/.npmrc b/tests/specs/npm/npmrc_username_no_password/.npmrc new file mode 100644 index 0000000000..e7f4e60644 --- /dev/null +++ b/tests/specs/npm/npmrc_username_no_password/.npmrc @@ -0,0 +1,2 @@ +@denotest:registry=http://localhost:4261/ +//localhost:4261/:username=deno \ No newline at end of file diff --git a/tests/specs/npm/npmrc_username_no_password/__test__.jsonc b/tests/specs/npm/npmrc_username_no_password/__test__.jsonc new file mode 100644 index 0000000000..a36537cb76 --- /dev/null +++ b/tests/specs/npm/npmrc_username_no_password/__test__.jsonc @@ -0,0 +1,11 @@ +{ + "envs": { + "DENO_FUTURE": "1" + }, + "tempDir": true, + "steps": [{ + "args": "install", + "output": "install.out", + "exitCode": 1 + }] +} diff --git a/tests/specs/npm/npmrc_username_no_password/install.out b/tests/specs/npm/npmrc_username_no_password/install.out new file mode 100644 index 0000000000..d49a2ba0d8 --- /dev/null +++ b/tests/specs/npm/npmrc_username_no_password/install.out @@ -0,0 +1,3 @@ +[UNORDERED_START] +error: Error getting response at http://localhost:4261/@denotest/basic for package "@denotest/basic": Both the username and password must be provided for basic auth +[UNORDERED_END] diff --git a/tests/specs/npm/npmrc_username_no_password/package.json b/tests/specs/npm/npmrc_username_no_password/package.json new file mode 100644 index 0000000000..f3757170a5 --- /dev/null +++ b/tests/specs/npm/npmrc_username_no_password/package.json @@ -0,0 +1,7 @@ +{ + "name": "npmrc_test", + "version": "0.0.1", + "dependencies": { + "@denotest/basic": "^=1.0.0" + } +} diff --git a/tests/specs/npm/npmrc_username_password/.npmrc b/tests/specs/npm/npmrc_username_password/.npmrc new file mode 100644 index 0000000000..c318678aea --- /dev/null +++ b/tests/specs/npm/npmrc_username_password/.npmrc @@ -0,0 +1,6 @@ +@denotest:registry=http://localhost:4261/ +//localhost:4261/:username=deno +//localhost:4261/:_password=land +@denotest2:registry=http://localhost:4262/ +//localhost:4262/:username=deno +//localhost:4262/:_password=land2 diff --git a/tests/specs/npm/npmrc_username_password/__test__.jsonc b/tests/specs/npm/npmrc_username_password/__test__.jsonc new file mode 100644 index 0000000000..44298ed2f5 --- /dev/null +++ b/tests/specs/npm/npmrc_username_password/__test__.jsonc @@ -0,0 +1,13 @@ +{ + "envs": { + "DENO_FUTURE": "1" + }, + "tempDir": true, + "steps": [{ + "args": "install", + "output": "install.out" + }, { + "args": "run -A main.js", + "output": "main.out" + }] +} diff --git a/tests/specs/npm/npmrc_username_password/install.out b/tests/specs/npm/npmrc_username_password/install.out new file mode 100644 index 0000000000..30643527e6 --- /dev/null +++ b/tests/specs/npm/npmrc_username_password/install.out @@ -0,0 +1,8 @@ +[UNORDERED_START] +Download http://localhost:4261/@denotest/basic +Download http://localhost:4262/@denotest2/basic +Download http://localhost:4261/@denotest/basic/1.0.0.tgz +Download http://localhost:4262/@denotest2/basic/1.0.0.tgz +Initialize @denotest2/basic@1.0.0 +Initialize @denotest/basic@1.0.0 +[UNORDERED_END] diff --git a/tests/specs/npm/npmrc_username_password/main.js b/tests/specs/npm/npmrc_username_password/main.js new file mode 100644 index 0000000000..66b3936360 --- /dev/null +++ b/tests/specs/npm/npmrc_username_password/main.js @@ -0,0 +1,8 @@ +import { getValue, setValue } from "@denotest/basic"; +import * as test from "@denotest2/basic"; + +console.log(getValue()); +setValue(42); +console.log(getValue()); + +console.log(test.getValue()); diff --git a/tests/specs/npm/npmrc_username_password/main.out b/tests/specs/npm/npmrc_username_password/main.out new file mode 100644 index 0000000000..bbe210bdbc --- /dev/null +++ b/tests/specs/npm/npmrc_username_password/main.out @@ -0,0 +1,3 @@ +0 +42 +0 diff --git a/tests/specs/npm/npmrc_username_password/package.json b/tests/specs/npm/npmrc_username_password/package.json new file mode 100644 index 0000000000..13e305931e --- /dev/null +++ b/tests/specs/npm/npmrc_username_password/package.json @@ -0,0 +1,8 @@ +{ + "name": "npmrc_test", + "version": "0.0.1", + "dependencies": { + "@denotest/basic": "^=1.0.0", + "@denotest2/basic": "1.0.0" + } +}