captcha repository is removed
This commit is contained in:
parent
fb1b451959
commit
81ac84043b
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
|
@ -23,6 +23,8 @@
|
|||
"URL_DATABASE": "postgresql://beteran:qwer5795QWER@192.168.50.200:25432/beteran",
|
||||
"URL_BROKER": "nats://192.168.50.200:4222",
|
||||
"QUEUE_BROKER": "bet.beteran",
|
||||
"CAPTCHA_SALT": "qwer5795QWER",
|
||||
"PASSWORD_SALT": "qwer5795QWER",
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
|
@ -47,6 +49,8 @@
|
|||
"URL_DATABASE": "postgresql://beteran:qwer5795QWER@192.168.50.200:25432/beteran",
|
||||
"URL_BROKER": "nats://192.168.50.200:4222",
|
||||
"QUEUE_BROKER": "bet.beteran",
|
||||
"CAPTCHA_SALT": "qwer5795QWER",
|
||||
"PASSWORD_SALT": "qwer5795QWER",
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
|
|
|
@ -28,7 +28,7 @@ tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
|||
tokio-cron-scheduler = { version = "0" }
|
||||
uuid = { version = "0", features = ["serde", "v4", "v5"] }
|
||||
|
||||
beteran-protobuf-rust = { git = "https://gitlab.loafle.net/bet/beteran-protobuf-rust.git", tag = "v0.1.22-snapshot" }
|
||||
beteran-common-rust = { git = "https://gitlab.loafle.net/bet/beteran-common-rust.git", tag = "v0.1.2-snapshot" }
|
||||
beteran-protobuf-rust = { git = "https://gitlab.loafle.net/bet/beteran-protobuf-rust.git", tag = "v0.1.23-snapshot" }
|
||||
beteran-common-rust = { git = "https://gitlab.loafle.net/bet/beteran-common-rust.git", tag = "v0.1.3-snapshot" }
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
DROP UNIQUE INDEX uidx_captchas_token;
|
||||
DROP TABLE captchas;
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
CREATE TABLE IF NOT EXISTS captchas (
|
||||
id UUID DEFAULT uuid_generate_v4(),
|
||||
token TEXT NOT NULL,
|
||||
security_code TEXT NOT NULL,
|
||||
expires_at BIGINT NOT NULL DEFAULT (extract(epoch from now()) * 1000),
|
||||
created_at BIGINT NOT NULL DEFAULT (extract(epoch from now()) * 1000),
|
||||
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
-- index
|
||||
CREATE UNIQUE INDEX uidx_captchas_token ON captchas (token);
|
11
src/main.rs
11
src/main.rs
|
@ -34,6 +34,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
None => "".to_string(),
|
||||
};
|
||||
|
||||
let captcha_salt = match env::var_os("CAPTCHA_SALT") {
|
||||
Some(v) => v.into_string().unwrap(),
|
||||
None => "".to_string(),
|
||||
};
|
||||
let password_salt = match env::var_os("PASSWORD_SALT") {
|
||||
Some(v) => v.into_string().unwrap(),
|
||||
None => "".to_string(),
|
||||
};
|
||||
|
||||
let manager = ConnectionManager::<PgConnection>::new(url_db);
|
||||
let pool = Pool::builder()
|
||||
.max_size(4)
|
||||
|
@ -50,6 +59,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
connection_server_broker.clone(),
|
||||
queue_server_broker.clone(),
|
||||
pool.clone(),
|
||||
captcha_salt.clone(),
|
||||
password_salt.clone(),
|
||||
);
|
||||
|
||||
println!("Server service [beteran-server-service] is started");
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
//!
|
||||
//!
|
||||
|
||||
///
|
||||
pub mod models;
|
||||
///
|
||||
pub mod repository;
|
||||
///
|
||||
pub mod schema;
|
|
@ -1,37 +0,0 @@
|
|||
use super::schema::captchas;
|
||||
use beteran_common_rust as bcr;
|
||||
|
||||
///
|
||||
#[derive(Eq, Hash, Identifiable, Queryable, PartialEq, Debug, Clone)]
|
||||
#[table_name = "captchas"]
|
||||
pub struct Captcha {
|
||||
///
|
||||
pub id: uuid::Uuid,
|
||||
///
|
||||
pub security_code: String,
|
||||
///
|
||||
pub expires_at: i64,
|
||||
///
|
||||
pub created_at: i64,
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Insertable, Debug, Clone)]
|
||||
#[table_name = "captchas"]
|
||||
pub struct NewCaptcha {
|
||||
///
|
||||
pub security_code: String,
|
||||
///
|
||||
pub expires_at: i64,
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FindAll {
|
||||
///
|
||||
pub expires_at: Option<i64>,
|
||||
///
|
||||
pub pagination: Option<bcr::models::pagination::Pagination>,
|
||||
///
|
||||
pub sorts: Option<Vec<bcr::models::pagination::Sort>>,
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
//!
|
||||
//!
|
||||
use super::{models, schema::captchas};
|
||||
use beteran_common_rust as bcr;
|
||||
use diesel::prelude::*;
|
||||
use diesel::result::Error;
|
||||
|
||||
///
|
||||
pub struct Repository {}
|
||||
|
||||
impl std::fmt::Debug for Repository {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("Repository of captchas").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Repository {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Repository {
|
||||
///
|
||||
pub fn new() -> Repository {
|
||||
Repository {}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn insert(
|
||||
&self,
|
||||
conn: &diesel::PgConnection,
|
||||
new_captcha: &models::NewCaptcha,
|
||||
) -> Result<models::Captcha, Error> {
|
||||
let captcha = diesel::insert_into(captchas::table)
|
||||
.values(new_captcha)
|
||||
.get_result::<models::Captcha>(conn)?;
|
||||
|
||||
Ok(captcha)
|
||||
}
|
||||
|
||||
///
|
||||
pub fn select(
|
||||
&self,
|
||||
conn: &diesel::PgConnection,
|
||||
id: uuid::Uuid,
|
||||
) -> Result<Option<models::Captcha>, Error> {
|
||||
match captchas::table.find(id).first::<models::Captcha>(conn) {
|
||||
Ok(m) => Ok(Some(m)),
|
||||
Err(e) => match e {
|
||||
diesel::result::Error::NotFound => Ok(None),
|
||||
_ => Err(e),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn select_all_count(
|
||||
&self,
|
||||
conn: &diesel::PgConnection,
|
||||
find_all: models::FindAll,
|
||||
) -> Result<i64, Error> {
|
||||
use captchas::dsl;
|
||||
|
||||
let mut q = captchas::table.into_boxed();
|
||||
|
||||
if let Some(sp) = find_all.expires_at {
|
||||
q = q.filter(dsl::expires_at.lt(sp));
|
||||
}
|
||||
|
||||
q.count().get_result(conn)
|
||||
}
|
||||
|
||||
///
|
||||
pub fn select_all(
|
||||
&self,
|
||||
conn: &diesel::PgConnection,
|
||||
find_all: models::FindAll,
|
||||
) -> Result<Vec<models::Captcha>, Error> {
|
||||
use captchas::dsl;
|
||||
|
||||
let mut q = captchas::table.into_boxed();
|
||||
|
||||
if let Some(sp) = find_all.expires_at {
|
||||
q = q.filter(dsl::expires_at.lt(sp));
|
||||
}
|
||||
|
||||
if let Some(p) = find_all.pagination {
|
||||
let page = p.page.unwrap_or(1);
|
||||
|
||||
if let Some(page_size) = p.page_size {
|
||||
q = q.offset(((page - 1) * page_size) as i64);
|
||||
q = q.limit(page_size as i64);
|
||||
}
|
||||
}
|
||||
if let Some(orderbys) = find_all.sorts {
|
||||
for s in orderbys {
|
||||
match s {
|
||||
bcr::models::pagination::Sort::ASC(property) => match property.as_str() {
|
||||
"expires_at" => {
|
||||
q = q.order_by(dsl::expires_at.asc());
|
||||
}
|
||||
"created_at" => {
|
||||
q = q.order_by(dsl::created_at.asc());
|
||||
}
|
||||
|
||||
_ => {}
|
||||
},
|
||||
bcr::models::pagination::Sort::DESC(property) => match property.as_str() {
|
||||
"expires_at" => {
|
||||
q = q.order_by(dsl::expires_at.desc());
|
||||
}
|
||||
"created_at" => {
|
||||
q = q.order_by(dsl::created_at.desc());
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
q.load::<models::Captcha>(conn)
|
||||
}
|
||||
|
||||
///
|
||||
pub fn delete_expired(&self, conn: &diesel::PgConnection) -> Result<u64, Error> {
|
||||
use captchas::dsl;
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
|
||||
diesel::delete(dsl::captchas.filter(dsl::expires_at.le(now)))
|
||||
.execute(conn)
|
||||
.map(|c| c as u64)
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
//!
|
||||
//!
|
||||
|
||||
table! {
|
||||
///
|
||||
captchas(id) {
|
||||
///
|
||||
id -> Uuid,
|
||||
///
|
||||
security_code -> Text,
|
||||
///
|
||||
expires_at -> BigInt,
|
||||
///
|
||||
created_at -> BigInt,
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
pub mod captcha;
|
||||
pub mod member;
|
||||
pub mod member_class;
|
||||
pub mod member_level;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
//!
|
||||
//!
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::super::super::repositories;
|
||||
use beteran_common_rust as bcr;
|
||||
use beteran_protobuf_rust as bpr;
|
||||
|
@ -13,30 +11,34 @@ use diesel::{
|
|||
use prost::Message;
|
||||
|
||||
///
|
||||
pub struct Service {
|
||||
pub struct Service<'a> {
|
||||
connection_broker: nats::asynk::Connection,
|
||||
queue_broker: String,
|
||||
pool: Pool<ConnectionManager<PgConnection>>,
|
||||
member_repository: repositories::member::repository::Repository,
|
||||
member_site_repository: repositories::member_site::repository::Repository,
|
||||
member_session_repository: repositories::member_session::repository::Repository,
|
||||
captcha_repository: repositories::captcha::repository::Repository,
|
||||
argon2_config: argon2::Config<'a>,
|
||||
captcha_salt: String,
|
||||
password_salt: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Service {
|
||||
impl std::fmt::Debug for Service<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("Service of service.member.service.identity")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Service {
|
||||
impl Service<'_> {
|
||||
///
|
||||
pub fn new(
|
||||
connection_broker: nats::asynk::Connection,
|
||||
queue_broker: String,
|
||||
pool: Pool<ConnectionManager<PgConnection>>,
|
||||
) -> Service {
|
||||
captcha_salt: String,
|
||||
password_salt: String,
|
||||
) -> Service<'static> {
|
||||
Service {
|
||||
connection_broker,
|
||||
queue_broker,
|
||||
|
@ -44,7 +46,19 @@ impl Service {
|
|||
member_repository: repositories::member::repository::Repository::new(),
|
||||
member_site_repository: repositories::member_site::repository::Repository::new(),
|
||||
member_session_repository: repositories::member_session::repository::Repository::new(),
|
||||
captcha_repository: repositories::captcha::repository::Repository::new(),
|
||||
argon2_config: argon2::Config {
|
||||
variant: argon2::Variant::Argon2i,
|
||||
version: argon2::Version::Version13,
|
||||
mem_cost: 65536,
|
||||
time_cost: 10,
|
||||
lanes: 4,
|
||||
thread_mode: argon2::ThreadMode::Parallel,
|
||||
secret: &[],
|
||||
ad: &[],
|
||||
hash_length: 32,
|
||||
},
|
||||
captcha_salt,
|
||||
password_salt,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,23 +302,23 @@ impl Service {
|
|||
}
|
||||
};
|
||||
|
||||
let site_url = match client.site_url {
|
||||
Some(site_url) => site_url,
|
||||
None => {
|
||||
return Err(bcr::error::rpc::Error::InvalidParams(
|
||||
bcr::error::rpc::InvalidParams {
|
||||
message: "invalid site_url information".to_string(),
|
||||
detail: bcr::error::rpc::InvalidParamsDetail {
|
||||
location: "request".to_string(),
|
||||
param: "client.site_url".to_string(),
|
||||
value: "".to_string(),
|
||||
error_type: bcr::error::rpc::InvalidParamsType::Required,
|
||||
message: "".to_string(),
|
||||
},
|
||||
},
|
||||
));
|
||||
}
|
||||
};
|
||||
// let site_url = match client.site_url {
|
||||
// Some(site_url) => site_url,
|
||||
// None => {
|
||||
// return Err(bcr::error::rpc::Error::InvalidParams(
|
||||
// bcr::error::rpc::InvalidParams {
|
||||
// message: "invalid site_url information".to_string(),
|
||||
// detail: bcr::error::rpc::InvalidParamsDetail {
|
||||
// location: "request".to_string(),
|
||||
// param: "client.site_url".to_string(),
|
||||
// value: "".to_string(),
|
||||
// error_type: bcr::error::rpc::InvalidParamsType::Required,
|
||||
// message: "".to_string(),
|
||||
// },
|
||||
// },
|
||||
// ));
|
||||
// }
|
||||
// };
|
||||
|
||||
let conn = self.pool.get().map_err(|e| {
|
||||
bcr::error::rpc::Error::Server(bcr::error::rpc::Server {
|
||||
|
@ -314,36 +328,37 @@ impl Service {
|
|||
})
|
||||
})?;
|
||||
|
||||
match self
|
||||
.member_site_repository
|
||||
.select_by_url(&conn, &site_url)
|
||||
.map_err(|e| {
|
||||
bcr::error::rpc::Error::Server(bcr::error::rpc::Server {
|
||||
code: bpr::protobuf::rpc::Error::SERVER_00,
|
||||
message: format!("server {}", e),
|
||||
data: None,
|
||||
})
|
||||
})? {
|
||||
Some(ms) => ms,
|
||||
None => {
|
||||
return Err(bcr::error::rpc::Error::InvalidParams(
|
||||
bcr::error::rpc::InvalidParams {
|
||||
message: "invalid site_url information".to_string(),
|
||||
detail: bcr::error::rpc::InvalidParamsDetail {
|
||||
location: "request".to_string(),
|
||||
param: "client.site_url".to_string(),
|
||||
value: "".to_string(),
|
||||
error_type: bcr::error::rpc::InvalidParamsType::None,
|
||||
message: "".to_string(),
|
||||
},
|
||||
},
|
||||
));
|
||||
}
|
||||
};
|
||||
// match self
|
||||
// .member_site_repository
|
||||
// .select_by_url(&conn, &site_url)
|
||||
// .map_err(|e| {
|
||||
// bcr::error::rpc::Error::Server(bcr::error::rpc::Server {
|
||||
// code: bpr::protobuf::rpc::Error::SERVER_00,
|
||||
// message: format!("server {}", e),
|
||||
// data: None,
|
||||
// })
|
||||
// })? {
|
||||
// Some(ms) => ms,
|
||||
// None => {
|
||||
// return Err(bcr::error::rpc::Error::InvalidParams(
|
||||
// bcr::error::rpc::InvalidParams {
|
||||
// message: "invalid site_url information".to_string(),
|
||||
// detail: bcr::error::rpc::InvalidParamsDetail {
|
||||
// location: "request".to_string(),
|
||||
// param: "client.site_url".to_string(),
|
||||
// value: "".to_string(),
|
||||
// error_type: bcr::error::rpc::InvalidParamsType::None,
|
||||
// message: "".to_string(),
|
||||
// },
|
||||
// },
|
||||
// ));
|
||||
// }
|
||||
// };
|
||||
|
||||
let mut c = captcha::Captcha::new();
|
||||
|
||||
let c = c
|
||||
.add_chars(5)
|
||||
.apply_filter(captcha::filters::Noise::new(0.1))
|
||||
.view(220, 120);
|
||||
|
||||
|
@ -359,30 +374,19 @@ impl Service {
|
|||
};
|
||||
|
||||
let security_code = c.chars_as_string();
|
||||
let expires_at = (chrono::Utc::now() + chrono::Duration::hours(2)).timestamp();
|
||||
|
||||
let new_captcha = repositories::captcha::models::NewCaptcha {
|
||||
security_code,
|
||||
expires_at,
|
||||
};
|
||||
|
||||
let inserted_captcha = self
|
||||
.captcha_repository
|
||||
.insert(&conn, &new_captcha)
|
||||
.map_err(|e| {
|
||||
bcr::error::rpc::Error::Server(bcr::error::rpc::Server {
|
||||
code: bpr::protobuf::rpc::Error::SERVER_00,
|
||||
message: format!("server {}", e),
|
||||
data: None,
|
||||
})
|
||||
})?;
|
||||
let security_code_hash = argon2::hash_encoded(
|
||||
security_code.as_bytes(),
|
||||
self.captcha_salt.as_bytes(),
|
||||
&self.argon2_config,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
message
|
||||
.respond(
|
||||
bpr::ss::member::identity::CaptchaResponse {
|
||||
error: None,
|
||||
result: Some(bpr::ss::member::identity::captcha_response::Result {
|
||||
token: inserted_captcha.id.to_string(),
|
||||
security_code_hash,
|
||||
image: image_as_base64,
|
||||
}),
|
||||
}
|
||||
|
@ -487,65 +491,28 @@ impl Service {
|
|||
}
|
||||
};
|
||||
|
||||
let captcha_id = uuid::Uuid::from_str(req.token.as_str()).map_err(|e| {
|
||||
bcr::error::rpc::Error::InvalidParams(bcr::error::rpc::InvalidParams {
|
||||
message: "invalid captcha token".to_string(),
|
||||
detail: bcr::error::rpc::InvalidParamsDetail {
|
||||
location: "request".to_string(),
|
||||
param: "token".to_string(),
|
||||
value: "".to_string(),
|
||||
error_type: bcr::error::rpc::InvalidParamsType::None,
|
||||
message: e.to_string(),
|
||||
},
|
||||
})
|
||||
})?;
|
||||
let security_code_hash = req.security_code_hash;
|
||||
let security_code = req.security_code;
|
||||
let username = req.username;
|
||||
let password = req.password;
|
||||
|
||||
let captcha = match self
|
||||
.captcha_repository
|
||||
.select(&conn, captcha_id)
|
||||
.map_err(|e| {
|
||||
bcr::error::rpc::Error::Server(bcr::error::rpc::Server {
|
||||
code: bpr::protobuf::rpc::Error::SERVER_00,
|
||||
message: format!("server {}", e),
|
||||
data: None,
|
||||
})
|
||||
})? {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return Err(bcr::error::rpc::Error::InvalidParams(
|
||||
bcr::error::rpc::InvalidParams {
|
||||
message: "invalid captcha token".to_string(),
|
||||
let security_code_matches =
|
||||
argon2::verify_encoded(security_code_hash.as_str(), security_code.as_bytes()).map_err(
|
||||
|e| {
|
||||
bcr::error::rpc::Error::InvalidParams(bcr::error::rpc::InvalidParams {
|
||||
message: "invalid security_code".to_string(),
|
||||
detail: bcr::error::rpc::InvalidParamsDetail {
|
||||
location: "request".to_string(),
|
||||
param: "token".to_string(),
|
||||
value: "".to_string(),
|
||||
param: "security_code".to_string(),
|
||||
value: security_code.clone(),
|
||||
error_type: bcr::error::rpc::InvalidParamsType::None,
|
||||
message: "".to_string(),
|
||||
message: e.to_string(),
|
||||
},
|
||||
},
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if !captcha.expires_at < chrono::Utc::now().timestamp() {
|
||||
return Err(bcr::error::rpc::Error::InvalidParams(
|
||||
bcr::error::rpc::InvalidParams {
|
||||
message: "invalid captcha token".to_string(),
|
||||
detail: bcr::error::rpc::InvalidParamsDetail {
|
||||
location: "request".to_string(),
|
||||
param: "token".to_string(),
|
||||
value: "".to_string(),
|
||||
error_type: bcr::error::rpc::InvalidParamsType::None,
|
||||
message: "captcha token is expired".to_string(),
|
||||
},
|
||||
})
|
||||
},
|
||||
));
|
||||
}
|
||||
)?;
|
||||
|
||||
if !captcha.security_code.eq(&security_code) {
|
||||
if !security_code_matches {
|
||||
return Err(bcr::error::rpc::Error::InvalidParams(
|
||||
bcr::error::rpc::InvalidParams {
|
||||
message: "invalid security_code".to_string(),
|
||||
|
|
Loading…
Reference in New Issue
Block a user