mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
fix: batch upload authentication (#21397)
This commit is contained in:
parent
334c118c97
commit
ffa09541d7
2 changed files with 90 additions and 65 deletions
|
@ -4,6 +4,7 @@ use std::fmt::Write;
|
|||
use std::io::IsTerminal;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
|
@ -227,21 +228,21 @@ async fn perform_publish(
|
|||
let client = http_client.client()?;
|
||||
let registry_url = crate::cache::DENO_REGISTRY_URL.to_string();
|
||||
|
||||
let authorization = match auth_method {
|
||||
let permissions = packages
|
||||
.iter()
|
||||
.map(|package| Permission::VersionPublish {
|
||||
scope: &package.scope,
|
||||
package: &package.package,
|
||||
version: &package.version,
|
||||
tarball_hash: &package.tarball_hash,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let authorizations = match auth_method {
|
||||
AuthMethod::Interactive => {
|
||||
let verifier = uuid::Uuid::new_v4().to_string();
|
||||
let challenge = BASE64_STANDARD.encode(sha2::Sha256::digest(&verifier));
|
||||
|
||||
let permissions = packages
|
||||
.iter()
|
||||
.map(|package| Permission::VersionPublish {
|
||||
scope: &package.scope,
|
||||
package: &package.package,
|
||||
version: &package.version,
|
||||
tarball_hash: &package.tarball_hash,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let response = client
|
||||
.post(format!("{}authorizations", registry_url))
|
||||
.json(&serde_json::json!({
|
||||
|
@ -290,7 +291,12 @@ async fn perform_publish(
|
|||
colors::gray("Authenticated as"),
|
||||
colors::cyan(res.user.name)
|
||||
);
|
||||
break format!("Bearer {}", res.token);
|
||||
let authorization: Rc<str> = format!("Bearer {}", res.token).into();
|
||||
let mut authorizations = Vec::new();
|
||||
for _ in &packages {
|
||||
authorizations.push(authorization.clone());
|
||||
}
|
||||
break authorizations;
|
||||
}
|
||||
Err(err) => {
|
||||
if err.code == "authorizationPending" {
|
||||
|
@ -302,54 +308,65 @@ async fn perform_publish(
|
|||
}
|
||||
}
|
||||
}
|
||||
AuthMethod::Token(token) => format!("Bearer {}", token),
|
||||
AuthMethod::Oidc(oidc_config) => {
|
||||
let permissions = packages
|
||||
.iter()
|
||||
.map(|package| Permission::VersionPublish {
|
||||
scope: &package.scope,
|
||||
package: &package.package,
|
||||
version: &package.version,
|
||||
tarball_hash: &package.tarball_hash,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let audience = json!({ "permissions": permissions }).to_string();
|
||||
|
||||
let url = format!(
|
||||
"{}&audience={}",
|
||||
oidc_config.url,
|
||||
percent_encoding::percent_encode(
|
||||
audience.as_bytes(),
|
||||
percent_encoding::NON_ALPHANUMERIC
|
||||
)
|
||||
);
|
||||
|
||||
let response = client
|
||||
.get(url)
|
||||
.bearer_auth(oidc_config.token)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to get OIDC token")?;
|
||||
let status = response.status();
|
||||
let text = response.text().await.with_context(|| {
|
||||
format!("Failed to get OIDC token: status {}", status)
|
||||
})?;
|
||||
if !status.is_success() {
|
||||
bail!(
|
||||
"Failed to get OIDC token: status {}, response: '{}'",
|
||||
status,
|
||||
text
|
||||
);
|
||||
AuthMethod::Token(token) => {
|
||||
let authorization: Rc<str> = format!("Bearer {}", token).into();
|
||||
let mut authorizations = Vec::new();
|
||||
for _ in &packages {
|
||||
authorizations.push(authorization.clone());
|
||||
}
|
||||
let OidcTokenResponse { value } = serde_json::from_str(&text)
|
||||
.with_context(|| {
|
||||
format!("Failed to parse OIDC token: '{}' (status {})", text, status)
|
||||
authorizations
|
||||
}
|
||||
AuthMethod::Oidc(oidc_config) => {
|
||||
let mut authorizations = Vec::new();
|
||||
for permissions in permissions.chunks(16) {
|
||||
let audience = json!({ "permissions": permissions }).to_string();
|
||||
let url = format!(
|
||||
"{}&audience={}",
|
||||
oidc_config.url,
|
||||
percent_encoding::percent_encode(
|
||||
audience.as_bytes(),
|
||||
percent_encoding::NON_ALPHANUMERIC
|
||||
)
|
||||
);
|
||||
|
||||
let response = client
|
||||
.get(url)
|
||||
.bearer_auth(&oidc_config.token)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to get OIDC token")?;
|
||||
let status = response.status();
|
||||
let text = response.text().await.with_context(|| {
|
||||
format!("Failed to get OIDC token: status {}", status)
|
||||
})?;
|
||||
format!("githuboidc {}", value)
|
||||
if !status.is_success() {
|
||||
bail!(
|
||||
"Failed to get OIDC token: status {}, response: '{}'",
|
||||
status,
|
||||
text
|
||||
);
|
||||
}
|
||||
let OidcTokenResponse { value } = serde_json::from_str(&text)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to parse OIDC token: '{}' (status {})",
|
||||
text, status
|
||||
)
|
||||
})?;
|
||||
|
||||
let authorization: Rc<str> = format!("githuboidc {}", value).into();
|
||||
for _ in &packages {
|
||||
authorizations.push(authorization.clone());
|
||||
}
|
||||
}
|
||||
authorizations
|
||||
}
|
||||
};
|
||||
|
||||
for package in packages {
|
||||
assert_eq!(packages.len(), authorizations.len());
|
||||
for (package, authorization) in
|
||||
packages.into_iter().zip(authorizations.into_iter())
|
||||
{
|
||||
println!(
|
||||
"{} @{}/{}@{} ...",
|
||||
colors::intense_blue("Publishing"),
|
||||
|
@ -365,7 +382,7 @@ async fn perform_publish(
|
|||
|
||||
let response = client
|
||||
.post(url)
|
||||
.header(AUTHORIZATION, &authorization)
|
||||
.header(AUTHORIZATION, &*authorization)
|
||||
.header(CONTENT_ENCODING, "gzip")
|
||||
.body(package.tarball)
|
||||
.send()
|
||||
|
|
|
@ -18,25 +18,33 @@ pub fn create_gzipped_tarball(
|
|||
unfurler: ImportMapUnfurler,
|
||||
) -> Result<Bytes, AnyError> {
|
||||
let mut tar = TarGzArchive::new();
|
||||
let dir_url = Url::from_directory_path(&dir).unwrap();
|
||||
let dir = dir
|
||||
.canonicalize()
|
||||
.map_err(|_| anyhow::anyhow!("Unable to canonicalize path {:?}", dir))?;
|
||||
|
||||
for entry in walkdir::WalkDir::new(dir).follow_links(false) {
|
||||
for entry in walkdir::WalkDir::new(&dir).follow_links(false) {
|
||||
let entry = entry?;
|
||||
|
||||
if entry.file_type().is_file() {
|
||||
let url = Url::from_file_path(entry.path())
|
||||
.map_err(|_| anyhow::anyhow!("Invalid file path {:?}", entry.path()))?;
|
||||
let relative_path = dir_url
|
||||
.make_relative(&url)
|
||||
.expect("children can be relative to parent");
|
||||
.map_err(|_| anyhow::anyhow!("Unable to convert path to url"))?;
|
||||
let relative_path = entry
|
||||
.path()
|
||||
.strip_prefix(&dir)
|
||||
.map_err(|err| anyhow::anyhow!("Unable to strip prefix: {err}"))?;
|
||||
let relative_path = relative_path.to_str().ok_or_else(|| {
|
||||
anyhow::anyhow!("Unable to convert path to string {:?}", relative_path)
|
||||
})?;
|
||||
let data = std::fs::read(entry.path())
|
||||
.with_context(|| format!("Unable to read file {:?}", entry.path()))?;
|
||||
let content = unfurler
|
||||
.unfurl(&url, data)
|
||||
.with_context(|| format!("Unable to unfurl file {:?}", entry.path()))?;
|
||||
tar.add_file(relative_path, &content).with_context(|| {
|
||||
format!("Unable to add file to tarball {:?}", entry.path())
|
||||
})?;
|
||||
tar
|
||||
.add_file(relative_path.to_string(), &content)
|
||||
.with_context(|| {
|
||||
format!("Unable to add file to tarball {:?}", entry.path())
|
||||
})?;
|
||||
} else if entry.file_type().is_dir() {
|
||||
// skip
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue