diff --git a/Cargo.lock b/Cargo.lock index f6c10918f40..ac4fd9a0b21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,7 +211,7 @@ dependencies = [ "lettre_email 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "license-exprs 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "oauth2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "oauth2 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1498,11 +1498,18 @@ dependencies = [ [[package]] name = "oauth2" -version = "0.3.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "curl 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2111,6 +2118,17 @@ dependencies = [ "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "sha2" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "siphasher" version = "0.2.2" @@ -2950,7 +2968,7 @@ dependencies = [ "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" -"checksum oauth2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f4fcd990d45681b9eba5f4f3fa7d0371ec277f4e4380a94104d26aa4fae386fc" +"checksum oauth2 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b782199cf581a36bdee16efd40f12f10a5aefb8864ed16417a8dc8a4c25f13b" "checksum openssl 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "5af9e83eb3c51ee806387d26a43056f3246d865844caa6dd704d2ba7e831c264" "checksum openssl-probe 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d98df0270d404ccd3c050a41d579c52d1db15375168bb3471e04ec0f5f378daf" "checksum openssl-sys 0.9.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ff3d1b390ab1b9700f682ad95a30dc9c0f40dd212ca57266012cfc678b0e365a" @@ -3022,6 +3040,7 @@ dependencies = [ "checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" "checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" "checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" +"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "88aea073965ab29f6edb5493faf96ad662fb18aa9eeb186a3b7057951605ed15" diff --git a/Cargo.toml b/Cargo.toml index 0c540febc50..eb6de196c78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ tar = "0.4.16" base64 = "0.9" openssl = "0.10.13" -oauth2 = "0.3" +oauth2 = "2.0.0" log = "0.4" env_logger = "0.5" hex = "0.3" diff --git a/src/app.rs b/src/app.rs index db84bdb015f..e8708416526 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,6 +4,7 @@ use crate::{db, Config, Env}; use std::{path::PathBuf, sync::Arc, time::Duration}; use diesel::r2d2; +use oauth2::basic::BasicClient; use reqwest::Client; use scheduled_thread_pool::ScheduledThreadPool; @@ -16,7 +17,7 @@ pub struct App { pub diesel_database: db::DieselPool, /// The GitHub OAuth2 configuration - pub github: oauth2::Config, + pub github: BasicClient, /// A unique key used with conduit_cookie to generate cookies pub session_key: String, @@ -43,15 +44,21 @@ impl App { /// /// - GitHub OAuth /// - Database connection pools - /// - Holds an HTTP `Client` and associated connection pool, if provided + /// - A `git2::Repository` instance from the index repo checkout (that server.rs ensures exists) pub fn new(config: &Config, http_client: Option) -> App { - let mut github = oauth2::Config::new( - &config.gh_client_id, - &config.gh_client_secret, - "https://github.com/login/oauth/authorize", - "https://github.com/login/oauth/access_token", - ); - github.scopes.push(String::from("read:org")); + use oauth2::prelude::*; + use oauth2::{AuthUrl, ClientId, ClientSecret, Scope, TokenUrl}; + use url::Url; + + let github = BasicClient::new( + ClientId::new(config.gh_client_id.clone()), + Some(ClientSecret::new(config.gh_client_secret.clone())), + AuthUrl::new(Url::parse("https://github.com/login/oauth/authorize").unwrap()), + Some(TokenUrl::new( + Url::parse("https://github.com/login/oauth/access_token").unwrap(), + )), + ) + .add_scope(Scope::new("read:org".to_string())); let db_pool_size = match (dotenv::var("DB_POOL_SIZE"), config.env) { (Ok(num), _) => num.parse().expect("couldn't parse DB_POOL_SIZE"), diff --git a/src/controllers.rs b/src/controllers.rs index a8f947f1282..dc80fcf803d 100644 --- a/src/controllers.rs +++ b/src/controllers.rs @@ -34,7 +34,7 @@ mod prelude { fn query(&self) -> IndexMap { url::form_urlencoded::parse(self.query_string().unwrap_or("").as_bytes()) - .map(|(a, b)| (a.into_owned(), b.into_owned())) + .into_owned() .collect() } diff --git a/src/controllers/user/session.rs b/src/controllers/user/session.rs index 0f0760d8113..fdbdd5f099d 100644 --- a/src/controllers/user/session.rs +++ b/src/controllers/user/session.rs @@ -2,8 +2,7 @@ use crate::controllers::prelude::*; use crate::github; use conduit_cookie::RequestSession; -use rand::distributions::Alphanumeric; -use rand::{thread_rng, Rng}; +use oauth2::{prelude::*, AuthorizationCode, TokenResponse}; use crate::models::{NewUser, User}; use crate::schema::users; @@ -25,17 +24,14 @@ use crate::util::errors::{CargoError, ReadOnlyMode}; /// } /// ``` pub fn github_authorize(req: &mut dyn Request) -> CargoResult { - // Generate a random 16 char ASCII string - let mut rng = thread_rng(); - let state: String = std::iter::repeat(()) - .map(|()| rng.sample(Alphanumeric)) - .take(16) - .collect(); + let (url, state) = req + .app() + .github + .authorize_url(oauth2::CsrfToken::new_random); + let state = state.secret().to_string(); req.session() .insert("github_oauth_state".to_string(), state.clone()); - let url = req.app().github.authorize_url(state.clone()); - #[derive(Serialize)] struct R { url: String, @@ -92,11 +88,16 @@ pub fn github_access_token(req: &mut dyn Request) -> CargoResult { } // Fetch the access token from github using the code we just got - let token = req.app().github.exchange(code).map_err(|s| human(&s))?; - - let ghuser = github::github::(req.app(), "/user", &token)?; - let user = ghuser.save_to_database(&token.access_token, &*req.db_conn()?)?; + let code = AuthorizationCode::new(code); + let token = req + .app() + .github + .exchange_code(code) + .map_err(|s| human(&s))?; + let token = token.access_token(); + let ghuser = github::github_api::(req.app(), "/user", token)?; + let user = ghuser.save_to_database(&token.secret(), &*req.db_conn()?)?; req.session() .insert("user_id".to_string(), user.id.to_string()); req.mut_extensions().insert(user); diff --git a/src/github.rs b/src/github.rs index f5b1a877407..428ee60fa1e 100644 --- a/src/github.rs +++ b/src/github.rs @@ -1,6 +1,6 @@ //! This module implements functionality for interacting with GitHub. -use oauth2::*; +use oauth2::{prelude::*, AccessToken}; use reqwest::{self, header}; use serde::de::DeserializeOwned; @@ -13,7 +13,7 @@ use crate::util::{errors::NotFound, human, internal, CargoError, CargoResult}; /// Does all the nonsense for sending a GET to Github. Doesn't handle parsing /// because custom error-code handling may be desirable. Use /// `parse_github_response` to handle the "common" processing of responses. -pub fn github(app: &App, url: &str, auth: &Token) -> CargoResult +pub fn github_api(app: &App, url: &str, auth: &AccessToken) -> CargoResult where T: DeserializeOwned, { @@ -23,10 +23,7 @@ where app.http_client() .get(&url) .header(header::ACCEPT, "application/vnd.github.v3+json") - .header( - header::AUTHORIZATION, - format!("token {}", auth.access_token), - ) + .header(header::AUTHORIZATION, format!("token {}", auth.secret())) .send()? .error_for_status() .map_err(|e| handle_error_response(&e))? @@ -55,16 +52,6 @@ fn handle_error_response(error: &reqwest::Error) -> Box { } } -/// Gets a token with the given string as the access token, but all -/// other info null'd out. Generally, just to be fed to the `github` fn. -pub fn token(token: String) -> Token { - Token { - access_token: token, - scopes: Vec::new(), - token_type: String::new(), - } -} - pub fn team_url(login: &str) -> String { let mut login_pieces = login.split(':'); login_pieces.next(); diff --git a/src/models/team.rs b/src/models/team.rs index dd3162cb4e0..831b3a93390 100644 --- a/src/models/team.rs +++ b/src/models/team.rs @@ -1,9 +1,11 @@ use diesel::prelude::*; use crate::app::App; -use crate::github; +use crate::github::{github_api, team_url}; use crate::util::{errors::NotFound, human, CargoResult}; +use oauth2::{prelude::*, AccessToken}; + use crate::models::{Crate, CrateOwner, Owner, OwnerKind, User}; use crate::schema::{crate_owners, teams}; use crate::views::EncodableTeam; @@ -141,8 +143,8 @@ impl Team { // FIXME: we just set per_page=100 and don't bother chasing pagination // links. A hundred teams should be enough for any org, right? let url = format!("/orgs/{}/teams?per_page=100", org_name); - let token = github::token(req_user.gh_access_token.clone()); - let teams = github::github::>(app, &url, &token)?; + let token = AccessToken::new(req_user.gh_access_token.clone()); + let teams = github_api::>(app, &url, &token)?; let team = teams .into_iter() @@ -164,7 +166,7 @@ impl Team { } let url = format!("/orgs/{}", org_name); - let org = github::github::(app, &url, &token)?; + let org = github_api::(app, &url, &token)?; NewTeam::new(&login.to_lowercase(), team.id, team.name, org.avatar_url) .create_or_update(conn) @@ -200,7 +202,7 @@ impl Team { avatar, .. } = self; - let url = github::team_url(&login); + let url = team_url(&login); EncodableTeam { id, @@ -222,8 +224,8 @@ fn team_with_gh_id_contains_user(app: &App, github_id: i32, user: &User) -> Carg } let url = format!("/teams/{}/memberships/{}", &github_id, &user.gh_login); - let token = github::token(user.gh_access_token.clone()); - let membership = match github::github::(app, &url, &token) { + let token = AccessToken::new(user.gh_access_token.clone()); + let membership = match github_api::(app, &url, &token) { // Officially how `false` is returned Err(ref e) if e.is::() => return Ok(false), x => x?,