Skip to content

Commit 1b743ca

Browse files
bors-voyager[bot]carols10centsjtgeibel
committed
Merge #1565
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>
2 parents 3b34485 + d9e5291 commit 1b743ca

File tree

11 files changed

+347
-44
lines changed

11 files changed

+347
-44
lines changed

src/controllers/krate/publish.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use util::{read_fill, read_le_u32};
1414
use controllers::prelude::*;
1515
use models::dependency;
1616
use models::{Badge, Category, Keyword, NewCrate, NewVersion, Rights, User};
17-
use views::{EncodableCrate, EncodableCrateUpload};
17+
use views::{EncodableCrateUpload, GoodCrate, PublishWarnings};
1818

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

6666
let conn = req.db_conn()?;
67+
68+
let mut other_warnings = vec![];
69+
if !user.has_verified_email(&conn)? {
70+
other_warnings.push(String::from(
71+
"You do not currently have a verified email address associated with your crates.io \
72+
account. Starting 2019-02-28, a verified email will be required to publish crates. \
73+
Visit https://crates.io/me to set and verify your email address.",
74+
));
75+
}
76+
6777
// Create a transaction on the database, if there are no errors,
6878
// commit the transactions to record a new or updated crate.
6979
conn.transaction(|| {
@@ -196,23 +206,13 @@ pub fn publish(req: &mut dyn Request) -> CargoResult<Response> {
196206
crate_bomb.path = None;
197207
readme_bomb.path = None;
198208

199-
#[derive(Serialize)]
200-
struct Warnings<'a> {
201-
invalid_categories: Vec<&'a str>,
202-
invalid_badges: Vec<&'a str>,
203-
}
204-
let warnings = Warnings {
209+
let warnings = PublishWarnings {
205210
invalid_categories: ignored_invalid_categories,
206211
invalid_badges: ignored_invalid_badges,
212+
other: other_warnings,
207213
};
208214

209-
#[derive(Serialize)]
210-
struct R<'a> {
211-
#[serde(rename = "crate")]
212-
krate: EncodableCrate,
213-
warnings: Warnings<'a>,
214-
}
215-
Ok(req.json(&R {
215+
Ok(req.json(&GoodCrate {
216216
krate: krate.minimal_encodable(&max_version, None, false, None),
217217
warnings,
218218
}))

src/models/badge.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,11 @@ impl Badge {
104104
serde_json::from_value(serde_json::to_value(self).unwrap()).unwrap()
105105
}
106106

107-
pub fn update_crate<'a>(
107+
pub fn update_crate(
108108
conn: &PgConnection,
109109
krate: &Crate,
110-
badges: Option<&'a HashMap<String, HashMap<String, String>>>,
111-
) -> QueryResult<Vec<&'a str>> {
110+
badges: Option<&HashMap<String, HashMap<String, String>>>,
111+
) -> QueryResult<Vec<String>> {
112112
use diesel::{delete, insert_into};
113113

114114
let mut invalid_badges = vec![];
@@ -126,7 +126,7 @@ impl Badge {
126126
badges::attributes.eq(attributes_json),
127127
));
128128
} else {
129-
invalid_badges.push(&**k);
129+
invalid_badges.push(k.to_string());
130130
}
131131
}
132132
}

src/models/category.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,18 @@ impl Category {
101101
}
102102
}
103103

