mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -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::io::IsTerminal;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base64::prelude::BASE64_STANDARD;
|
use base64::prelude::BASE64_STANDARD;
|
||||||
|
@ -227,21 +228,21 @@ async fn perform_publish(
|
||||||
let client = http_client.client()?;
|
let client = http_client.client()?;
|
||||||
let registry_url = crate::cache::DENO_REGISTRY_URL.to_string();
|
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 => {
|
AuthMethod::Interactive => {
|
||||||
let verifier = uuid::Uuid::new_v4().to_string();
|
let verifier = uuid::Uuid::new_v4().to_string();
|
||||||
let challenge = BASE64_STANDARD.encode(sha2::Sha256::digest(&verifier));
|
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
|
let response = client
|
||||||
.post(format!("{}authorizations", registry_url))
|
.post(format!("{}authorizations", registry_url))
|
||||||
.json(&serde_json::json!({
|
.json(&serde_json::json!({
|
||||||
|
@ -290,7 +291,12 @@ async fn perform_publish(
|
||||||
colors::gray("Authenticated as"),
|
colors::gray("Authenticated as"),
|
||||||
colors::cyan(res.user.name)
|
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) => {
|
Err(err) => {
|
||||||
if err.code == "authorizationPending" {
|
if err.code == "authorizationPending" {
|
||||||
|
@ -302,54 +308,65 @@ async fn perform_publish(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AuthMethod::Token(token) => format!("Bearer {}", token),
|
AuthMethod::Token(token) => {
|
||||||
AuthMethod::Oidc(oidc_config) => {
|
let authorization: Rc<str> = format!("Bearer {}", token).into();
|
||||||
let permissions = packages
|
let mut authorizations = Vec::new();
|
||||||
.iter()
|
for _ in &packages {
|
||||||
.map(|package| Permission::VersionPublish {
|
authorizations.push(authorization.clone());
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
let OidcTokenResponse { value } = serde_json::from_str(&text)
|
authorizations
|
||||||
.with_context(|| {
|
}
|
||||||
format!("Failed to parse OIDC token: '{}' (status {})", text, status)
|
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!(
|
println!(
|
||||||
"{} @{}/{}@{} ...",
|
"{} @{}/{}@{} ...",
|
||||||
colors::intense_blue("Publishing"),
|
colors::intense_blue("Publishing"),
|
||||||
|
@ -365,7 +382,7 @@ async fn perform_publish(
|
||||||
|
|
||||||
let response = client
|
let response = client
|
||||||
.post(url)
|
.post(url)
|
||||||
.header(AUTHORIZATION, &authorization)
|
.header(AUTHORIZATION, &*authorization)
|
||||||
.header(CONTENT_ENCODING, "gzip")
|
.header(CONTENT_ENCODING, "gzip")
|
||||||
.body(package.tarball)
|
.body(package.tarball)
|
||||||
.send()
|
.send()
|
||||||
|
|
|
@ -18,25 +18,33 @@ pub fn create_gzipped_tarball(
|
||||||
unfurler: ImportMapUnfurler,
|
unfurler: ImportMapUnfurler,
|
||||||
) -> Result<Bytes, AnyError> {
|
) -> Result<Bytes, AnyError> {
|
||||||
let mut tar = TarGzArchive::new();
|
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?;
|
let entry = entry?;
|
||||||
|
|
||||||
if entry.file_type().is_file() {
|
if entry.file_type().is_file() {
|
||||||
let url = Url::from_file_path(entry.path())
|
let url = Url::from_file_path(entry.path())
|
||||||
.map_err(|_| anyhow::anyhow!("Invalid file path {:?}", entry.path()))?;
|
.map_err(|_| anyhow::anyhow!("Unable to convert path to url"))?;
|
||||||
let relative_path = dir_url
|
let relative_path = entry
|
||||||
.make_relative(&url)
|
.path()
|
||||||
.expect("children can be relative to parent");
|
.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())
|
let data = std::fs::read(entry.path())
|
||||||
.with_context(|| format!("Unable to read file {:?}", entry.path()))?;
|
.with_context(|| format!("Unable to read file {:?}", entry.path()))?;
|
||||||
let content = unfurler
|
let content = unfurler
|
||||||
.unfurl(&url, data)
|
.unfurl(&url, data)
|
||||||
.with_context(|| format!("Unable to unfurl file {:?}", entry.path()))?;
|
.with_context(|| format!("Unable to unfurl file {:?}", entry.path()))?;
|
||||||
tar.add_file(relative_path, &content).with_context(|| {
|
tar
|
||||||
format!("Unable to add file to tarball {:?}", entry.path())
|
.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() {
|
} else if entry.file_type().is_dir() {
|
||||||
// skip
|
// skip
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Reference in a new issue