mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-28 11:20:36 +00:00
end-to-end tests
This commit is contained in:
parent
6d1c07bcc5
commit
2ac15086fb
@ -15,10 +15,13 @@ crates-io = { path = "../crates-io" }
|
||||
snapbox = { version = "0.4.0", features = ["diff", "path"] }
|
||||
filetime = "0.2"
|
||||
flate2 = { version = "1.0", default-features = false, features = ["zlib"] }
|
||||
pasetors = { version = "0.6.4", features = ["v3", "paserk", "std", "serde"] }
|
||||
time = { version = "0.3", features = ["parsing", "formatting"]}
|
||||
git2 = "0.15.0"
|
||||
glob = "0.3"
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.0"
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tar = { version = "0.4.38", default-features = false }
|
||||
termcolor = "1.1.2"
|
||||
|
@ -5,6 +5,9 @@ use cargo_util::paths::append;
|
||||
use cargo_util::Sha256;
|
||||
use flate2::write::GzEncoder;
|
||||
use flate2::Compression;
|
||||
use pasetors::keys::{AsymmetricPublicKey, AsymmetricSecretKey};
|
||||
use pasetors::paserk::FormatAsPaserk;
|
||||
use pasetors::token::UntrustedToken;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt;
|
||||
use std::fs::{self, File};
|
||||
@ -13,6 +16,8 @@ use std::net::{SocketAddr, TcpListener, TcpStream};
|
||||
use std::path::PathBuf;
|
||||
use std::thread::{self, JoinHandle};
|
||||
use tar::{Builder, Header};
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
use time::{Duration, OffsetDateTime};
|
||||
use url::Url;
|
||||
|
||||
/// Gets the path to the local index pretending to be crates.io. This is a Git repo
|
||||
@ -55,12 +60,30 @@ fn generate_url(name: &str) -> Url {
|
||||
Url::from_file_path(generate_path(name)).ok().unwrap()
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Token {
|
||||
Plaintext(String),
|
||||
Keys(String, Option<String>),
|
||||
}
|
||||
|
||||
impl Token {
|
||||
/// This is a valid PASETO secret key.
|
||||
/// This one is already publicly available as part of the text of the RFC so is safe to use for tests.
|
||||
pub fn rfc_key() -> Token {
|
||||
Token::Keys(
|
||||
"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36"
|
||||
.to_string(),
|
||||
Some("sub".to_string()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for initializing registries.
|
||||
pub struct RegistryBuilder {
|
||||
/// If set, configures an alternate registry with the given name.
|
||||
alternative: Option<String>,
|
||||
/// If set, the authorization token for the registry.
|
||||
token: Option<String>,
|
||||
/// The authorization token for the registry.
|
||||
token: Option<Token>,
|
||||
/// If set, the registry requires authorization for all operations.
|
||||
auth_required: bool,
|
||||
/// If set, serves the index over http.
|
||||
@ -83,7 +106,7 @@ pub struct TestRegistry {
|
||||
path: PathBuf,
|
||||
api_url: Url,
|
||||
dl_url: Url,
|
||||
token: Option<String>,
|
||||
token: Token,
|
||||
}
|
||||
|
||||
impl TestRegistry {
|
||||
@ -96,9 +119,17 @@ impl TestRegistry {
|
||||
}
|
||||
|
||||
pub fn token(&self) -> &str {
|
||||
self.token
|
||||
.as_deref()
|
||||
.expect("registry was not configured with a token")
|
||||
match &self.token {
|
||||
Token::Plaintext(s) => s,
|
||||
Token::Keys(_, _) => panic!("registry was not configured with a plaintext token"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key(&self) -> &str {
|
||||
match &self.token {
|
||||
Token::Plaintext(_) => panic!("registry was not configured with a secret key"),
|
||||
Token::Keys(s, _) => s,
|
||||
}
|
||||
}
|
||||
|
||||
/// Shutdown the server thread and wait for it to stop.
|
||||
@ -169,8 +200,8 @@ impl RegistryBuilder {
|
||||
|
||||
/// Sets the token value
|
||||
#[must_use]
|
||||
pub fn token(mut self, token: &str) -> Self {
|
||||
self.token = Some(token.to_string());
|
||||
pub fn token(mut self, token: Token) -> Self {
|
||||
self.token = Some(token);
|
||||
self
|
||||
}
|
||||
|
||||
@ -219,7 +250,9 @@ impl RegistryBuilder {
|
||||
let dl_url = generate_url(&format!("{prefix}dl"));
|
||||
let dl_path = generate_path(&format!("{prefix}dl"));
|
||||
let api_path = generate_path(&format!("{prefix}api"));
|
||||
let token = Some(self.token.unwrap_or_else(|| format!("{prefix}sekrit")));
|
||||
let token = self
|
||||
.token
|
||||
.unwrap_or_else(|| Token::Plaintext(format!("{prefix}sekrit")));
|
||||
|
||||
let (server, index_url, api_url, dl_url) = if !self.http_index && !self.http_api {
|
||||
// No need to start the HTTP server.
|
||||
@ -287,32 +320,48 @@ impl RegistryBuilder {
|
||||
}
|
||||
|
||||
if self.configure_token {
|
||||
let token = registry.token.as_deref().unwrap();
|
||||
let credentials = paths::home().join(".cargo/credentials");
|
||||
if let Some(alternative) = &self.alternative {
|
||||
append(
|
||||
&credentials,
|
||||
format!(
|
||||
r#"
|
||||
match ®istry.token {
|
||||
Token::Plaintext(token) => {
|
||||
if let Some(alternative) = &self.alternative {
|
||||
append(
|
||||
&credentials,
|
||||
format!(
|
||||
r#"
|
||||
[registries.{alternative}]
|
||||
token = "{token}"
|
||||
"#
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
append(
|
||||
&credentials,
|
||||
format!(
|
||||
r#"
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
append(
|
||||
&credentials,
|
||||
format!(
|
||||
r#"
|
||||
[registry]
|
||||
token = "{token}"
|
||||
"#
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Token::Keys(key, subject) => {
|
||||
let mut out = if let Some(alternative) = &self.alternative {
|
||||
format!("\n[registries.{alternative}]\n")
|
||||
} else {
|
||||
format!("\n[registry]\n")
|
||||
};
|
||||
out += &format!("secret-key = \"{key}\"\n");
|
||||
if let Some(subject) = subject {
|
||||
out += &format!("secret-key-subject = \"{subject}\"\n");
|
||||
}
|
||||
|
||||
append(&credentials, out.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -536,16 +585,24 @@ pub struct HttpServer {
|
||||
listener: TcpListener,
|
||||
registry_path: PathBuf,
|
||||
dl_path: PathBuf,
|
||||
token: Option<String>,
|
||||
addr: SocketAddr,
|
||||
token: Token,
|
||||
auth_required: bool,
|
||||
custom_responders: HashMap<&'static str, Box<dyn Send + Fn(&Request, &HttpServer) -> Response>>,
|
||||
}
|
||||
|
||||
pub struct Mutation<'a> {
|
||||
pub mutation: &'a str,
|
||||
pub name: Option<&'a str>,
|
||||
pub vers: Option<&'a str>,
|
||||
pub cksum: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl HttpServer {
|
||||
pub fn new(
|
||||
registry_path: PathBuf,
|
||||
dl_path: PathBuf,
|
||||
token: Option<String>,
|
||||
token: Token,
|
||||
auth_required: bool,
|
||||
api_responders: HashMap<
|
||||
&'static str,
|
||||
@ -558,6 +615,7 @@ impl HttpServer {
|
||||
listener,
|
||||
registry_path,
|
||||
dl_path,
|
||||
addr,
|
||||
token,
|
||||
auth_required,
|
||||
custom_responders: api_responders,
|
||||
@ -648,17 +706,135 @@ impl HttpServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Route the request
|
||||
fn route(&self, req: &Request) -> Response {
|
||||
let authorized = |mutatation: bool| {
|
||||
if mutatation || self.auth_required {
|
||||
self.token == req.authorization
|
||||
} else {
|
||||
assert!(req.authorization.is_none(), "unexpected token");
|
||||
true
|
||||
fn check_authorized(&self, req: &Request, mutation: Option<Mutation>) -> bool {
|
||||
let (private_key, private_key_subject) = if mutation.is_some() || self.auth_required {
|
||||
match &self.token {
|
||||
Token::Plaintext(token) => return Some(token) == req.authorization.as_ref(),
|
||||
Token::Keys(private_key, private_key_subject) => {
|
||||
(private_key.as_str(), private_key_subject)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert!(req.authorization.is_none(), "unexpected token");
|
||||
return true;
|
||||
};
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Some(e) => e,
|
||||
None => return false,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let secret: AsymmetricSecretKey<pasetors::version3::V3> = private_key.try_into().unwrap();
|
||||
let public: AsymmetricPublicKey<pasetors::version3::V3> = (&secret).try_into().unwrap();
|
||||
let pub_key_id: pasetors::paserk::Id = (&public).into();
|
||||
let mut paserk_pub_key_id = String::new();
|
||||
FormatAsPaserk::fmt(&pub_key_id, &mut paserk_pub_key_id).unwrap();
|
||||
// https://github.com/rust-lang/rfcs/blob/master/text/3231-cargo-asymmetric-tokens.md#how-the-registry-server-will-validate-an-asymmetric-token
|
||||
|
||||
// - The PASETO is in v3.public format.
|
||||
let authorization = t!(&req.authorization);
|
||||
let untrusted_token = t!(
|
||||
UntrustedToken::<pasetors::Public, pasetors::version3::V3>::try_from(authorization)
|
||||
.ok()
|
||||
);
|
||||
|
||||
// - The PASETO validates using the public key it looked up based on the key ID.
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct Footer<'a> {
|
||||
url: &'a str,
|
||||
kip: &'a str,
|
||||
}
|
||||
let footer: Footer = t!(serde_json::from_slice(untrusted_token.untrusted_footer()).ok());
|
||||
if footer.kip != paserk_pub_key_id {
|
||||
return false;
|
||||
}
|
||||
let trusted_token =
|
||||
t!(
|
||||
pasetors::version3::PublicToken::verify(&public, &untrusted_token, None, None,)
|
||||
.ok()
|
||||
);
|
||||
|
||||
// - The URL matches the registry base URL
|
||||
if footer.url != "https://github.com/rust-lang/crates.io-index"
|
||||
&& footer.url != &format!("sparse+http://{}/index/", self.addr.to_string())
|
||||
{
|
||||
dbg!(footer.url);
|
||||
return false;
|
||||
}
|
||||
|
||||
// - The PASETO is still within its valid time period.
|
||||
#[derive(serde::Deserialize)]
|
||||
struct Message<'a> {
|
||||
iat: &'a str,
|
||||
sub: Option<&'a str>,
|
||||
mutation: Option<&'a str>,
|
||||
name: Option<&'a str>,
|
||||
vers: Option<&'a str>,
|
||||
cksum: Option<&'a str>,
|
||||
_challenge: Option<&'a str>, // todo: PASETO with challenges
|
||||
v: Option<u8>,
|
||||
}
|
||||
let message: Message = t!(serde_json::from_str(trusted_token.payload()).ok());
|
||||
let token_time = t!(OffsetDateTime::parse(message.iat, &Rfc3339).ok());
|
||||
let now = OffsetDateTime::now_utc();
|
||||
if (now - token_time) > Duration::MINUTE {
|
||||
return false;
|
||||
}
|
||||
if private_key_subject.as_deref() != message.sub {
|
||||
dbg!(message.sub);
|
||||
return false;
|
||||
}
|
||||
// - If the claim v is set, that it has the value of 1.
|
||||
if let Some(v) = message.v {
|
||||
if v != 1 {
|
||||
dbg!(message.v);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// - If the server issues challenges, that the challenge has not yet been answered.
|
||||
// todo: PASETO with challenges
|
||||
// - If the operation is a mutation:
|
||||
if let Some(mutation) = mutation {
|
||||
// - That the operation matches the mutation field an is one of publish, yank, or unyank.
|
||||
if message.mutation != Some(mutation.mutation) {
|
||||
dbg!(message.mutation);
|
||||
return false;
|
||||
}
|
||||
// - That the package, and version match the request.
|
||||
if message.name != mutation.name {
|
||||
dbg!(message.name);
|
||||
return false;
|
||||
}
|
||||
if message.vers != mutation.vers {
|
||||
dbg!(message.vers);
|
||||
return false;
|
||||
}
|
||||
// - If the mutation is publish, that the version has not already been published, and that the hash matches the request.
|
||||
if mutation.mutation == "publish" {
|
||||
if message.cksum != mutation.cksum {
|
||||
dbg!(message.cksum);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// - If the operation is a read, that the mutation field is not set.
|
||||
if message.mutation.is_some()
|
||||
|| message.name.is_some()
|
||||
|| message.vers.is_some()
|
||||
|| message.cksum.is_some()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Route the request
|
||||
fn route(&self, req: &Request) -> Response {
|
||||
// Check for custom responder
|
||||
if let Some(responder) = self.custom_responders.get(req.url.path()) {
|
||||
return responder(&req, self);
|
||||
@ -666,39 +842,53 @@ impl HttpServer {
|
||||
let path: Vec<_> = req.url.path()[1..].split('/').collect();
|
||||
match (req.method.as_str(), path.as_slice()) {
|
||||
("get", ["index", ..]) => {
|
||||
if !authorized(false) {
|
||||
if !self.check_authorized(req, None) {
|
||||
self.unauthorized(req)
|
||||
} else {
|
||||
self.index(&req)
|
||||
}
|
||||
}
|
||||
("get", ["dl", ..]) => {
|
||||
if !authorized(false) {
|
||||
if !self.check_authorized(req, None) {
|
||||
self.unauthorized(req)
|
||||
} else {
|
||||
self.dl(&req)
|
||||
}
|
||||
}
|
||||
// publish
|
||||
("put", ["api", "v1", "crates", "new"]) => {
|
||||
if !authorized(true) {
|
||||
self.unauthorized(req)
|
||||
} else {
|
||||
self.publish(req)
|
||||
}
|
||||
}
|
||||
("put", ["api", "v1", "crates", "new"]) => self.check_authorized_publish(req),
|
||||
// The remainder of the operators in the test framework do nothing other than responding 'ok'.
|
||||
//
|
||||
// Note: We don't need to support anything real here because there are no tests that
|
||||
// currently require anything other than publishing via the http api.
|
||||
|
||||
// yank
|
||||
("delete", ["api", "v1", "crates", .., "yank"])
|
||||
// unyank
|
||||
| ("put", ["api", "v1", "crates", .., "unyank"])
|
||||
// yank / unyank
|
||||
("delete" | "put", ["api", "v1", "crates", crate_name, version, mutation]) => {
|
||||
if !self.check_authorized(
|
||||
req,
|
||||
Some(Mutation {
|
||||
mutation,
|
||||
name: Some(crate_name),
|
||||
vers: Some(version),
|
||||
cksum: None,
|
||||
}),
|
||||
) {
|
||||
self.unauthorized(req)
|
||||
} else {
|
||||
self.ok(&req)
|
||||
}
|
||||
}
|
||||
// owners
|
||||
| ("get" | "put" | "delete", ["api", "v1", "crates", .., "owners"]) => {
|
||||
if !authorized(true) {
|
||||
("get" | "put" | "delete", ["api", "v1", "crates", crate_name, "owners"]) => {
|
||||
if !self.check_authorized(
|
||||
req,
|
||||
Some(Mutation {
|
||||
mutation: "owners",
|
||||
name: Some(crate_name),
|
||||
vers: None,
|
||||
cksum: None,
|
||||
}),
|
||||
) {
|
||||
self.unauthorized(req)
|
||||
} else {
|
||||
self.ok(&req)
|
||||
@ -813,7 +1003,7 @@ impl HttpServer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn publish(&self, req: &Request) -> Response {
|
||||
pub fn check_authorized_publish(&self, req: &Request) -> Response {
|
||||
if let Some(body) = &req.body {
|
||||
// Get the metadata of the package
|
||||
let (len, remaining) = body.split_at(4);
|
||||
@ -824,6 +1014,19 @@ impl HttpServer {
|
||||
let (len, remaining) = remaining.split_at(4);
|
||||
let file_len = u32::from_le_bytes(len.try_into().unwrap());
|
||||
let (file, _remaining) = remaining.split_at(file_len as usize);
|
||||
let file_cksum = cksum(&file);
|
||||
|
||||
if !self.check_authorized(
|
||||
req,
|
||||
Some(Mutation {
|
||||
mutation: "publish",
|
||||
name: Some(&new_crate.name),
|
||||
vers: Some(&new_crate.vers),
|
||||
cksum: Some(&file_cksum),
|
||||
}),
|
||||
) {
|
||||
return self.unauthorized(req);
|
||||
}
|
||||
|
||||
// Write the `.crate`
|
||||
let dst = self
|
||||
@ -860,7 +1063,7 @@ impl HttpServer {
|
||||
serde_json::json!(new_crate.name),
|
||||
&new_crate.vers,
|
||||
deps,
|
||||
&cksum(file),
|
||||
&file_cksum,
|
||||
new_crate.features,
|
||||
false,
|
||||
new_crate.links,
|
||||
|
@ -158,7 +158,9 @@ fn get_token_test() -> (Project, TestRegistry) {
|
||||
// API server that checks that the token is included correctly.
|
||||
let server = registry::RegistryBuilder::new()
|
||||
.no_configure_token()
|
||||
.token("sekrit")
|
||||
.token(cargo_test_support::registry::Token::Plaintext(
|
||||
"sekrit".to_string(),
|
||||
))
|
||||
.alternative()
|
||||
.http_api()
|
||||
.build();
|
||||
|
@ -91,6 +91,39 @@ Caused by:
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn simple_add_with_asymmetric() {
|
||||
let registry = registry::RegistryBuilder::new()
|
||||
.http_api()
|
||||
.token(cargo_test_support::registry::Token::rfc_key())
|
||||
.build();
|
||||
setup("foo", None);
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
description = "foo"
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
// The http_api server will check that the authorization is correct.
|
||||
// If the authorization was not sent then we wuld get an unauthorized error.
|
||||
p.cargo("owner -a username")
|
||||
.arg("-Zregistry-auth")
|
||||
.masquerade_as_nightly_cargo(&["registry-auth"])
|
||||
.replace_crates_io(registry.index_url())
|
||||
.with_status(0)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn simple_remove() {
|
||||
let registry = registry::init();
|
||||
@ -124,3 +157,36 @@ Caused by:
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn simple_remove_with_asymmetric() {
|
||||
let registry = registry::RegistryBuilder::new()
|
||||
.http_api()
|
||||
.token(cargo_test_support::registry::Token::rfc_key())
|
||||
.build();
|
||||
setup("foo", None);
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
description = "foo"
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
// The http_api server will check that the authorization is correct.
|
||||
// If the authorization was not sent then we wuld get an unauthorized error.
|
||||
p.cargo("owner -r username")
|
||||
.arg("-Zregistry-auth")
|
||||
.replace_crates_io(registry.index_url())
|
||||
.masquerade_as_nightly_cargo(&["registry-auth"])
|
||||
.with_status(0)
|
||||
.run();
|
||||
}
|
||||
|
@ -134,6 +134,83 @@ See [..]
|
||||
|
||||
// Check that the `token` key works at the root instead of under a
|
||||
// `[registry]` table.
|
||||
#[cargo_test]
|
||||
fn simple_publish_with_http() {
|
||||
let _reg = registry::RegistryBuilder::new()
|
||||
.http_api()
|
||||
.token(registry::Token::Plaintext("sekrit".to_string()))
|
||||
.build();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
description = "foo"
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
p.cargo("publish --no-verify --token sekrit --registry dummy-registry")
|
||||
.with_stderr(
|
||||
"\
|
||||
[UPDATING] `dummy-registry` index
|
||||
[WARNING] manifest has no documentation, [..]
|
||||
See [..]
|
||||
[PACKAGING] foo v0.0.1 ([CWD])
|
||||
[PACKAGED] [..] files, [..] ([..] compressed)
|
||||
[UPLOADING] foo v0.0.1 ([CWD])
|
||||
[UPDATING] `dummy-registry` index
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn simple_publish_with_asymmetric() {
|
||||
let _reg = registry::RegistryBuilder::new()
|
||||
.http_api()
|
||||
.http_index()
|
||||
.alternative_named("dummy-registry")
|
||||
.token(registry::Token::rfc_key())
|
||||
.build();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
description = "foo"
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
p.cargo("publish --no-verify -Zregistry-auth -Zsparse-registry --registry dummy-registry")
|
||||
.masquerade_as_nightly_cargo(&["registry-auth", "sparse-registry"])
|
||||
.with_stderr(
|
||||
"\
|
||||
[UPDATING] `dummy-registry` index
|
||||
[WARNING] manifest has no documentation, [..]
|
||||
See [..]
|
||||
[PACKAGING] foo v0.0.1 ([CWD])
|
||||
[PACKAGED] [..] files, [..] ([..] compressed)
|
||||
[UPLOADING] foo v0.0.1 ([CWD])
|
||||
[UPDATING] `dummy-registry` index
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn old_token_location() {
|
||||
// `publish` generally requires a remote registry
|
||||
@ -2579,7 +2656,8 @@ fn wait_for_subsequent_publish() {
|
||||
*lock += 1;
|
||||
if *lock == 3 {
|
||||
// Run the publish on the 3rd attempt
|
||||
server.publish(&publish_req2.lock().unwrap().as_ref().unwrap());
|
||||
let rep = server.check_authorized_publish(&publish_req2.lock().unwrap().as_ref().unwrap());
|
||||
assert_eq!(rep.code, 200);
|
||||
}
|
||||
server.index(req)
|
||||
})
|
||||
|
@ -64,6 +64,19 @@ fn simple() {
|
||||
cargo(&p, "build").with_stderr(SUCCCESS_OUTPUT).run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn simple_with_asymmetric() {
|
||||
let _registry = RegistryBuilder::new()
|
||||
.alternative()
|
||||
.auth_required()
|
||||
.http_index()
|
||||
.token(cargo_test_support::registry::Token::rfc_key())
|
||||
.build();
|
||||
|
||||
let p = make_project();
|
||||
cargo(&p, "build").with_stderr(SUCCCESS_OUTPUT).run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn environment_config() {
|
||||
let registry = RegistryBuilder::new()
|
||||
@ -100,6 +113,27 @@ fn environment_token() {
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn environment_token_with_asymmetric() {
|
||||
let registry = RegistryBuilder::new()
|
||||
.alternative()
|
||||
.auth_required()
|
||||
.no_configure_token()
|
||||
.http_index()
|
||||
.token(cargo_test_support::registry::Token::Keys(
|
||||
"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36"
|
||||
.to_string(),
|
||||
None,
|
||||
))
|
||||
.build();
|
||||
|
||||
let p = make_project();
|
||||
cargo(&p, "build")
|
||||
.env("CARGO_REGISTRIES_ALTERNATIVE_SECRET_KEY", registry.key())
|
||||
.with_stderr(SUCCCESS_OUTPUT)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn missing_token() {
|
||||
let _registry = RegistryBuilder::new()
|
||||
|
@ -50,6 +50,44 @@ Caused by:
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn explicit_version_with_asymmetric() {
|
||||
let registry = registry::RegistryBuilder::new()
|
||||
.http_api()
|
||||
.token(cargo_test_support::registry::Token::rfc_key())
|
||||
.build();
|
||||
setup("foo", "0.0.1");
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[project]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
authors = []
|
||||
license = "MIT"
|
||||
description = "foo"
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
// The http_api server will check that the authorization is correct.
|
||||
// If the authorization was not sent then we wuld get an unauthorized error.
|
||||
p.cargo("yank --version 0.0.1")
|
||||
.arg("-Zregistry-auth")
|
||||
.masquerade_as_nightly_cargo(&["registry-auth"])
|
||||
.replace_crates_io(registry.index_url())
|
||||
.run();
|
||||
|
||||
p.cargo("yank --undo --version 0.0.1")
|
||||
.arg("-Zregistry-auth")
|
||||
.masquerade_as_nightly_cargo(&["registry-auth"])
|
||||
.replace_crates_io(registry.index_url())
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn inline_version() {
|
||||
let registry = registry::init();
|
||||
|
Loading…
x
Reference in New Issue
Block a user