Skip to content

Commit

Permalink
Merge #1565
Browse files Browse the repository at this point in the history
1565: Email verification warning r=jtgeibel a=carols10cents

This is the start of the implementation of rust-lang/crates-io-cargo-teams#8. 

We can start warning now; if folks are using nightly, cargo will show it. Next release, beta will show it, and then the release after that, stable will show it. We have until the release after THAT to implement the hard error. I think. If I've counted correctly.

Co-authored-by: Carol (Nichols || Goulding) <carol.nichols@gmail.com>
Co-authored-by: Justin Geibel <jtgeibel@gmail.com>
  • Loading branch information
3 people committed Nov 30, 2018
2 parents 3b34485 + d9e5291 commit 1b743ca
Show file tree
Hide file tree
Showing 11 changed files with 347 additions and 44 deletions.
28 changes: 14 additions & 14 deletions src/controllers/krate/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use util::{read_fill, read_le_u32};
use controllers::prelude::*;
use models::dependency;
use models::{Badge, Category, Keyword, NewCrate, NewVersion, Rights, User};
use views::{EncodableCrate, EncodableCrateUpload};
use views::{EncodableCrateUpload, GoodCrate, PublishWarnings};

/// Handles the `PUT /crates/new` route.
/// Used by `cargo publish` to publish a new crate or to publish a new version of an
Expand Down Expand Up @@ -64,6 +64,16 @@ pub fn publish(req: &mut dyn Request) -> CargoResult<Response> {
let categories: Vec<_> = categories.iter().map(|k| &***k).collect();

let conn = req.db_conn()?;

let mut other_warnings = vec![];
if !user.has_verified_email(&conn)? {
other_warnings.push(String::from(
"You do not currently have a verified email address associated with your crates.io \
account. Starting 2019-02-28, a verified email will be required to publish crates. \
Visit https://crates.io/me to set and verify your email address.",
));
}

// Create a transaction on the database, if there are no errors,
// commit the transactions to record a new or updated crate.
conn.transaction(|| {
Expand Down Expand Up @@ -196,23 +206,13 @@ pub fn publish(req: &mut dyn Request) -> CargoResult<Response> {
crate_bomb.path = None;
readme_bomb.path = None;

#[derive(Serialize)]
struct Warnings<'a> {
invalid_categories: Vec<&'a str>,
invalid_badges: Vec<&'a str>,
}
let warnings = Warnings {
let warnings = PublishWarnings {
invalid_categories: ignored_invalid_categories,
invalid_badges: ignored_invalid_badges,
other: other_warnings,
};

#[derive(Serialize)]
struct R<'a> {
#[serde(rename = "crate")]
krate: EncodableCrate,
warnings: Warnings<'a>,
}
Ok(req.json(&R {
Ok(req.json(&GoodCrate {
krate: krate.minimal_encodable(&max_version, None, false, None),
warnings,
}))
Expand Down
8 changes: 4 additions & 4 deletions src/models/badge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ impl Badge {
serde_json::from_value(serde_json::to_value(self).unwrap()).unwrap()
}

pub fn update_crate<'a>(
pub fn update_crate(
conn: &PgConnection,
krate: &Crate,
badges: Option<&'a HashMap<String, HashMap<String, String>>>,
) -> QueryResult<Vec<&'a str>> {
badges: Option<&HashMap<String, HashMap<String, String>>>,
) -> QueryResult<Vec<String>> {
use diesel::{delete, insert_into};

let mut invalid_badges = vec![];
Expand All @@ -126,7 +126,7 @@ impl Badge {
badges::attributes.eq(attributes_json),
));
} else {
invalid_badges.push(&**k);
invalid_badges.push(k.to_string());
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/models/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,18 @@ impl Category {
}
}

pub fn update_crate<'a>(
pub fn update_crate(
conn: &PgConnection,
krate: &Crate,
slugs: &[&'a str],
) -> QueryResult<Vec<&'a str>> {
slugs: &[&str],
) -> QueryResult<Vec<String>> {
conn.transaction(|| {
let categories = Category::by_slugs_case_sensitive(slugs).load::<Category>(conn)?;
let invalid_categories = slugs
.iter()
.cloned()
.filter(|s| !categories.iter().any(|c| c.slug == *s))
.map(|s| s.to_string())
.collect();
let crate_categories = categories
.iter()
Expand Down
10 changes: 10 additions & 0 deletions src/models/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ impl User {
Ok(best)
}

pub fn has_verified_email(&self, conn: &PgConnection) -> CargoResult<bool> {
use diesel::dsl::exists;
let email_exists = diesel::select(exists(
emails::table
.filter(emails::user_id.eq(self.id))
.filter(emails::verified.eq(true)),
)).get_result(&*conn)?;
Ok(email_exists)
}

/// Converts this `User` model into an `EncodablePrivateUser` for JSON serialization.
pub fn encodable_private(
self,
Expand Down
13 changes: 1 addition & 12 deletions src/tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use models::{Crate, CrateOwner, Dependency, Team, User, Version};
use models::{NewCategory, NewTeam, NewUser};
use schema::*;
use views::krate_publish as u;
use views::{EncodableCrate, EncodableKeyword, EncodableOwner, EncodableVersion};
use views::{EncodableCrate, EncodableKeyword, EncodableOwner, EncodableVersion, GoodCrate};

macro_rules! t {
($e:expr) => {
Expand Down Expand Up @@ -93,22 +93,11 @@ mod user;
mod util;
mod version;

#[derive(Deserialize, Debug)]
pub struct GoodCrate {
#[serde(rename = "crate")]
krate: EncodableCrate,
warnings: Warnings,
}
#[derive(Deserialize)]
pub struct CrateList {
crates: Vec<EncodableCrate>,
meta: CrateMeta,
}
#[derive(Deserialize, Debug)]
struct Warnings {
invalid_categories: Vec<String>,
invalid_badges: Vec<String>,
}
#[derive(Deserialize)]
struct CrateMeta {
total: i32,
Expand Down
26 changes: 16 additions & 10 deletions src/tests/badge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ fn travis_ci_required_keys() {

let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
assert_eq!(invalid_badges.len(), 1);
assert!(invalid_badges.contains(&"travis-ci"));
assert_eq!(invalid_badges.first().unwrap(), "travis-ci");
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
}

Expand All @@ -384,7 +384,7 @@ fn gitlab_required_keys() {

let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
assert_eq!(invalid_badges.len(), 1);
assert!(invalid_badges.contains(&"gitlab"));
assert_eq!(invalid_badges.first().unwrap(), "gitlab");
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
}

Expand All @@ -406,7 +406,10 @@ fn isitmaintained_issue_resolution_required_keys() {

let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
assert_eq!(invalid_badges.len(), 1);
assert!(invalid_badges.contains(&"isitmaintained_issue_resolution"));
assert_eq!(
invalid_badges.first().unwrap(),
"isitmaintained_issue_resolution"
);
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
}

Expand All @@ -428,7 +431,10 @@ fn isitmaintained_open_issues_required_keys() {

let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
assert_eq!(invalid_badges.len(), 1);
assert!(invalid_badges.contains(&"isitmaintained_open_issues"));
assert_eq!(
invalid_badges.first().unwrap(),
"isitmaintained_open_issues"
);
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
}

Expand All @@ -445,7 +451,7 @@ fn codecov_required_keys() {

let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
assert_eq!(invalid_badges.len(), 1);
assert!(invalid_badges.contains(&"codecov"));
assert_eq!(invalid_badges.first().unwrap(), "codecov");
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
}

Expand All @@ -462,7 +468,7 @@ fn coveralls_required_keys() {

let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
assert_eq!(invalid_badges.len(), 1);
assert!(invalid_badges.contains(&"coveralls"));
assert_eq!(invalid_badges.first().unwrap(), "coveralls");
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
}

Expand All @@ -479,7 +485,7 @@ fn circle_ci_required_keys() {

let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
assert_eq!(invalid_badges.len(), 1);
assert!(invalid_badges.contains(&"circle-ci"));
assert_eq!(invalid_badges.first().unwrap(), "circle-ci");
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
}

Expand All @@ -499,7 +505,7 @@ fn maintenance_required_keys() {

let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
assert_eq!(invalid_badges.len(), 1);
assert!(invalid_badges.contains(&"maintenance"));
assert_eq!(invalid_badges.first().unwrap(), "maintenance");
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
}

Expand All @@ -521,7 +527,7 @@ fn maintenance_invalid_values() {

let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
assert_eq!(invalid_badges.len(), 1);
assert!(invalid_badges.contains(&"maintenance"));
assert_eq!(invalid_badges.first().unwrap(), "maintenance");
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
}

Expand All @@ -542,6 +548,6 @@ fn unknown_badge() {

let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
assert_eq!(invalid_badges.len(), 1);
assert!(invalid_badges.contains(&"not-a-badge"));
assert_eq!(invalid_badges.first().unwrap(), "not-a-badge");
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
}
73 changes: 73 additions & 0 deletions src/tests/http-data/krate_new_krate_with_unverified_email_warns
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
[
{
"request": {
"uri": "http://alexcrichton-test.s3.amazonaws.com/crates/foo_unverified_email/foo_unverified_email-1.0.0.crate",
"method": "PUT",
"headers": [
[
"accept",
"*/*"
],
[
"content-length",
"35"
],
[
"host",
"alexcrichton-test.s3.amazonaws.com"
],
[
"accept-encoding",
"gzip"
],
[
"user-agent",
"reqwest/0.9.1"
],
[
"content-type",
"application/x-tar"
],
[
"authorization",
"AWS AKIAICL5IWUZYWWKA7JA:uDc39eNdF6CcwB+q+JwKsoDLQc4="
],
[
"date",
"Fri, 15 Sep 2017 07:53:06 -0700"
]
],
"body": "H4sIAAAAAAAA/+3AAQEAAACCIP+vbkhQwKsBLq+17wAEAAA="
},
"response": {
"status": 200,
"headers": [
[
"x-amz-request-id",
"26589A5E52F8395C"
],
[
"ETag",
"\"f9016ad360cebb4fe2e6e96e5949f022\""
],
[
"date",
"Fri, 15 Sep 2017 14:53:07 GMT"
],
[
"content-length",
"0"
],
[
"x-amz-id-2",
"JdIvnNTw53aqXjBIqBLNuN4kxf/w1XWX+xuIiGBDYy7yzOSDuAMtBSrTW4ZWetcCIdqCUHuQ51A="
],
[
"Server",
"AmazonS3"
]
],
"body": ""
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
[
{
"request": {
"uri": "http://alexcrichton-test.s3.amazonaws.com/crates/foo_verified_email/foo_verified_email-1.0.0.crate",
"method": "PUT",
"headers": [
[
"accept",
"*/*"
],
[
"content-length",
"35"
],
[
"host",
"alexcrichton-test.s3.amazonaws.com"
],
[
"accept-encoding",
"gzip"
],
[
"user-agent",
"reqwest/0.9.1"
],
[
"content-type",
"application/x-tar"
],
[
"authorization",
"AWS AKIAICL5IWUZYWWKA7JA:uDc39eNdF6CcwB+q+JwKsoDLQc4="
],
[
"date",
"Fri, 15 Sep 2017 07:53:06 -0700"
]
],
"body": "H4sIAAAAAAAA/+3AAQEAAACCIP+vbkhQwKsBLq+17wAEAAA="
},
"response": {
"status": 200,
"headers": [
[
"x-amz-request-id",
"26589A5E52F8395C"
],
[
"ETag",
"\"f9016ad360cebb4fe2e6e96e5949f022\""
],
[
"date",
"Fri, 15 Sep 2017 14:53:07 GMT"
],
[
"content-length",
"0"
],
[
"x-amz-id-2",
"JdIvnNTw53aqXjBIqBLNuN4kxf/w1XWX+xuIiGBDYy7yzOSDuAMtBSrTW4ZWetcCIdqCUHuQ51A="
],
[
"Server",
"AmazonS3"
]
],
"body": ""
}
}
]
Loading

0 comments on commit 1b743ca

Please sign in to comment.