From 34075738ea78d90e11bb75c051d3e6e78d80d212 Mon Sep 17 00:00:00 2001 From: Jai A Date: Sat, 26 Sep 2020 22:49:16 -0700 Subject: [PATCH] Basic GitHub integration --- .env | 3 ++ src/database/postgres_database.rs | 2 +- src/main.rs | 4 ++ src/routes/auth.rs | 84 +++++++++++++++++++++++++++++++ src/routes/mod.rs | 2 + src/search/indexing/mod.rs | 12 +++-- src/search/mod.rs | 3 +- 7 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 src/routes/auth.rs diff --git a/.env b/.env index d3b4292be..095fba82d 100644 --- a/.env +++ b/.env @@ -20,3 +20,6 @@ MAX_CURSEFORGE_ID=450000 LOCAL_INDEX_INTERVAL=3600 # 12 hours EXTERNAL_INDEX_INTERVAL=43200 + +GITHUB_CLIENT_ID=3acffb2e808d16d4b226 +GITHUB_CLIENT_SECRET=none \ No newline at end of file diff --git a/src/database/postgres_database.rs b/src/database/postgres_database.rs index 38b67eaf2..eaa6c3136 100644 --- a/src/database/postgres_database.rs +++ b/src/database/postgres_database.rs @@ -1,4 +1,4 @@ -use log::{debug, info}; +use log::info; use sqlx::migrate::{Migrate, MigrateDatabase, Migrator}; use sqlx::postgres::{PgPool, PgPoolOptions}; use sqlx::{Connection, PgConnection, Postgres}; diff --git a/src/main.rs b/src/main.rs index 48bea700f..64e5d0183 100644 --- a/src/main.rs +++ b/src/main.rs @@ -193,6 +193,7 @@ async fn main() -> std::io::Result<()> { .service(routes::index_get) .service( web::scope("/api/v1/") + .configure(routes::auth_config) .configure(routes::tags_config) .configure(routes::mods_config), ) @@ -246,4 +247,7 @@ fn check_env_vars() { } check_var::("LOCAL_INDEX_INTERVAL"); + + check_var::("GITHUB_CLIENT_ID"); + check_var::("GITHUB_CLIENT_SECRET"); } diff --git a/src/routes/auth.rs b/src/routes/auth.rs new file mode 100644 index 000000000..80fdbd954 --- /dev/null +++ b/src/routes/auth.rs @@ -0,0 +1,84 @@ +use crate::models::error::ApiError; +use log::{info}; +use actix_web::web::{Query, ServiceConfig, scope}; +use actix_web::{get, HttpResponse}; +use actix_web::http::StatusCode; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +pub fn config(cfg: &mut ServiceConfig) { + cfg.service(auth_callback); +} + +#[derive(Error, Debug)] +pub enum AuthorizationError { + #[error("Environment Error")] + EnvError(#[from] dotenv::Error), + #[error("An unknown database error occured")] + SqlxDatabaseError(#[from] sqlx::Error), + #[error("Database Error: {0}")] + DatabaseError(#[from] crate::database::models::DatabaseError), + #[error("Error while parsing JSON: {0}")] + SerDeError(#[from] serde_json::Error), + #[error("Error while communicating to GitHub OAuth2")] + GithubError(#[from] reqwest::Error), +} +// "https://github.com/login/oauth/authorize?client_id=3acffb2e808d16d4b226&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fapi%2Fv1%2Fauthcallback" +impl actix_web::ResponseError for AuthorizationError { + fn status_code(&self) -> StatusCode { + match self { + AuthorizationError::EnvError(..) => StatusCode::INTERNAL_SERVER_ERROR, + AuthorizationError::SqlxDatabaseError(..) => StatusCode::INTERNAL_SERVER_ERROR, + AuthorizationError::DatabaseError(..) => StatusCode::INTERNAL_SERVER_ERROR, + AuthorizationError::SerDeError(..) => StatusCode::BAD_REQUEST, + AuthorizationError::GithubError(..) => StatusCode::FAILED_DEPENDENCY, + } + } + + fn error_response(&self) -> HttpResponse { + HttpResponse::build(self.status_code()).json(ApiError { + error: match self { + AuthorizationError::EnvError(..) => "environment_error", + AuthorizationError::SqlxDatabaseError(..) => "database_error", + AuthorizationError::DatabaseError(..) => "database_error", + AuthorizationError::SerDeError(..) => "invalid_input", + AuthorizationError::GithubError(..) => "github_error", + }, + description: &self.to_string(), + }) + } +} + +#[derive(Serialize, Deserialize)] +pub struct Authorization { + pub code: String, + pub state: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct AccessToken { + pub access_token: String, + pub scope: String, + pub token_type: String, +} + +#[get("authcallback")] +pub async fn auth_callback(Query(info): Query) -> Result { + let client_id = dotenv::var("GITHUB_CLIENT_ID")?; + let client_secret = dotenv::var("GITHUB_CLIENT_SECRET")?; + + let url = format!( + "https://github.com/login/oauth/access_token?client_id={}&client_secret={}&code={}", + client_id, client_secret, info.code + ); + + let token : AccessToken = reqwest::Client::new() + .post(&url) + .header(reqwest::header::ACCEPT, "application/json") + .send() + .await? + .json() + .await?; + + Ok(HttpResponse::Ok().json(token)) +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index b1a710939..1573f4cca 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,5 +1,6 @@ use actix_web::web; +mod auth; mod index; mod mod_creation; mod mods; @@ -9,6 +10,7 @@ mod version_creation; mod versions; pub use tags::config as tags_config; +pub use auth::config as auth_config; pub use self::index::index_get; pub use self::not_found::not_found; diff --git a/src/search/indexing/mod.rs b/src/search/indexing/mod.rs index 5a633a66e..b3dfa58dd 100644 --- a/src/search/indexing/mod.rs +++ b/src/search/indexing/mod.rs @@ -63,9 +63,9 @@ pub async fn index_mods(pool: PgPool, settings: IndexingSettings) -> Result<(), } if settings.index_external { let end_index = dotenv::var("MAX_CURSEFORGE_ID") - .ok() - .map(|i| i.parse().unwrap()) - .unwrap_or(450_000); + .ok() + .map(|i| i.parse().unwrap()) + .unwrap_or(450_000); docs_to_add.append(&mut index_curseforge(1, end_index).await?); } @@ -271,7 +271,11 @@ fn default_settings() -> Settings { .with_searchable_attributes(searchable_attributes) .with_stop_words(vec![]) .with_synonyms(HashMap::new()) - .with_attributes_for_faceting(vec![String::from("categories"), String::from("host"), String::from("versions")]) + .with_attributes_for_faceting(vec![ + String::from("categories"), + String::from("host"), + String::from("versions"), + ]) } //endregion diff --git a/src/search/mod.rs b/src/search/mod.rs index 10740e4f0..47ae74ed6 100644 --- a/src/search/mod.rs +++ b/src/search/mod.rs @@ -5,8 +5,7 @@ use actix_web::web::HttpResponse; use meilisearch_sdk::client::Client; use meilisearch_sdk::document::Document; use meilisearch_sdk::search::Query; -use serde::ser::SerializeStruct; -use serde::{Deserialize, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::cmp::min; use thiserror::Error;