mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2025-03-14 13:08:27 +03:00
error correction, tests for err branches, rm get_token, get_token,
delete captcha
This commit is contained in:
parent
6f690734c5
commit
8f87efeeb3
14 changed files with 252 additions and 114 deletions
|
@ -56,7 +56,7 @@ async fn delete_account(
|
|||
Err(ServiceError::WrongPassword)
|
||||
}
|
||||
}
|
||||
Err(RowNotFound) => Err(ServiceError::UsernameNotFound),
|
||||
Err(RowNotFound) => Err(ServiceError::AccountNotFound),
|
||||
Err(_) => Err(ServiceError::InternalServerError),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ use crate::api::v1::ROUTES;
|
|||
use crate::data::Data;
|
||||
use crate::*;
|
||||
|
||||
use crate::errors::*;
|
||||
use crate::tests::*;
|
||||
|
||||
#[actix_rt::test]
|
||||
|
@ -144,17 +145,38 @@ async fn email_udpate_password_validation_del_userworks() {
|
|||
|
||||
assert_eq!(email_update_resp.status(), StatusCode::OK);
|
||||
|
||||
let payload = Password {
|
||||
password: creds.password,
|
||||
let mut payload = Password {
|
||||
password: NAME.into(),
|
||||
};
|
||||
bad_post_req_test(
|
||||
NAME,
|
||||
PASSWORD,
|
||||
ROUTES.account.delete,
|
||||
&payload,
|
||||
ServiceError::WrongPassword,
|
||||
StatusCode::UNAUTHORIZED,
|
||||
)
|
||||
.await;
|
||||
|
||||
payload.password = PASSWORD.into();
|
||||
let delete_user_resp = test::call_service(
|
||||
&app,
|
||||
post_request!(&payload, ROUTES.account.delete)
|
||||
.cookie(cookies.clone())
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(delete_user_resp.status(), StatusCode::OK);
|
||||
|
||||
let account_not_found_resp = test::call_service(
|
||||
&app,
|
||||
post_request!(&payload, ROUTES.account.delete)
|
||||
.cookie(cookies)
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(delete_user_resp.status(), StatusCode::OK);
|
||||
assert_eq!(account_not_found_resp.status(), StatusCode::NOT_FOUND);
|
||||
let txt: ErrorToResponse = test::read_body_json(account_not_found_resp).await;
|
||||
assert_eq!(txt.error, format!("{}", ServiceError::AccountNotFound));
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ use crate::AppData;
|
|||
pub mod routes {
|
||||
pub struct MCaptcha {
|
||||
pub delete: &'static str,
|
||||
pub get_token: &'static str,
|
||||
pub update_key: &'static str,
|
||||
}
|
||||
|
||||
|
@ -35,7 +34,6 @@ pub mod routes {
|
|||
pub const fn new() -> MCaptcha {
|
||||
MCaptcha {
|
||||
update_key: "/api/v1/mcaptcha/update/key",
|
||||
get_token: "/api/v1/mcaptcha/get",
|
||||
delete: "/api/v1/mcaptcha/delete",
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +43,6 @@ pub mod routes {
|
|||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(update_token);
|
||||
cfg.service(delete_mcaptcha);
|
||||
cfg.service(get_token);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
|
@ -163,35 +160,10 @@ async fn update_token_helper(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.mcaptcha.get_token",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn get_token(
|
||||
payload: web::Json<MCaptchaDetails>,
|
||||
data: AppData,
|
||||
id: Identity,
|
||||
) -> ServiceResult<impl Responder> {
|
||||
let username = id.identity().unwrap();
|
||||
let res = match sqlx::query_as!(
|
||||
MCaptchaDetails,
|
||||
"SELECT key, name from mcaptcha_config
|
||||
WHERE key = ($1) AND user_id = (SELECT ID FROM mcaptcha_users WHERE name = $2) ",
|
||||
&payload.key,
|
||||
&username,
|
||||
)
|
||||
.fetch_one(&data.db)
|
||||
.await
|
||||
{
|
||||
Err(sqlx::Error::RowNotFound) => Err(ServiceError::TokenNotFound),
|
||||
Ok(m) => Ok(m),
|
||||
Err(e) => {
|
||||
let e: ServiceError = e.into();
|
||||
Err(e)
|
||||
}
|
||||
}?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(res))
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct DeleteCaptcha {
|
||||
pub key: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[my_codegen::post(
|
||||
|
@ -199,21 +171,58 @@ async fn get_token(
|
|||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn delete_mcaptcha(
|
||||
payload: web::Json<MCaptchaDetails>,
|
||||
payload: web::Json<DeleteCaptcha>,
|
||||
data: AppData,
|
||||
id: Identity,
|
||||
) -> ServiceResult<impl Responder> {
|
||||
use argon2_creds::Config;
|
||||
use sqlx::Error::RowNotFound;
|
||||
|
||||
let username = id.identity().unwrap();
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM mcaptcha_config
|
||||
WHERE key = ($1) AND user_id = (SELECT ID FROM mcaptcha_users WHERE name = $2) ",
|
||||
&payload.key,
|
||||
struct PasswordID {
|
||||
password: String,
|
||||
id: i32,
|
||||
}
|
||||
|
||||
let rec = sqlx::query_as!(
|
||||
PasswordID,
|
||||
r#"SELECT ID, password FROM mcaptcha_users WHERE name = ($1)"#,
|
||||
&username,
|
||||
)
|
||||
.execute(&data.db)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok())
|
||||
.fetch_one(&data.db)
|
||||
.await;
|
||||
|
||||
match rec {
|
||||
Ok(rec) => {
|
||||
if Config::verify(&rec.password, &payload.password)? {
|
||||
sqlx::query!(
|
||||
"DELETE FROM mcaptcha_levels
|
||||
WHERE config_id = (
|
||||
SELECT config_id FROM mcaptcha_config
|
||||
WHERE key = $1 AND user_id = $2
|
||||
);",
|
||||
&payload.key,
|
||||
&rec.id,
|
||||
)
|
||||
.execute(&data.db)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM mcaptcha_config WHERE key = ($1) AND user_id = $2;",
|
||||
&payload.key,
|
||||
&rec.id,
|
||||
)
|
||||
.execute(&data.db)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok())
|
||||
} else {
|
||||
Err(ServiceError::WrongPassword)
|
||||
}
|
||||
}
|
||||
Err(RowNotFound) => Err(ServiceError::UsernameNotFound),
|
||||
Err(_) => Err(ServiceError::InternalServerError),
|
||||
}
|
||||
}
|
||||
|
||||
// Workflow:
|
||||
|
@ -235,34 +244,6 @@ mod tests {
|
|||
use crate::tests::*;
|
||||
use crate::*;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn add_mcaptcha_works() {
|
||||
const NAME: &str = "testusermcaptcha";
|
||||
const PASSWORD: &str = "longpassworddomain";
|
||||
const EMAIL: &str = "testusermcaptcha@a.com";
|
||||
|
||||
{
|
||||
let data = Data::new().await;
|
||||
delete_user(NAME, &data).await;
|
||||
}
|
||||
|
||||
// 1. add mcaptcha token
|
||||
register_and_signin(NAME, EMAIL, PASSWORD).await;
|
||||
let (data, _, signin_resp, token_key) = add_levels_util(NAME, PASSWORD).await;
|
||||
let cookies = get_cookie!(signin_resp);
|
||||
let app = get_app!(data).await;
|
||||
|
||||
// 4. delete token
|
||||
let del_token = test::call_service(
|
||||
&app,
|
||||
post_request!(&token_key, ROUTES.mcaptcha.delete)
|
||||
.cookie(cookies.clone())
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(del_token.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn update_and_get_mcaptcha_works() {
|
||||
const NAME: &str = "updateusermcaptcha";
|
||||
|
@ -292,30 +273,15 @@ mod tests {
|
|||
let updated_token: MCaptchaDetails =
|
||||
test::read_body_json(update_token_resp).await;
|
||||
|
||||
// get token key with updated key
|
||||
// get levels with udpated key
|
||||
let get_token_resp = test::call_service(
|
||||
&app,
|
||||
post_request!(&updated_token, ROUTES.mcaptcha.get_token)
|
||||
post_request!(&updated_token, ROUTES.levels.get)
|
||||
.cookie(cookies.clone())
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
// if updated key doesn't exist in databse, a non 200 result will bereturned
|
||||
assert_eq!(get_token_resp.status(), StatusCode::OK);
|
||||
|
||||
// check if they match
|
||||
let mut get_token_key: MCaptchaDetails =
|
||||
test::read_body_json(get_token_resp).await;
|
||||
assert_eq!(get_token_key.key, updated_token.key);
|
||||
|
||||
get_token_key.key = "nonexistent".into();
|
||||
|
||||
let get_nonexistent_token_resp = test::call_service(
|
||||
&app,
|
||||
post_request!(&get_token_key, ROUTES.mcaptcha.get_token)
|
||||
.cookie(cookies.clone())
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(get_nonexistent_token_resp.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,20 +30,20 @@ pub mod routes {
|
|||
|
||||
pub struct Levels {
|
||||
pub add: &'static str,
|
||||
// pub delete: &'static str,
|
||||
pub delete: &'static str,
|
||||
pub get: &'static str,
|
||||
pub update: &'static str,
|
||||
}
|
||||
|
||||
impl Levels {
|
||||
pub const fn new() -> Levels {
|
||||
let add = "/api/v1/mcaptcha/levels/add";
|
||||
let update = "/api/v1/mcaptcha/levels/update";
|
||||
// let delete = "/api/v1/mcaptcha/levels/delete";
|
||||
let get = "/api/v1/mcaptcha/levels/get";
|
||||
let add = "/api/v1/mcaptcha/add";
|
||||
let update = "/api/v1/mcaptcha/update";
|
||||
let delete = "/api/v1/mcaptcha/delete";
|
||||
let get = "/api/v1/mcaptcha/get";
|
||||
Levels {
|
||||
add,
|
||||
// delete,
|
||||
delete,
|
||||
get,
|
||||
update,
|
||||
}
|
||||
|
@ -252,6 +252,7 @@ mod tests {
|
|||
use actix_web::test;
|
||||
|
||||
use super::*;
|
||||
use crate::api::v1::mcaptcha::captcha::DeleteCaptcha;
|
||||
use crate::api::v1::ROUTES;
|
||||
use crate::data::Data;
|
||||
use crate::tests::*;
|
||||
|
@ -326,5 +327,32 @@ mod tests {
|
|||
assert_eq!(get_level_resp.status(), StatusCode::OK);
|
||||
let res_levels: Vec<Level> = test::read_body_json(get_level_resp).await;
|
||||
assert_eq!(res_levels, levels);
|
||||
|
||||
// 4. delete captcha
|
||||
let mut delete_payload = DeleteCaptcha {
|
||||
key: key.key,
|
||||
password: format!("worongpass{}", PASSWORD),
|
||||
};
|
||||
|
||||
bad_post_req_test(
|
||||
NAME,
|
||||
PASSWORD,
|
||||
ROUTES.mcaptcha.delete,
|
||||
&delete_payload,
|
||||
ServiceError::WrongPassword,
|
||||
StatusCode::UNAUTHORIZED,
|
||||
)
|
||||
.await;
|
||||
|
||||
delete_payload.password = PASSWORD.into();
|
||||
|
||||
let del_resp = test::call_service(
|
||||
&app,
|
||||
post_request!(&delete_payload, ROUTES.mcaptcha.delete)
|
||||
.cookie(cookies.clone())
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(del_resp.status(), StatusCode::OK);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,6 +102,17 @@ async fn auth_works() {
|
|||
)
|
||||
.await;
|
||||
|
||||
creds.login = "nonexistantuser@example.com".into();
|
||||
bad_post_req_test(
|
||||
NAME,
|
||||
PASSWORD,
|
||||
ROUTES.auth.login,
|
||||
&creds,
|
||||
ServiceError::AccountNotFound,
|
||||
StatusCode::NOT_FOUND,
|
||||
)
|
||||
.await;
|
||||
|
||||
// 4. trying to signin with wrong password
|
||||
creds.login = NAME.into();
|
||||
creds.password = NAME.into();
|
||||
|
|
|
@ -61,6 +61,7 @@ mod tests {
|
|||
PAGES.panel.sitekey.add,
|
||||
PAGES.panel.sitekey.list,
|
||||
PAGES.panel.notifications,
|
||||
"/sitekey/test/delete",
|
||||
];
|
||||
|
||||
for url in urls.iter() {
|
||||
|
|
45
src/pages/panel/sitekey/delete.rs
Normal file
45
src/pages/panel/sitekey/delete.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use actix_web::{HttpResponse, Responder};
|
||||
use lazy_static::lazy_static;
|
||||
use my_codegen::get;
|
||||
use sailfish::TemplateOnce;
|
||||
|
||||
use crate::PAGES;
|
||||
|
||||
#[derive(Clone, TemplateOnce)]
|
||||
#[template(path = "panel/sitekey/delete/index.html")]
|
||||
struct IndexPage;
|
||||
const PAGE: &str = "Confirm Access";
|
||||
|
||||
impl Default for IndexPage {
|
||||
fn default() -> Self {
|
||||
IndexPage
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref INDEX: String = IndexPage::default().render_once().unwrap();
|
||||
}
|
||||
|
||||
#[get(path = "PAGES.panel.sitekey.delete", wrap = "crate::CheckLogin")]
|
||||
pub async fn delete_sitekey() -> impl Responder {
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
.body(&*INDEX)
|
||||
}
|
45
src/pages/panel/sitekey/login.rs
Normal file
45
src/pages/panel/sitekey/login.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use actix_web::{HttpResponse, Responder};
|
||||
use lazy_static::lazy_static;
|
||||
use my_codegen::get;
|
||||
use sailfish::TemplateOnce;
|
||||
|
||||
use crate::PAGES;
|
||||
|
||||
#[derive(Clone, TemplateOnce)]
|
||||
#[template(path = "auth/login/index.html")]
|
||||
struct IndexPage;
|
||||
const PAGE: &str = "Login";
|
||||
|
||||
impl Default for IndexPage {
|
||||
fn default() -> Self {
|
||||
IndexPage
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref INDEX: String = IndexPage::default().render_once().unwrap();
|
||||
}
|
||||
|
||||
#[get(path = "PAGES.auth.login")]
|
||||
pub async fn login() -> impl Responder {
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
.body(&*INDEX)
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
mod add;
|
||||
mod delete;
|
||||
mod edit;
|
||||
pub mod list;
|
||||
mod view;
|
||||
|
@ -26,6 +27,7 @@ pub mod routes {
|
|||
pub add: &'static str,
|
||||
pub view: &'static str,
|
||||
pub edit: &'static str,
|
||||
pub delete: &'static str,
|
||||
}
|
||||
|
||||
impl Sitekey {
|
||||
|
@ -35,6 +37,7 @@ pub mod routes {
|
|||
add: "/sitekeys/add",
|
||||
view: "/sitekey/{key}",
|
||||
edit: "/sitekey/{key}/edit",
|
||||
delete: "/sitekey/{key}/delete",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,4 +48,5 @@ pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
|
|||
cfg.service(list::list_sitekeys);
|
||||
cfg.service(view::view_sitekey);
|
||||
cfg.service(edit::edit_sitekey);
|
||||
cfg.service(delete::delete_sitekey);
|
||||
}
|
||||
|
|
|
@ -159,6 +159,7 @@ pub async fn bad_post_req_test<T: Serialize>(
|
|||
.await;
|
||||
assert_eq!(dup_token_resp.status(), s);
|
||||
let txt: ErrorToResponse = test::read_body_json(dup_token_resp).await;
|
||||
//println!("{}", txt.error);
|
||||
assert_eq!(txt.error, format!("{}", dup_err));
|
||||
}
|
||||
|
||||
|
|
|
@ -24,20 +24,6 @@ const ROUTES = {
|
|||
emailExists: '/api/v1/account/email/exists',
|
||||
healthCheck: '/api/v1/meta/health',
|
||||
buildDetails: '/api/v1/meta/build',
|
||||
addDomain: '/api/v1/mcaptcha/domain/add',
|
||||
challengeDomain: '/api/v1/mcaptcha/domain/domain/verify/challenge/get',
|
||||
proveDomain: '/api/v1/mcaptcha/domain/domain/verify/challenge/prove',
|
||||
deleteDomain: '/api/v1/mcaptcha/domain/delete',
|
||||
addToken: '/api/v1/mcaptcha/domain/token/add',
|
||||
updateTokenKey: '/api/v1/mcaptcha/domain/token/update',
|
||||
getTokenKey: '/api/v1/mcaptcha/domain/token/get',
|
||||
deleteToken: '/api/v1/mcaptcha/domain/token/delete',
|
||||
addTokenLevels: '/api/v1/mcaptcha/domain/token/levels/add',
|
||||
updateTokenLevels: '/api/v1/mcaptcha/domain/token/levels/update',
|
||||
deleteTokenLevels: '/api/v1/mcaptcha/domain/token/levels/delete',
|
||||
getTokenLevels: '/api/v1/mcaptcha/domain/token/levels/get',
|
||||
getTokenDuration: '/api/v1/mcaptcha/domain/token/token/get',
|
||||
updateTokenDuration: '/api/v1/mcaptcha/domain/token/token/update',
|
||||
markNotificationRead: '/api/v1/notifications/read',
|
||||
};
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
/>
|
||||
</label>
|
||||
|
||||
<label class="sitekey-form__label" for="duration">
|
||||
<label class="sitekey-form__label" for="password">
|
||||
Password
|
||||
<input
|
||||
class="sitekey-form__input"
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
/>
|
||||
</label>
|
||||
|
||||
<label class="sitekey-form__label" for="username"
|
||||
<label class="sitekey-form__label" for="email"
|
||||
>Email(optional)
|
||||
<input
|
||||
class="sitekey-form__input"
|
||||
|
@ -54,7 +54,7 @@
|
|||
|
||||
</label>
|
||||
|
||||
<label for="password" class="sitekey-form__label"
|
||||
<label for="password" class="sitekey-form__label" for="password-check"
|
||||
>Re-enter Password
|
||||
<input
|
||||
class="sitekey-form__input"
|
||||
|
|
29
templates/panel/sitekey/delete/index.html
Normal file
29
templates/panel/sitekey/delete/index.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
<. include!("../../../components/headers/index.html"); .>
|
||||
<div class="tmp-layout">
|
||||
<main class="auth-main">
|
||||
<div class="auth-inner-container">
|
||||
<. include!("../../../auth/logo.html"); .>
|
||||
<form
|
||||
class="sitekey-form"
|
||||
method="POST"
|
||||
action="<.= crate::V1_API_ROUTES.auth.login .>"
|
||||
id="form"
|
||||
>
|
||||
<h1 class="form__title">
|
||||
Confirm Access
|
||||
</h1>
|
||||
<label class="sitekey-form__label" for="password">
|
||||
Password
|
||||
<input
|
||||
class="sitekey-form__input"
|
||||
type="password"
|
||||
name="password"
|
||||
id="password"
|
||||
required
|
||||
/>
|
||||
<. include!("../../../components/showPassword/index.html"); .>
|
||||
</label>
|
||||
<input type="submit" class="sitekey-form__submit" value="Confirm access" />
|
||||
</form>
|
||||
</div>
|
||||
<. include!("../../../components/footers.html"); .>
|
Loading…
Add table
Reference in a new issue