error correction, tests for err branches, rm get_token, get_token,

delete captcha
This commit is contained in:
realaravinth 2021-07-17 17:43:53 +05:30
parent 6f690734c5
commit 8f87efeeb3
No known key found for this signature in database
GPG key ID: AD9F0F08E855ED88
14 changed files with 252 additions and 114 deletions

View file

@ -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),
}
}

View file

@ -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));
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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() {

View 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)
}

View 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)
}

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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',
};

View file

@ -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"

View file

@ -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"

View 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"); .>