Skip to content

Commit

Permalink
fix(auth): glue login and authentication stuff, refactor, and make it…
Browse files Browse the repository at this point in the history
… all work
  • Loading branch information
zkat committed Sep 28, 2023
1 parent 0513019 commit 23cf1bc
Show file tree
Hide file tree
Showing 35 changed files with 602 additions and 362 deletions.
8 changes: 6 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Expand Up @@ -74,6 +74,7 @@ homepage = "https://orogene.dev"
rust-version = "1.67.1"

[workspace.dependencies]
anyhow = "1.0.75"
async-compression = "0.3.5"
async-process = "1.0.1"
async-std = "1.12.0"
Expand Down Expand Up @@ -125,7 +126,7 @@ rand = "0.8.5"
reflink-copy = "0.1.5"
regex = "1.7.2"
reqwest = "0.11.14"
reqwest-middleware = "0.2.0"
reqwest-middleware = "=0.2.2"
resvg = "0.29.0"
rkyv = "0.7.41"
sentry = "0.31.0"
Expand All @@ -136,6 +137,7 @@ ssri = "9.0.0"
supports-unicode = "2.0.0"
syn = "1.0.33"
tar = "0.4.38"
task-local-extensions = "0.1.4"
tempfile = "3.3.0"
term_grid = "0.1.7"
term_size = "0.3.2"
Expand Down
115 changes: 59 additions & 56 deletions crates/nassun/src/client.rs
Expand Up @@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::path::{Path, PathBuf};

use async_std::sync::Arc;
use oro_client::OroClient;
use oro_client::{OroClient, OroClientBuilder};
use oro_common::{CorgiManifest, CorgiPackument, CorgiVersionMetadata, Packument, VersionMetadata};
use url::Url;

Expand All @@ -20,38 +20,41 @@ use crate::resolver::{PackageResolution, PackageResolver};
use crate::tarball::Tarball;

/// Build a new Nassun instance with specified options.
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[derive(Clone, Debug, Default)]
pub struct NassunOpts {
client_builder: OroClientBuilder,
client: Option<OroClient>,
#[cfg(not(target_arch = "wasm32"))]
cache: Option<PathBuf>,
base_dir: Option<PathBuf>,
default_tag: Option<String>,
registries: HashMap<Option<String>, Url>,
credentials: Vec<(String, String, String)>,
memoize_metadata: bool,
#[cfg(not(target_arch = "wasm32"))]
proxy: bool,
#[cfg(not(target_arch = "wasm32"))]
proxy_url: Option<String>,
#[cfg(not(target_arch = "wasm32"))]
no_proxy_domain: Option<String>,
#[cfg(not(target_arch = "wasm32"))]
fetch_retries: u32,
}

