0
0
Fork 0
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:
Luca Casonato 2023-11-30 19:54:54 +01:00 committed by GitHub
parent 334c118c97
commit ffa09541d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 90 additions and 65 deletions

View file

@ -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()

View file

@ -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 {