mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2026-02-11 10:05:41 +00:00
errorable and seperated runner methods for auth
This commit is contained in:
@@ -18,7 +18,7 @@
|
|||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
|
||||||
use super::auth::Password;
|
use super::auth::runners::Password;
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::AppData;
|
use crate::AppData;
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use actix_web::test;
|
|||||||
|
|
||||||
use super::email::*;
|
use super::email::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::api::v1::auth::*;
|
use crate::api::v1::auth::runners::Password;
|
||||||
use crate::api::v1::ROUTES;
|
use crate::api::v1::ROUTES;
|
||||||
use crate::data::Data;
|
use crate::data::Data;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|||||||
@@ -14,12 +14,10 @@
|
|||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
use actix_web::http::header;
|
use actix_web::http::header;
|
||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
use log::debug;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::mcaptcha::get_random;
|
use super::mcaptcha::get_random;
|
||||||
@@ -47,11 +45,10 @@ pub mod routes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
pub mod runners {
|
||||||
cfg.service(signup);
|
use std::borrow::Cow;
|
||||||
cfg.service(signin);
|
|
||||||
cfg.service(signout);
|
use super::*;
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct Register {
|
pub struct Register {
|
||||||
@@ -72,11 +69,36 @@ pub struct Password {
|
|||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.auth.register")]
|
/// returns Ok(()) when everything checks out and the user is authenticated. Erros otherwise
|
||||||
async fn signup(
|
pub async fn login_runner(payload: &Login, data: &AppData) -> ServiceResult<()> {
|
||||||
payload: web::Json<Register>,
|
use argon2_creds::Config;
|
||||||
data: AppData,
|
use sqlx::Error::RowNotFound;
|
||||||
) -> ServiceResult<impl Responder> {
|
|
||||||
|
let rec = sqlx::query_as!(
|
||||||
|
Password,
|
||||||
|
r#"SELECT password FROM mcaptcha_users WHERE name = ($1)"#,
|
||||||
|
&payload.username,
|
||||||
|
)
|
||||||
|
.fetch_one(&data.db)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match rec {
|
||||||
|
Ok(s) => {
|
||||||
|
if Config::verify(&s.password, &payload.password)? {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ServiceError::WrongPassword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(RowNotFound) => Err(ServiceError::UsernameNotFound),
|
||||||
|
Err(_) => Err(ServiceError::InternalServerError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn register_runner(
|
||||||
|
payload: &Register,
|
||||||
|
data: &AppData,
|
||||||
|
) -> ServiceResult<()> {
|
||||||
if !crate::SETTINGS.server.allow_registration {
|
if !crate::SETTINGS.server.allow_registration {
|
||||||
return Err(ServiceError::ClosedForRegistration);
|
return Err(ServiceError::ClosedForRegistration);
|
||||||
}
|
}
|
||||||
@@ -135,39 +157,33 @@ async fn signup(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||||
|
cfg.service(register);
|
||||||
|
cfg.service(login);
|
||||||
|
cfg.service(signout);
|
||||||
|
}
|
||||||
|
#[my_codegen::post(path = "crate::V1_API_ROUTES.auth.register")]
|
||||||
|
async fn register(
|
||||||
|
payload: web::Json<runners::Register>,
|
||||||
|
data: AppData,
|
||||||
|
) -> ServiceResult<impl Responder> {
|
||||||
|
runners::register_runner(&payload, &data).await?;
|
||||||
Ok(HttpResponse::Ok())
|
Ok(HttpResponse::Ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.auth.login")]
|
#[my_codegen::post(path = "crate::V1_API_ROUTES.auth.login")]
|
||||||
async fn signin(
|
async fn login(
|
||||||
id: Identity,
|
id: Identity,
|
||||||
payload: web::Json<Login>,
|
payload: web::Json<runners::Login>,
|
||||||
data: AppData,
|
data: AppData,
|
||||||
) -> ServiceResult<impl Responder> {
|
) -> ServiceResult<impl Responder> {
|
||||||
use argon2_creds::Config;
|
runners::login_runner(&payload, &data).await?;
|
||||||
use sqlx::Error::RowNotFound;
|
|
||||||
|
|
||||||
let rec = sqlx::query_as!(
|
|
||||||
Password,
|
|
||||||
r#"SELECT password FROM mcaptcha_users WHERE name = ($1)"#,
|
|
||||||
&payload.username,
|
|
||||||
)
|
|
||||||
.fetch_one(&data.db)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match rec {
|
|
||||||
Ok(s) => {
|
|
||||||
if Config::verify(&s.password, &payload.password)? {
|
|
||||||
debug!("remembered {}", payload.username);
|
|
||||||
id.remember(payload.into_inner().username);
|
id.remember(payload.into_inner().username);
|
||||||
Ok(HttpResponse::Ok())
|
Ok(HttpResponse::Ok())
|
||||||
} else {
|
|
||||||
Err(ServiceError::WrongPassword)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(RowNotFound) => Err(ServiceError::UsernameNotFound),
|
|
||||||
Err(_) => Err(ServiceError::InternalServerError),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[my_codegen::get(path = "crate::V1_API_ROUTES.auth.logout", wrap = "crate::CheckLogin")]
|
#[my_codegen::get(path = "crate::V1_API_ROUTES.auth.logout", wrap = "crate::CheckLogin")]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
use actix_web::http::{header, StatusCode};
|
use actix_web::http::{header, StatusCode};
|
||||||
use actix_web::test;
|
use actix_web::test;
|
||||||
|
|
||||||
use crate::api::v1::auth::*;
|
use crate::api::v1::auth::runners::{Login, Register};
|
||||||
use crate::api::v1::ROUTES;
|
use crate::api::v1::ROUTES;
|
||||||
use crate::data::Data;
|
use crate::data::Data;
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
@@ -57,17 +57,6 @@ async fn auth_works() {
|
|||||||
let (_, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
|
let (_, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
|
||||||
let cookies = get_cookie!(signin_resp);
|
let cookies = get_cookie!(signin_resp);
|
||||||
|
|
||||||
// // check if update user secret works
|
|
||||||
// let resp = test::call_service(
|
|
||||||
// &mut app,
|
|
||||||
// test::TestRequest::post()
|
|
||||||
// .cookie(cookies.clone())
|
|
||||||
// .uri(GET_SECRET)
|
|
||||||
// .to_request(),
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// assert_eq!(resp.status(), StatusCode::OK);
|
|
||||||
|
|
||||||
// 2. check if duplicate username is allowed
|
// 2. check if duplicate username is allowed
|
||||||
let msg = Register {
|
let msg = Register {
|
||||||
username: NAME.into(),
|
username: NAME.into(),
|
||||||
@@ -86,7 +75,7 @@ async fn auth_works() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// 3. sigining in with non-existent user
|
// 3. sigining in with non-existent user
|
||||||
let mut login = Login {
|
let mut creds = Login {
|
||||||
username: "nonexistantuser".into(),
|
username: "nonexistantuser".into(),
|
||||||
password: msg.password.clone(),
|
password: msg.password.clone(),
|
||||||
};
|
};
|
||||||
@@ -94,21 +83,36 @@ async fn auth_works() {
|
|||||||
NAME,
|
NAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
ROUTES.auth.login,
|
ROUTES.auth.login,
|
||||||
&login,
|
&creds,
|
||||||
ServiceError::UsernameNotFound,
|
ServiceError::UsernameNotFound,
|
||||||
StatusCode::NOT_FOUND,
|
StatusCode::NOT_FOUND,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let resp = test::call_service(
|
||||||
|
&mut app,
|
||||||
|
post_request!(&creds, PAGES.auth.login).to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
|
creds.username = NAME.into();
|
||||||
|
let resp = test::call_service(
|
||||||
|
&mut app,
|
||||||
|
post_request!(&creds, PAGES.auth.login).to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
// 4. trying to signin with wrong password
|
// 4. trying to signin with wrong password
|
||||||
login.username = NAME.into();
|
creds.username = NAME.into();
|
||||||
login.password = NAME.into();
|
creds.password = NAME.into();
|
||||||
|
|
||||||
bad_post_req_test(
|
bad_post_req_test(
|
||||||
NAME,
|
NAME,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
ROUTES.auth.login,
|
ROUTES.auth.login,
|
||||||
&login,
|
&creds,
|
||||||
ServiceError::WrongPassword,
|
ServiceError::WrongPassword,
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ mod data;
|
|||||||
mod docs;
|
mod docs;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod middleware;
|
mod middleware;
|
||||||
|
#[macro_use]
|
||||||
mod pages;
|
mod pages;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod routes;
|
mod routes;
|
||||||
|
|||||||
@@ -15,21 +15,36 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::PAGES;
|
use actix_identity::Identity;
|
||||||
use actix_web::{HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use my_codegen::get;
|
use my_codegen::get;
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::TemplateOnce;
|
||||||
|
|
||||||
|
use crate::api::v1::auth::runners;
|
||||||
|
use crate::pages::errors::Errorable;
|
||||||
|
use crate::AppData;
|
||||||
|
use crate::PAGES;
|
||||||
|
|
||||||
#[derive(Clone, TemplateOnce)]
|
#[derive(Clone, TemplateOnce)]
|
||||||
#[template(path = "auth/login/index.html")]
|
#[template(path = "auth/login/index.html")]
|
||||||
struct IndexPage;
|
struct IndexPage {
|
||||||
|
username: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::ImplErrorable!(IndexPage);
|
||||||
|
|
||||||
const PAGE: &str = "Login";
|
const PAGE: &str = "Login";
|
||||||
|
|
||||||
impl Default for IndexPage {
|
impl Default for IndexPage {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
IndexPage
|
IndexPage {
|
||||||
|
username: None,
|
||||||
|
password: None,
|
||||||
|
error: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,3 +58,28 @@ pub async fn login() -> impl Responder {
|
|||||||
.content_type("text/html; charset=utf-8")
|
.content_type("text/html; charset=utf-8")
|
||||||
.body(&*INDEX)
|
.body(&*INDEX)
|
||||||
}
|
}
|
||||||
|
#[my_codegen::post(path = "PAGES.auth.login")]
|
||||||
|
async fn login_post(
|
||||||
|
id: Identity,
|
||||||
|
payload: web::Json<runners::Login>,
|
||||||
|
data: AppData,
|
||||||
|
) -> impl Responder {
|
||||||
|
match runners::login_runner(&payload, &data).await {
|
||||||
|
Ok(_) => {
|
||||||
|
id.remember(payload.into_inner().username);
|
||||||
|
HttpResponse::Ok().into()
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let payload = payload.into_inner();
|
||||||
|
let username = Some(payload.username);
|
||||||
|
let password = Some(payload.password);
|
||||||
|
|
||||||
|
let page = IndexPage {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
page.get_error_resp(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pub mod register;
|
|||||||
|
|
||||||
pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
|
pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
|
||||||
cfg.service(login::login);
|
cfg.service(login::login);
|
||||||
|
cfg.service(login::login_post);
|
||||||
cfg.service(register::join);
|
cfg.service(register::join);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,37 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{error::ResponseError, web, HttpResponse, Responder};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::TemplateOnce;
|
||||||
|
|
||||||
use crate::errors::PageError;
|
use crate::errors::PageError;
|
||||||
|
|
||||||
|
pub trait Errorable: TemplateOnce {
|
||||||
|
fn get_error_resp<E: ResponseError>(self, e: E) -> HttpResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ImplErrorable {
|
||||||
|
($struct:ident) => {
|
||||||
|
impl crate::pages::errors::Errorable for $struct {
|
||||||
|
fn get_error_resp<E>(mut self, e: E) -> actix_web::HttpResponse
|
||||||
|
where
|
||||||
|
E: actix_web::error::ResponseError + std::fmt::Display,
|
||||||
|
//R: actix_web::Responder
|
||||||
|
{
|
||||||
|
self.error = Some(e.to_string());
|
||||||
|
let page = self.render_once().unwrap();
|
||||||
|
println!("status code: {}", e.status_code());
|
||||||
|
actix_web::dev::HttpResponseBuilder::new(e.status_code())
|
||||||
|
.content_type("text/html; charset=utf-8")
|
||||||
|
.body(&page)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, TemplateOnce)]
|
#[derive(Clone, TemplateOnce)]
|
||||||
#[template(path = "errors/index.html")]
|
#[template(path = "errors/index.html")]
|
||||||
struct ErrorPage<'a> {
|
struct ErrorPage<'a> {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use libmcaptcha::defense::Level;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::api::v1::auth::{Login, Register};
|
use crate::api::v1::auth::runners::{Login, Register};
|
||||||
use crate::api::v1::mcaptcha::captcha::MCaptchaDetails;
|
use crate::api::v1::mcaptcha::captcha::MCaptchaDetails;
|
||||||
use crate::api::v1::mcaptcha::levels::AddLevels;
|
use crate::api::v1::mcaptcha::levels::AddLevels;
|
||||||
use crate::api::v1::ROUTES;
|
use crate::api::v1::ROUTES;
|
||||||
|
|||||||
Reference in New Issue
Block a user