impl NassunOpts {
pub fn new() -> Self {
Default::default()
}

/// A preconfigured [`OroClient`] to use for requests. Providing this will
/// override all other client-related options.
pub fn client(mut self, client: OroClient) -> Self {
self.client = Some(client);
self
}

/// Cache directory to use for requests.
#[cfg(not(target_arch = "wasm32"))]
pub fn cache(mut self, cache: impl AsRef<Path>) -> Self {
self.cache = Some(PathBuf::from(cache.as_ref()));
self.client_builder = self.client_builder.cache(cache.as_ref());
self
}

/// Sets the default registry for requests.
pub fn registry(mut self, registry: Url) -> Self {
self.client_builder = self.client_builder.registry(registry.clone());
self.registries.insert(None, registry);
self
}
Expand All @@ -66,12 +69,34 @@ impl NassunOpts {
self
}

/// Set the credential-config for this instance. The config has the form (registry,key,value)
/// where registry is the host of the registry, key is the key of a single entry in the respective
/// config JSON object and value is the corresponding value. A single registry may have multiple entries
/// in this vector (e.g. "username" & "password"). The format will be handled by by OroClient.
pub fn credentials(mut self, credentials: Vec<(String, String, String)>) -> Self {
self.credentials = credentials;
/// Sets basic auth credentials for a registry.
pub fn basic_auth(
mut self,
registry: Url,
username: impl AsRef<str>,
password: Option<impl AsRef<str>>,
) -> Self {
let username = username.as_ref();
let password = password.map(|p| p.as_ref().to_string());
self.client_builder =
self.client_builder
.basic_auth(registry, username.to_string(), password);
self
}

/// Sets bearer token credentials for a registry.
pub fn token_auth(mut self, registry: Url, token: impl AsRef<str>) -> Self {
self.client_builder = self
.client_builder
.token_auth(registry, token.as_ref().to_string());
self
}

/// Sets the legacy, pre-encoded auth token for a registry.
pub fn legacy_auth(mut self, registry: Url, legacy_auth_token: impl AsRef<str>) -> Self {
self.client_builder = self
.client_builder
.legacy_auth(registry, legacy_auth_token.as_ref().to_string());
self
}

Expand All @@ -96,67 +121,45 @@ impl NassunOpts {
self
}

#[cfg(not(target_arch = "wasm32"))]
pub fn proxy(mut self, proxy: bool) -> Self {
self.proxy = proxy;
/// Number of times to retry failed requests.
pub fn retries(mut self, retries: u32) -> Self {
self.client_builder = self.client_builder.retries(retries);
self
}

/// Whether to use a proxy for requests.
#[cfg(not(target_arch = "wasm32"))]
pub fn proxy_url(mut self, proxy_url: impl AsRef<str>) -> Self {
self.proxy_url = Some(proxy_url.as_ref().into());
pub fn proxy(mut self, proxy: bool) -> Self {
self.client_builder = self.client_builder.proxy(proxy);
self
}

/// Proxy URL to use for requests. If `no_proxy_domain` is needed, it must
/// be called before this method.
#[cfg(not(target_arch = "wasm32"))]
pub fn no_proxy_domain(mut self, no_proxy_domain: impl AsRef<str>) -> Self {
self.no_proxy_domain = Some(no_proxy_domain.as_ref().into());
self
pub fn proxy_url(mut self, proxy_url: impl AsRef<str>) -> Result<Self> {
self.client_builder = self.client_builder.proxy_url(proxy_url.as_ref())?;
Ok(self)
}

/// Sets the NO_PROXY domain.
#[cfg(not(target_arch = "wasm32"))]
pub fn fetch_retries(mut self, fetch_retries: u32) -> Self {
self.fetch_retries = fetch_retries;
pub fn no_proxy_domain(mut self, no_proxy_domain: impl AsRef<str>) -> Self {
self.client_builder = self
.client_builder
.no_proxy_domain(no_proxy_domain.as_ref());
self
}

/// Build a new Nassun instance from this options object.
pub fn build(self) -> Nassun {
let registry = self
.registries
.get(&None)
.cloned()
.unwrap_or_else(|| "https://registry.npmjs.org/".parse().unwrap());
#[cfg(target_arch = "wasm32")]
let client_builder = OroClient::builder().registry(registry);
#[cfg(not(target_arch = "wasm32"))]
let mut client_builder = OroClient::builder()
.registry(registry)
.fetch_retries(self.fetch_retries)
.proxy(self.proxy);
#[cfg(not(target_arch = "wasm32"))]
if let Some(domain) = self.no_proxy_domain {
client_builder = client_builder.no_proxy_domain(domain);
}
#[cfg(not(target_arch = "wasm32"))]
if let Some(proxy) = self.proxy_url {
if let Ok(builder) = client_builder.clone().proxy_url(&proxy) {
client_builder = builder;
} else {
tracing::warn!("Failed to parse proxy URL: {}", proxy)
}
}
#[cfg(not(target_arch = "wasm32"))]
let cache = if let Some(cache) = self.cache {
client_builder = client_builder.cache(cache.clone());
Arc::new(Some(cache))
} else {
Arc::new(None)
};
client_builder = client_builder
.credentials(self.credentials)
.expect("failed to set credential list");
let client = client_builder.build();
let client = self.client.unwrap_or_else(|| self.client_builder.build());
Nassun {
#[cfg(not(target_arch = "wasm32"))]
cache,
Expand Down
45 changes: 37 additions & 8 deletions crates/node-maintainer/src/maintainer.rs
Expand Up @@ -35,6 +35,7 @@ pub type ScriptLineHandler = Arc<dyn Fn(&str) + Send + Sync>;
#[derive(Clone)]
pub struct NodeMaintainerOptions {
nassun_opts: NassunOpts,
nassun: Option<Nassun>,
concurrency: usize,
locked: bool,
kdl_lock: Option<Lockfile>,
Expand Down Expand Up @@ -139,10 +140,30 @@ impl NodeMaintainerOptions {
self
}

/// Credentials map used for all registries
/// This will be resolved into a proper credentials map inside nassun
pub fn credentials(mut self, credentials: Vec<(String, String, String)>) -> Self {
self.nassun_opts = self.nassun_opts.credentials(credentials);
/// Sets basic auth credentials for a registry.
pub fn basic_auth(
mut self,
registry: Url,
username: impl AsRef<str>,
password: Option<impl AsRef<str>>,
) -> Self {
let username = username.as_ref();
let password = password.map(|p| p.as_ref().to_string());
self.nassun_opts = self.nassun_opts.basic_auth(registry, username, password);
self
}

/// Sets bearer token credentials for a registry.
pub fn token_auth(mut self, registry: Url, token: impl AsRef<str>) -> Self {
self.nassun_opts = self.nassun_opts.token_auth(registry, token.as_ref());
self
}

/// Sets the legacy, pre-encoded auth token for a registry.
pub fn legacy_auth(mut self, registry: Url, legacy_auth_token: impl AsRef<str>) -> Self {
self.nassun_opts = self
.nassun_opts
.legacy_auth(registry, legacy_auth_token.as_ref());
self
}

Expand All @@ -160,6 +181,13 @@ impl NodeMaintainerOptions {
self
}

/// Provide a pre-configured Nassun instance. Using this option will
/// disable all other nassun-related configurations.
pub fn nassun(mut self, nassun: Nassun) -> Self {
self.nassun = Some(nassun);
self
}

/// When extracting packages, prefer to copy files instead of linking
/// them.
///
Expand Down Expand Up @@ -189,9 +217,9 @@ impl NodeMaintainerOptions {
}

#[cfg(not(target_arch = "wasm32"))]
pub fn proxy_url(mut self, proxy_url: impl AsRef<str>) -> Self {
self.nassun_opts = self.nassun_opts.proxy_url(proxy_url.as_ref());
self
pub fn proxy_url(mut self, proxy_url: impl AsRef<str>) -> Result<Self, NodeMaintainerError> {
self.nassun_opts = self.nassun_opts.proxy_url(proxy_url.as_ref())?;
Ok(self)
}

#[cfg(not(target_arch = "wasm32"))]
Expand Down Expand Up @@ -306,7 +334,7 @@ impl NodeMaintainerOptions {
root: CorgiManifest,
) -> Result<NodeMaintainer, NodeMaintainerError> {
let lockfile = self.get_lockfile().await?;
let nassun = self.nassun_opts.build();
let nassun = self.nassun.unwrap_or_else(|| self.nassun_opts.build());
let root_pkg = Nassun::dummy_from_manifest(root.clone());
let proj_root = self.root.unwrap_or_else(|| PathBuf::from("."));
let mut resolver = Resolver {
Expand Down Expand Up @@ -415,6 +443,7 @@ impl Default for NodeMaintainerOptions {
fn default() -> Self {
NodeMaintainerOptions {
nassun_opts: Default::default(),
nassun: None,
concurrency: DEFAULT_CONCURRENCY,
kdl_lock: None,
npm_lock: None,
Expand Down
3 changes: 3 additions & 0 deletions crates/oro-client/Cargo.toml
Expand Up @@ -14,6 +14,8 @@ rust-version.workspace = true
[dependencies]
oro-common = { version = "=0.3.29", path = "../oro-common" }

anyhow = { workspace = true }
async-trait = { workspace = true }
chrono = { workspace = true }
base64 = { workspace = true }
futures = { workspace = true, features = ["io-compat"] }
Expand All @@ -25,6 +27,7 @@ reqwest-middleware = { workspace = true }
reqwest-retry = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
task-local-extensions = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
url = { workspace = true }
Expand Down
7 changes: 6 additions & 1 deletion crates/oro-client/src/api/login.rs
Expand Up @@ -41,6 +41,7 @@ pub struct LoginWeb {
#[derive(Debug, Clone, Default)]
pub struct LoginOptions {
pub scope: Option<String>,
pub client: Option<OroClient>,
}

#[derive(Deserialize, Serialize)]
Expand Down Expand Up @@ -90,6 +91,7 @@ impl OroClient {
.client
.post(url.clone())
.headers(headers)
.header("X-Oro-Registry", self.registry.to_string())
.send()
.await?
.notify()
Expand Down Expand Up @@ -124,6 +126,7 @@ impl OroClient {
let response = self
.client
.put(url.clone())
.header("X-Oro-Registry", self.registry.to_string())
.headers(headers)
.body(
serde_json::to_string(&LoginCouch {
Expand Down Expand Up @@ -197,6 +200,7 @@ impl OroClient {
let response = self
.client_uncached
.get(done_url.as_ref())
.header("X-Oro-Registry", self.registry.to_string())
.headers(headers)
.send()
.await?
Expand Down Expand Up @@ -296,7 +300,8 @@ mod test {
"password",
None,
&LoginOptions {
scope: Some("@mycompany".to_owned())
scope: Some("@mycompany".to_owned()),
client: None,
}
)
.await?,
Expand Down

0 comments on commit 23cf1bc

Please sign in to comment.