From 4cd04202dff43ec2235e6661c70b9d30376ba205 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Fri, 27 Nov 2020 12:22:22 +0200 Subject: [PATCH 1/3] Add an option to disable the requirement for email verification before publishing --- .env.sample | 4 ++ src/config.rs | 8 +++ src/controllers/krate/publish.rs | 29 +++++--- src/tests/all.rs | 1 + ...ified_email_if_email_verification_disabled | 69 +++++++++++++++++++ src/tests/krate/publish.rs | 26 +++++++ 6 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 src/tests/http-data/krate_publish_new_krate_records_with_unverified_email_if_email_verification_disabled diff --git a/.env.sample b/.env.sample index 098d86f5cf7..4f615129ab3 100644 --- a/.env.sample +++ b/.env.sample @@ -60,3 +60,7 @@ export GH_CLIENT_SECRET= # Credentials for connecting to the Sentry error reporting service. # export SENTRY_DSN_API= export SENTRY_ENV_API=local + +# If you don't require users to complete email verification before +# they can publish a crate, uncomment this line. +# export DISABLE_EMAIL_VERIFICATION_REQUIREMENT=1 diff --git a/src/config.rs b/src/config.rs index 300badf8df4..ae01f1c5302 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,6 +18,7 @@ pub struct Config { pub blocked_traffic: Vec<(String, Vec)>, pub domain_name: String, pub allowed_origins: Vec, + pub require_email_verification: bool, } impl Default for Config { @@ -44,6 +45,8 @@ impl Default for Config { /// - `READ_ONLY_REPLICA_URL`: The URL of an optional postgres read-only replica database. /// - `BLOCKED_TRAFFIC`: A list of headers and environment variables to use for blocking ///. traffic. See the `block_traffic` module for more documentation. + /// - `DISABLE_EMAIL_VERIFICATION_REQUIREMENT`: Disable the requirement that email must be + ///. verified before publishing a crate. fn default() -> Config { let api_protocol = String::from("https"); let mirror = if dotenv::var("MIRROR").is_ok() { @@ -142,6 +145,7 @@ impl Default for Config { blocked_traffic: blocked_traffic(), domain_name: domain_name(), allowed_origins, + require_email_verification: require_email_verification(), } } } @@ -150,6 +154,10 @@ pub(crate) fn domain_name() -> String { dotenv::var("DOMAIN_NAME").unwrap_or_else(|_| "crates.io".into()) } +fn require_email_verification() -> bool { + !dotenv::var("DISABLE_EMAIL_VERIFICATION_REQUIREMENT").is_ok() +} + fn blocked_traffic() -> Vec<(String, Vec)> { let pattern_list = dotenv::var("BLOCKED_TRAFFIC").unwrap_or_default(); parse_traffic_patterns(&pattern_list) diff --git a/src/controllers/krate/publish.rs b/src/controllers/krate/publish.rs index 0972391493f..8d88ee99fd2 100644 --- a/src/controllers/krate/publish.rs +++ b/src/controllers/krate/publish.rs @@ -55,14 +55,25 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult { let api_token_id = ids.api_token_id(); let user = ids.user(); - let verified_email_address = user.verified_email(&conn)?; - let verified_email_address = verified_email_address.ok_or_else(|| { - cargo_err(&format!( - "A verified email address is required to publish crates to crates.io. \ - Visit https://{}/me to set and verify your email address.", - app.config.domain_name, - )) - })?; + let email_address = if app.config.require_email_verification { + let verified_email_address = user.verified_email(&conn)?; + verified_email_address.ok_or_else(|| { + cargo_err(&format!( + "A verified email address is required to publish crates to crates.io. \ + Visit https://{}/me to set and verify your email address.", + app.config.domain_name, + )) + })? + } else { + let email_address = user.email(&conn)?; + email_address.ok_or_else(|| { + cargo_err(&format!( + "An email address is required to publish crates to crates.io. \ + Visit https://{}/me to set and verify your email address.", + app.config.domain_name, + )) + })? + }; // Create a transaction on the database, if there are no errors, // commit the transactions to record a new or updated crate. @@ -152,7 +163,7 @@ pub fn publish(req: &mut dyn RequestExt) -> EndpointResult { file_length as i32, user.id, )? - .save(&conn, &new_crate.authors, &verified_email_address)?; + .save(&conn, &new_crate.authors, &email_address)?; insert_version_owner_action( &conn, diff --git a/src/tests/all.rs b/src/tests/all.rs index 8c13f3aecfb..74ee703f659 100644 --- a/src/tests/all.rs +++ b/src/tests/all.rs @@ -142,6 +142,7 @@ fn simple_config() -> Config { blocked_traffic: Default::default(), domain_name: "crates.io".into(), allowed_origins: Vec::new(), + require_email_verification: true, } } diff --git a/src/tests/http-data/krate_publish_new_krate_records_with_unverified_email_if_email_verification_disabled b/src/tests/http-data/krate_publish_new_krate_records_with_unverified_email_if_email_verification_disabled new file mode 100644 index 00000000000..849c0026c3e --- /dev/null +++ b/src/tests/http-data/krate_publish_new_krate_records_with_unverified_email_if_email_verification_disabled @@ -0,0 +1,69 @@ +[ + { + "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" + ], + [ + "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": "" + } + } +] \ No newline at end of file diff --git a/src/tests/krate/publish.rs b/src/tests/krate/publish.rs index 0fd3d20f834..d6f21b911df 100644 --- a/src/tests/krate/publish.rs +++ b/src/tests/krate/publish.rs @@ -618,6 +618,32 @@ fn new_krate_with_unverified_email_fails() { ); } +#[test] +fn new_krate_records_with_unverified_email_if_email_verification_disabled() { + let (app, _, _, token) = TestApp::full() + .with_config(|c| c.require_email_verification = false) + .with_token(); + + app.db(|conn| { + update(emails::table) + .set((emails::verified.eq(false),)) + .execute(conn) + .unwrap(); + }); + + let crate_to_publish = PublishBuilder::new("foo_unverified_email"); + + token.enqueue_publish(crate_to_publish).good(); + + app.db(|conn| { + let email: String = versions_published_by::table + .select(versions_published_by::email) + .first(conn) + .unwrap(); + assert_eq!(email, "something@example.com"); + }); +} + #[test] fn new_krate_records_verified_email() { let (app, _, _, token) = TestApp::full().with_token(); From 145b0bfbc05b14af311dc0c02c18503c574a59b7 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Fri, 27 Nov 2020 13:39:33 +0200 Subject: [PATCH 2/3] Update contributing docs to mention email verification --- docs/CONTRIBUTING.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index c4cafe2c646..5a9828799e2 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -439,12 +439,22 @@ OAuth Applications](https://github.com/settings/developers) and click on the - Homepage URL: `http://localhost:4200/` - Authorization callback URL: `http://localhost:4200/authorize/github` -Create the application, then take the Client ID ad Client Secret values and use +Create the application, then take the Client ID and Client Secret values and use them as the values of the `GH_CLIENT_ID` and `GH_CLIENT_SECRET` in your `.env`. Then restart your backend, and you should be able to log in to your local crates.io with your GitHub account. +The next step is that you need to verify your email address to be able +to publish crates. When you log in for the first time, an email will +be sent with a verification link. In development, the sending of +emails is simulated by a file representing the email being created in +your local `/tmp/` directory. You need to find that email file and +follow the link to verify your email. As an easier alternative, you +can disable requiring a verified email in development by setting +`DISABLE_EMAIL_VERIFICATION_REQUIREMENT` in your `.env` file and +restart your backend. + Go to http://localhost:4200/me to get your API token and run the `cargo login` command as directed. From 82b3aad5248f45d271cdd55a7e436fcb49c173f9 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Fri, 27 Nov 2020 14:09:55 +0200 Subject: [PATCH 3/3] Fix clippy warning --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index ae01f1c5302..c11da14363f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -155,7 +155,7 @@ pub(crate) fn domain_name() -> String { } fn require_email_verification() -> bool { - !dotenv::var("DISABLE_EMAIL_VERIFICATION_REQUIREMENT").is_ok() + dotenv::var("DISABLE_EMAIL_VERIFICATION_REQUIREMENT").is_err() } fn blocked_traffic() -> Vec<(String, Vec)> {