104-
pub fn update_crate<'a>(
104+
pub fn update_crate(
105105
conn: &PgConnection,
106106
krate: &Crate,
107-
slugs: &[&'a str],
108-
) -> QueryResult<Vec<&'a str>> {
107+
slugs: &[&str],
108+
) -> QueryResult<Vec<String>> {
109109
conn.transaction(|| {
110110
let categories = Category::by_slugs_case_sensitive(slugs).load::<Category>(conn)?;
111111
let invalid_categories = slugs
112112
.iter()
113113
.cloned()
114114
.filter(|s| !categories.iter().any(|c| c.slug == *s))
115+
.map(|s| s.to_string())
115116
.collect();
116117
let crate_categories = categories
117118
.iter()

src/models/user.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,16 @@ impl User {
154154
Ok(best)
155155
}
156156

157+
pub fn has_verified_email(&self, conn: &PgConnection) -> CargoResult<bool> {
158+
use diesel::dsl::exists;
159+
let email_exists = diesel::select(exists(
160+
emails::table
161+
.filter(emails::user_id.eq(self.id))
162+
.filter(emails::verified.eq(true)),
163+
)).get_result(&*conn)?;
164+
Ok(email_exists)
165+
}
166+
157167
/// Converts this `User` model into an `EncodablePrivateUser` for JSON serialization.
158168
pub fn encodable_private(
159169
self,

src/tests/all.rs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use models::{Crate, CrateOwner, Dependency, Team, User, Version};
4545
use models::{NewCategory, NewTeam, NewUser};
4646
use schema::*;
4747
use views::krate_publish as u;
48-
use views::{EncodableCrate, EncodableKeyword, EncodableOwner, EncodableVersion};
48+
use views::{EncodableCrate, EncodableKeyword, EncodableOwner, EncodableVersion, GoodCrate};
4949

5050
macro_rules! t {
5151
($e:expr) => {
@@ -93,22 +93,11 @@ mod user;
9393
mod util;
9494
mod version;
9595

96-
#[derive(Deserialize, Debug)]
97-
pub struct GoodCrate {
98-
#[serde(rename = "crate")]
99-
krate: EncodableCrate,
100-
warnings: Warnings,
101-
}
10296
#[derive(Deserialize)]
10397
pub struct CrateList {
10498
crates: Vec<EncodableCrate>,
10599
meta: CrateMeta,
106100
}
107-
#[derive(Deserialize, Debug)]
108-
struct Warnings {
109-
invalid_categories: Vec<String>,
110-
invalid_badges: Vec<String>,
111-
}
112101
#[derive(Deserialize)]
113102
struct CrateMeta {
114103
total: i32,

src/tests/badge.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ fn travis_ci_required_keys() {
367367

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

@@ -384,7 +384,7 @@ fn gitlab_required_keys() {
384384

385385
let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
386386
assert_eq!(invalid_badges.len(), 1);
387-
assert!(invalid_badges.contains(&"gitlab"));
387+
assert_eq!(invalid_badges.first().unwrap(), "gitlab");
388388
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
389389
}
390390

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

407407
let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
408408
assert_eq!(invalid_badges.len(), 1);
409-
assert!(invalid_badges.contains(&"isitmaintained_issue_resolution"));
409+
assert_eq!(
410+
invalid_badges.first().unwrap(),
411+
"isitmaintained_issue_resolution"
412+
);
410413
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
411414
}
412415

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

429432
let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
430433
assert_eq!(invalid_badges.len(), 1);
431-
assert!(invalid_badges.contains(&"isitmaintained_open_issues"));
434+
assert_eq!(
435+
invalid_badges.first().unwrap(),
436+
"isitmaintained_open_issues"
437+
);
432438
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
433439
}
434440

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

446452
let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
447453
assert_eq!(invalid_badges.len(), 1);
448-
assert!(invalid_badges.contains(&"codecov"));
454+
assert_eq!(invalid_badges.first().unwrap(), "codecov");
449455
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
450456
}
451457

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

463469
let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
464470
assert_eq!(invalid_badges.len(), 1);
465-
assert!(invalid_badges.contains(&"coveralls"));
471+
assert_eq!(invalid_badges.first().unwrap(), "coveralls");
466472
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
467473
}
468474

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

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

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

500506
let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
501507
assert_eq!(invalid_badges.len(), 1);
502-
assert!(invalid_badges.contains(&"maintenance"));
508+
assert_eq!(invalid_badges.first().unwrap(), "maintenance");
503509
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
504510
}
505511

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

522528
let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
523529
assert_eq!(invalid_badges.len(), 1);
524-
assert!(invalid_badges.contains(&"maintenance"));
530+
assert_eq!(invalid_badges.first().unwrap(), "maintenance");
525531
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
526532
}
527533

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

543549
let invalid_badges = Badge::update_crate(&conn, &krate, Some(&badges)).unwrap();
544550
assert_eq!(invalid_badges.len(), 1);
545-
assert!(invalid_badges.contains(&"not-a-badge"));
551+
assert_eq!(invalid_badges.first().unwrap(), "not-a-badge");
546552
assert_eq!(krate.badges(&conn).unwrap(), vec![]);
547553
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
[
2+
{
3+
"request": {
4+
"uri": "http://alexcrichton-test.s3.amazonaws.com/crates/foo_unverified_email/foo_unverified_email-1.0.0.crate",
5+
"method": "PUT",
6+
"headers": [
7+
[
8+
"accept",
9+
"*/*"
10+
],
11+
[
12+
"content-length",
13+
"35"
14+
],
15+
[
16+
"host",
17+
"alexcrichton-test.s3.amazonaws.com"
18+
],
19+
[
20+
"accept-encoding",
21+
"gzip"
22+
],
23+
[
24+
"user-agent",
25+
"reqwest/0.9.1"
26+
],
27+
[
28+
"content-type",
29+
"application/x-tar"
30+
],
31+
[
32+
"authorization",
33+
"AWS AKIAICL5IWUZYWWKA7JA:uDc39eNdF6CcwB+q+JwKsoDLQc4="
34+
],
35+
[
36+
"date",
37+
"Fri, 15 Sep 2017 07:53:06 -0700"
38+
]
39+
],
40+
"body": "H4sIAAAAAAAA/+3AAQEAAACCIP+vbkhQwKsBLq+17wAEAAA="
41+
},
42+
"response": {
43+
"status": 200,
44+
"headers": [
45+
[
46+
"x-amz-request-id",
47+
"26589A5E52F8395C"
48+
],
49+
[
50+
"ETag",
51+
"\"f9016ad360cebb4fe2e6e96e5949f022\""
52+
],
53+
[
54+
"date",
55+
"Fri, 15 Sep 2017 14:53:07 GMT"
56+
],
57+
[
58+
"content-length",
59+
"0"
60+
],
61+
[
62+
"x-amz-id-2",
63+
"JdIvnNTw53aqXjBIqBLNuN4kxf/w1XWX+xuIiGBDYy7yzOSDuAMtBSrTW4ZWetcCIdqCUHuQ51A="
64+
],
65+
[
66+
"Server",
67+
"AmazonS3"
68+
]
69+
],
70+
"body": ""
71+
}
72+
}
73+
]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
[
2+
{
3+
"request": {
4+
"uri": "http://alexcrichton-test.s3.amazonaws.com/crates/foo_verified_email/foo_verified_email-1.0.0.crate",
5+
"method": "PUT",
6+
"headers": [
7+
[
8+
"accept",
9+
"*/*"
10+
],
11+
[
12+
"content-length",
13+
"35"
14+
],
15+
[
16+
"host",
17+
"alexcrichton-test.s3.amazonaws.com"
18+
],
19+
[
20+
"accept-encoding",
21+
"gzip"
22+
],
23+
[
24+
"user-agent",
25+
"reqwest/0.9.1"
26+
],
27+
[
28+
"content-type",
29+
"application/x-tar"
30+
],
31+
[
32+
"authorization",
33+
"AWS AKIAICL5IWUZYWWKA7JA:uDc39eNdF6CcwB+q+JwKsoDLQc4="
34+
],
35+
[
36+
"date",
37+
"Fri, 15 Sep 2017 07:53:06 -0700"
38+
]
39+
],
40+
"body": "H4sIAAAAAAAA/+3AAQEAAACCIP+vbkhQwKsBLq+17wAEAAA="
41+
},
42+
"response": {
43+
"status": 200,
44+
"headers": [
45+
[
46+
"x-amz-request-id",
47+
"26589A5E52F8395C"
48+
],
49+
[
50+
"ETag",
51+
"\"f9016ad360cebb4fe2e6e96e5949f022\""
52+
],
53+
[
54+
"date",
55+
"Fri, 15 Sep 2017 14:53:07 GMT"
56+
],
57+
[
58+
"content-length",
59+
"0"
60+
],
61+
[
62+
"x-amz-id-2",
63+
"JdIvnNTw53aqXjBIqBLNuN4kxf/w1XWX+xuIiGBDYy7yzOSDuAMtBSrTW4ZWetcCIdqCUHuQ51A="
64+
],
65+
[
66+
"Server",
67+
"AmazonS3"
68+
]
69+
],
70+
"body": ""
71+
}
72+
}
73+
]

0 commit comments

Comments
 (0)