From a82d61ed27c707e8b3ac904c13684552fc571b20 Mon Sep 17 00:00:00 2001 From: realaravinth Date: Sat, 1 May 2021 23:39:52 +0530 Subject: [PATCH] api endpoints migrated to use auth middleware --- src/api/v1/auth.rs | 27 +++--------- src/api/v1/mcaptcha/duration.rs | 8 ++-- src/api/v1/mcaptcha/levels.rs | 14 +++--- src/api/v1/mcaptcha/mcaptcha.rs | 15 +++---- src/api/v1/mcaptcha/mod.rs | 2 - src/api/v1/pow/mod.rs | 1 - src/api/v1/tests/auth.rs | 4 +- src/api/v1/tests/mod.rs | 1 + src/api/v1/tests/protected.rs | 75 +++++++++++++++++++++++++++++++++ src/errors.rs | 7 ++- 10 files changed, 102 insertions(+), 52 deletions(-) create mode 100644 src/api/v1/tests/protected.rs diff --git a/src/api/v1/auth.rs b/src/api/v1/auth.rs index b2293856..d70d9fa9 100644 --- a/src/api/v1/auth.rs +++ b/src/api/v1/auth.rs @@ -24,6 +24,7 @@ use serde::{Deserialize, Serialize}; use super::mcaptcha::get_random; use crate::errors::*; +use crate::CheckLogin; use crate::Data; #[derive(Clone, Debug, Deserialize, Serialize)] @@ -153,8 +154,6 @@ pub struct Secret { #[get("/api/v1/account/secret/")] pub async fn get_secret(id: Identity, data: web::Data) -> ServiceResult { - is_authenticated(&id)?; - let username = id.identity().unwrap(); let secret = sqlx::query_as!( @@ -168,13 +167,11 @@ pub async fn get_secret(id: Identity, data: web::Data) -> ServiceResult, ) -> ServiceResult { - is_authenticated(&id)?; - let username = id.identity().unwrap(); let mut secret; @@ -211,7 +208,7 @@ pub struct Email { pub email: String, } -#[post("/api/v1/account/email/")] +#[post("/api/v1/account/email/", wrap = "CheckLogin")] pub async fn set_email( id: Identity, @@ -219,8 +216,6 @@ pub async fn set_email( data: web::Data, ) -> ServiceResult { - is_authenticated(&id)?; - let username = id.identity().unwrap(); data.creds.email(&payload.email)?; @@ -247,25 +242,17 @@ pub async fn set_email( Ok(HttpResponse::Ok()) } -#[get("/logout")] +#[get("/logout", wrap = "CheckLogin")] pub async fn signout(id: Identity) -> impl Responder { if let Some(_) = id.identity() { id.forget(); } - HttpResponse::Found() + HttpResponse::Ok() .set_header(header::LOCATION, "/login") .body("") } -/// Check if user is authenticated -// TODO use middleware -pub fn is_authenticated(id: &Identity) -> ServiceResult<()> { - // access request identity - id.identity().ok_or(ServiceError::AuthorizationRequired)?; - Ok(()) -} - -#[post("/api/v1/account/delete")] +#[post("/api/v1/account/delete", wrap = "CheckLogin")] pub async fn delete_account( id: Identity, payload: web::Json, @@ -274,8 +261,6 @@ pub async fn delete_account( use argon2_creds::Config; use sqlx::Error::RowNotFound; - is_authenticated(&id)?; - let username = id.identity().unwrap(); let rec = sqlx::query_as!( diff --git a/src/api/v1/mcaptcha/duration.rs b/src/api/v1/mcaptcha/duration.rs index fb8d87b5..6973f42b 100644 --- a/src/api/v1/mcaptcha/duration.rs +++ b/src/api/v1/mcaptcha/duration.rs @@ -19,9 +19,9 @@ use actix_identity::Identity; use actix_web::{post, web, HttpResponse, Responder}; use serde::{Deserialize, Serialize}; -use super::is_authenticated; use crate::api::v1::mcaptcha::mcaptcha::MCaptchaDetails; use crate::errors::*; +use crate::CheckLogin; use crate::Data; #[derive(Deserialize, Serialize)] @@ -30,13 +30,12 @@ pub struct UpdateDuration { pub duration: i32, } -#[post("/api/v1/mcaptcha/domain/token/duration/update")] +#[post("/api/v1/mcaptcha/domain/token/duration/update", wrap = "CheckLogin")] pub async fn update_duration( payload: web::Json, data: web::Data, id: Identity, ) -> ServiceResult { - is_authenticated(&id)?; let username = id.identity().unwrap(); if payload.duration > 0 { @@ -69,13 +68,12 @@ pub struct GetDuration { pub token: String, } -#[post("/api/v1/mcaptcha/domain/token/duration/get")] +#[post("/api/v1/mcaptcha/domain/token/duration/get", wrap = "CheckLogin")] pub async fn get_duration( payload: web::Json, data: web::Data, id: Identity, ) -> ServiceResult { - is_authenticated(&id)?; let username = id.identity().unwrap(); let duration = sqlx::query_as!( diff --git a/src/api/v1/mcaptcha/levels.rs b/src/api/v1/mcaptcha/levels.rs index f1c3eef8..8ccee607 100644 --- a/src/api/v1/mcaptcha/levels.rs +++ b/src/api/v1/mcaptcha/levels.rs @@ -20,9 +20,9 @@ use actix_web::{post, web, HttpResponse, Responder}; use m_captcha::{defense::Level, DefenseBuilder}; use serde::{Deserialize, Serialize}; -use super::is_authenticated; use crate::api::v1::mcaptcha::mcaptcha::MCaptchaDetails; use crate::errors::*; +use crate::CheckLogin; use crate::Data; #[derive(Serialize, Deserialize)] @@ -34,13 +34,12 @@ pub struct AddLevels { // TODO try for non-existent token names -#[post("/api/v1/mcaptcha/levels/add")] +#[post("/api/v1/mcaptcha/levels/add", wrap = "CheckLogin")] pub async fn add_levels( payload: web::Json, data: web::Data, id: Identity, ) -> ServiceResult { - is_authenticated(&id)?; let mut defense = DefenseBuilder::default(); let username = id.identity().unwrap(); @@ -75,13 +74,12 @@ pub async fn add_levels( Ok(HttpResponse::Ok()) } -#[post("/api/v1/mcaptcha/levels/update")] +#[post("/api/v1/mcaptcha/levels/update", wrap = "CheckLogin")] pub async fn update_levels( payload: web::Json, data: web::Data, id: Identity, ) -> ServiceResult { - is_authenticated(&id)?; let username = id.identity().unwrap(); let mut defense = DefenseBuilder::default(); @@ -134,13 +132,12 @@ pub async fn update_levels( Ok(HttpResponse::Ok()) } -#[post("/api/v1/mcaptcha/levels/delete")] +#[post("/api/v1/mcaptcha/levels/delete", wrap = "CheckLogin")] pub async fn delete_levels( payload: web::Json, data: web::Data, id: Identity, ) -> ServiceResult { - is_authenticated(&id)?; let username = id.identity().unwrap(); for level in payload.levels.iter() { @@ -162,13 +159,12 @@ pub async fn delete_levels( Ok(HttpResponse::Ok()) } -#[post("/api/v1/mcaptcha/levels/get")] +#[post("/api/v1/mcaptcha/levels/get", wrap = "CheckLogin")] pub async fn get_levels( payload: web::Json, data: web::Data, id: Identity, ) -> ServiceResult { - is_authenticated(&id)?; let username = id.identity().unwrap(); let levels = get_levels_util(&payload.key, &username, &data).await?; diff --git a/src/api/v1/mcaptcha/mcaptcha.rs b/src/api/v1/mcaptcha/mcaptcha.rs index 8a4ea253..64ee6dd2 100644 --- a/src/api/v1/mcaptcha/mcaptcha.rs +++ b/src/api/v1/mcaptcha/mcaptcha.rs @@ -20,8 +20,9 @@ use actix_identity::Identity; use actix_web::{post, web, HttpResponse, Responder}; use serde::{Deserialize, Serialize}; -use super::{get_random, is_authenticated}; +use super::get_random; use crate::errors::*; +use crate::CheckLogin; use crate::Data; #[derive(Clone, Debug, Deserialize, Serialize)] @@ -35,9 +36,8 @@ pub struct MCaptchaDetails { pub key: String, } -#[post("/api/v1/mcaptcha/add")] +#[post("/api/v1/mcaptcha/add", wrap = "CheckLogin")] pub async fn add_mcaptcha(data: web::Data, id: Identity) -> ServiceResult { - is_authenticated(&id)?; let username = id.identity().unwrap(); let mut key; @@ -78,7 +78,7 @@ pub async fn add_mcaptcha(data: web::Data, id: Identity) -> ServiceResult< Ok(HttpResponse::Ok().json(resp)) } -#[post("/api/v1/mcaptcha/update/key")] +#[post("/api/v1/mcaptcha/update/key", wrap = "CheckLogin")] pub async fn update_token( payload: web::Json, data: web::Data, @@ -86,7 +86,6 @@ pub async fn update_token( ) -> ServiceResult { use std::borrow::Cow; - is_authenticated(&id)?; let username = id.identity().unwrap(); let mut key; @@ -132,13 +131,12 @@ async fn update_token_helper( Ok(()) } -#[post("/api/v1/mcaptcha/get")] +#[post("/api/v1/mcaptcha/get", wrap = "CheckLogin")] pub async fn get_token( payload: web::Json, data: web::Data, id: Identity, ) -> ServiceResult { - is_authenticated(&id)?; let username = id.identity().unwrap(); let res = match sqlx::query_as!( MCaptchaDetails, @@ -161,13 +159,12 @@ pub async fn get_token( Ok(HttpResponse::Ok().json(res)) } -#[post("/api/v1/mcaptcha/delete")] +#[post("/api/v1/mcaptcha/delete", wrap = "CheckLogin")] pub async fn delete_mcaptcha( payload: web::Json, data: web::Data, id: Identity, ) -> ServiceResult { - is_authenticated(&id)?; let username = id.identity().unwrap(); sqlx::query!( diff --git a/src/api/v1/mcaptcha/mod.rs b/src/api/v1/mcaptcha/mod.rs index 8a649ea0..7ea73f92 100644 --- a/src/api/v1/mcaptcha/mod.rs +++ b/src/api/v1/mcaptcha/mod.rs @@ -20,8 +20,6 @@ pub mod levels; pub mod mcaptcha; pub mod stats; -pub use super::auth::is_authenticated; - pub fn get_random(len: usize) -> String { use std::iter; diff --git a/src/api/v1/pow/mod.rs b/src/api/v1/pow/mod.rs index d8e8e843..bb61a4dd 100644 --- a/src/api/v1/pow/mod.rs +++ b/src/api/v1/pow/mod.rs @@ -23,7 +23,6 @@ pub mod verify_pow; pub mod verify_token; pub use super::mcaptcha::duration::GetDurationResp; -pub use super::mcaptcha::is_authenticated; pub use super::mcaptcha::levels::I32Levels; // middleware protected by scope diff --git a/src/api/v1/tests/auth.rs b/src/api/v1/tests/auth.rs index 96339a3e..90bff751 100644 --- a/src/api/v1/tests/auth.rs +++ b/src/api/v1/tests/auth.rs @@ -130,7 +130,9 @@ async fn auth_works() { .to_request(), ) .await; - assert_eq!(signout_resp.status(), StatusCode::FOUND); + assert_eq!(signout_resp.status(), StatusCode::OK); + let headers = signout_resp.headers(); + assert_eq!(headers.get(header::LOCATION).unwrap(), "/login"); } #[actix_rt::test] diff --git a/src/api/v1/tests/mod.rs b/src/api/v1/tests/mod.rs index edcf8801..0cded212 100644 --- a/src/api/v1/tests/mod.rs +++ b/src/api/v1/tests/mod.rs @@ -16,3 +16,4 @@ */ mod auth; +mod protected; diff --git a/src/api/v1/tests/protected.rs b/src/api/v1/tests/protected.rs new file mode 100644 index 00000000..e04e994a --- /dev/null +++ b/src/api/v1/tests/protected.rs @@ -0,0 +1,75 @@ +/* +* Copyright (C) 2021 Aravinth Manivannan +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +use actix_web::http::StatusCode; +use actix_web::test; + +use crate::data::Data; +use crate::*; + +use crate::tests::*; + +#[actix_rt::test] +async fn protected_routes_work() { + const NAME: &str = "testuser619"; + const PASSWORD: &str = "longpassword2"; + const EMAIL: &str = "testuser119@a.com2"; + + let _post_protected_urls = [ + "/api/v1/account/secret/", + "/api/v1/account/email/", + "/api/v1/account/delete", + "/api/v1/mcaptcha/levels/add", + "/api/v1/mcaptcha/levels/update", + "/api/v1/mcaptcha/levels/delete", + "/api/v1/mcaptcha/levels/get", + "/api/v1/mcaptcha/domain/token/duration/update", + "/api/v1/mcaptcha/domain/token/duration/get", + "/api/v1/mcaptcha/add", + "/api/v1/mcaptcha/update/key", + "/api/v1/mcaptcha/get", + "/api/v1/mcaptcha/delete", + ]; + + let get_protected_urls = ["/logout"]; + + { + let data = Data::new().await; + delete_user(NAME, &data).await; + } + + let (data, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await; + let cookies = get_cookie!(signin_resp); + let mut app = get_app!(data).await; + + for url in get_protected_urls.iter() { + let resp = + test::call_service(&mut app, test::TestRequest::get().uri(url).to_request()).await; + assert_eq!(resp.status(), StatusCode::FOUND); + + let authenticated_resp = test::call_service( + &mut app, + test::TestRequest::get() + .uri(url) + .cookie(cookies.clone()) + .to_request(), + ) + .await; + + assert_eq!(authenticated_resp.status(), StatusCode::OK); + } +} diff --git a/src/errors.rs b/src/errors.rs index 80cb548a..ca961d84 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -53,9 +53,6 @@ pub enum ServiceError { #[display(fmt = "Username not found")] UsernameNotFound, - #[display(fmt = "Authorization required")] - AuthorizationRequired, - /// when the value passed contains profainity #[display(fmt = "Can't allow profanity in usernames")] ProfainityError, @@ -117,7 +114,6 @@ impl ResponseError for ServiceError { ServiceError::NotAUrl => StatusCode::BAD_REQUEST, ServiceError::WrongPassword => StatusCode::UNAUTHORIZED, ServiceError::UsernameNotFound => StatusCode::NOT_FOUND, - ServiceError::AuthorizationRequired => StatusCode::UNAUTHORIZED, ServiceError::ProfainityError => StatusCode::BAD_REQUEST, ServiceError::BlacklistError => StatusCode::BAD_REQUEST, @@ -155,18 +151,21 @@ impl From for ServiceError { } impl From for ServiceError { + #[cfg(not(tarpaulin_include))] fn from(_: ValidationErrors) -> ServiceError { ServiceError::NotAnEmail } } impl From for ServiceError { + #[cfg(not(tarpaulin_include))] fn from(_: ParseError) -> ServiceError { ServiceError::NotAUrl } } impl From for ServiceError { + #[cfg(not(tarpaulin_include))] fn from(e: CaptchaError) -> ServiceError { ServiceError::CaptchaError(e) }