FEA: matrix authentication
This commit is contained in:
parent
d2cbfa3cb5
commit
5dfcf27339
|
@ -0,0 +1,3 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
alter table users drop column username;
|
||||
drop table login_attempts_usernames;
|
|
@ -0,0 +1,15 @@
|
|||
-- Your SQL goes here
|
||||
alter table users
|
||||
add username text;
|
||||
|
||||
create unique index users_username_uindex
|
||||
on users (username);
|
||||
|
||||
create table login_attempts_usernames
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint login_attempts_usernames_pkey
|
||||
primary key,
|
||||
username text not null,
|
||||
timestamp timestamp default now() not null
|
||||
);
|
|
@ -1 +1 @@
|
|||
v0.2-3-g1b1d458
|
||||
v0.2-5-gd2cbfa3
|
||||
|
|
|
@ -32,6 +32,30 @@ pub fn login_attempts_exceeded(settings: &State<Settings>, email: String) -> Res
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks if maximum login attempts for username exceeded. Locks account if exceeded
|
||||
/// Returns:
|
||||
/// * Ok(true) if login attempts exceeded and account got locked
|
||||
/// * Ok(false) if login attempts not exceeded
|
||||
/// * Err(diesel::result::Error) if database error occured
|
||||
pub fn login_attempts_usernames_exceeded(settings: &State<Settings>, username: String) -> Result<bool, diesel::result::Error>{
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let result : Result<LoginAttemptsResult, diesel::result::Error> = sql_query("SELECT COUNT(*) AS count, MAX(timestamp) AS last_timestamp FROM login_attempts_usernames WHERE username=$1 AND timestamp > (now() - interval '1800 seconds')").bind::<Text, _>(username.clone()).get_result(&connection);
|
||||
let result = match result{
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
error!("Couldn't check for login attempts: {}", e);
|
||||
return Err(e)
|
||||
}
|
||||
};
|
||||
|
||||
if result.count > settings.application.max_login_attempts as i64 {
|
||||
Ok(true)
|
||||
}else{
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_login_attempt(settings: &State<Settings>, email2: String) -> Result<(), diesel::result::Error>{
|
||||
use crate::schema::login_attempts::dsl::{login_attempts, email};
|
||||
let connection = establish_connection(settings);
|
||||
|
@ -43,4 +67,17 @@ pub(crate) fn add_login_attempt(settings: &State<Settings>, email2: String) -> R
|
|||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_login_attempt_username(settings: &State<Settings>, username2: String) -> Result<(), diesel::result::Error>{
|
||||
use crate::schema::login_attempts_usernames::dsl::*;
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
match diesel::insert_into(login_attempts_usernames).values(username.eq(username2)).execute(&connection){
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!("Couldn't write login attempt into DB! {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,17 @@ pub fn get_user_by_email(email: String, settings: &State<Settings>) -> Option<Us
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_user_by_username(username: String, settings: &State<Settings>) -> Option<User> {
|
||||
use crate::schema::users::dsl::username as username1;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
match users.filter(username1.eq(username)).first(&connection) {
|
||||
Ok(user) => Some(user),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to get user associated to member entity id
|
||||
/// Returns:
|
||||
/// * Ok<Some<User>> if user found
|
||||
|
@ -31,7 +42,7 @@ pub fn get_user_by_member(settings: &State<Settings>, member_uuid: uuid::Uuid) -
|
|||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let user : Result<User, diesel::result::Error> = members.inner_join(crate::schema::users::table).filter(entity_id.eq(member_uuid)).select((id, password, email)).first(&connection);
|
||||
let user : Result<User, diesel::result::Error> = members.inner_join(crate::schema::users::table).filter(entity_id.eq(member_uuid)).select((id, password, email, username)).first(&connection);
|
||||
match user{
|
||||
Ok(user) => Ok(Some(user)),
|
||||
Err(e) => {
|
||||
|
|
|
@ -3,4 +3,5 @@ pub struct User {
|
|||
pub(crate) id: uuid::Uuid,
|
||||
pub(crate) password: Option<String>,
|
||||
pub(crate) email: String,
|
||||
pub(crate) username: Option<String>,
|
||||
}
|
||||
|
|
|
@ -136,6 +136,7 @@ mod tests {
|
|||
id: uuid,
|
||||
password: None,
|
||||
email: "test@test.de".to_string(),
|
||||
username: None
|
||||
};
|
||||
|
||||
let inserted_cookie = storage.add(
|
||||
|
@ -161,6 +162,7 @@ mod tests {
|
|||
id: uuid,
|
||||
password: None,
|
||||
email: "test@test.de".to_string(),
|
||||
username: None
|
||||
};
|
||||
|
||||
let inserted_cookie = storage.add(
|
||||
|
@ -187,6 +189,7 @@ mod tests {
|
|||
id: uuid,
|
||||
password: None,
|
||||
email: "test@test.de".to_string(),
|
||||
username: None
|
||||
};
|
||||
|
||||
let inserted_cookie = storage.add(
|
||||
|
@ -210,6 +213,7 @@ mod tests {
|
|||
id: uuid,
|
||||
password: None,
|
||||
email: "test@test.de".to_string(),
|
||||
username: None
|
||||
};
|
||||
|
||||
storage.add(
|
||||
|
|
|
@ -209,6 +209,7 @@ fn main() {
|
|||
modules::api::events::instances::delete::delete_instance,
|
||||
modules::api::events::event_units::position::read::get_check_position_requirements,
|
||||
modules::api::events::read::read_future_event_for_member,
|
||||
modules::api::users::read::matrix_check_credentials,
|
||||
],
|
||||
)
|
||||
.mount("/css", StaticFiles::from("resources/css"))
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
use rocket_contrib::json::Json;
|
||||
use rocket::State;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::database::controller::users::get_user_by_username;
|
||||
use crate::database::controller::login_protection::{login_attempts_exceeded, login_attempts_usernames_exceeded, add_login_attempt, add_login_attempt_username};
|
||||
use crate::database::controller::members::get_members_by_user_uuid;
|
||||
|
||||
#[derive(Queryable, Clone, Deserialize, Serialize)]
|
||||
pub struct Request{
|
||||
user: User,
|
||||
}
|
||||
|
||||
#[derive(Queryable, Clone, Deserialize, Serialize)]
|
||||
pub struct User{
|
||||
id: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[derive(Queryable, Clone, Deserialize, Serialize)]
|
||||
pub struct Profile{
|
||||
display_name: String
|
||||
}
|
||||
|
||||
#[derive(Queryable, Clone, Deserialize, Serialize)]
|
||||
pub struct Auth{
|
||||
success: bool,
|
||||
mxid: Option<String>,
|
||||
profile: Option<Profile>,
|
||||
}
|
||||
|
||||
/// Matrix integration
|
||||
#[post("/_matrix-internal/identity/v1/check_credentials", format = "json", data="<auth>")]
|
||||
pub fn matrix_check_credentials(
|
||||
settings: State<Settings>, auth: Json<Request>
|
||||
) -> Json<Auth> {
|
||||
let id = auth.user.id.replace("@", "").replace(":drk.digital", "");
|
||||
match get_user_by_username(id.clone(), &settings){
|
||||
None => {
|
||||
match login_attempts_usernames_exceeded(&settings, id.clone()){
|
||||
Ok(result) => {
|
||||
if result{
|
||||
return Json(Auth{
|
||||
success: false,
|
||||
mxid: None,
|
||||
profile: None
|
||||
})
|
||||
}else{
|
||||
add_login_attempt_username(&settings, id);
|
||||
return Json(Auth{
|
||||
success: false,
|
||||
mxid: None,
|
||||
profile: None
|
||||
})
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
return Json(Auth{
|
||||
success: false,
|
||||
mxid: None,
|
||||
profile: None
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(user) => {
|
||||
let user = match login_attempts_usernames_exceeded(&settings, id.clone()){
|
||||
Ok(result) => {
|
||||
if result{
|
||||
return Json(Auth{
|
||||
success: false,
|
||||
mxid: None,
|
||||
profile: None
|
||||
})
|
||||
}else{
|
||||
user
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
return Json(Auth{
|
||||
success: false,
|
||||
mxid: None,
|
||||
profile: None
|
||||
})
|
||||
}
|
||||
};
|
||||
let password_hash = match user.password.clone(){
|
||||
None => {return Json(Auth{
|
||||
success: false,
|
||||
mxid: None,
|
||||
profile: None
|
||||
})}
|
||||
Some(pw) => pw
|
||||
};
|
||||
if argon2::verify_encoded(&password_hash, auth.user.password.as_ref()).unwrap() {
|
||||
let member = match get_members_by_user_uuid(user.id, &settings).first(){
|
||||
Some(member) => {
|
||||
return Json(Auth{
|
||||
success: true,
|
||||
mxid: Some(format!("@{}:drk.digital", id)),
|
||||
profile: Some(Profile{
|
||||
display_name: format!("{} {}", member.firstname, member.lastname)
|
||||
})
|
||||
})
|
||||
},
|
||||
None => return Json(Auth{
|
||||
success: false,
|
||||
mxid: None,
|
||||
profile: None
|
||||
})
|
||||
};
|
||||
} else {
|
||||
add_login_attempt_username(&settings, id);
|
||||
return Json(Auth{
|
||||
success: false,
|
||||
mxid: None,
|
||||
profile: None
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ use crate::helper::session_cookies::model::SessionCookieStorage;
|
|||
use crate::modules::welcome::model::login_error_type::LoginError;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::welcome::model::login_form::LoginForm;
|
||||
use crate::database::controller::login_protection::add_login_attempt;
|
||||
use crate::database::controller::login_protection::{add_login_attempt, add_login_attempt_username};
|
||||
use chrono::{Duration, Utc};
|
||||
use rocket::http::{Cookie, Cookies};
|
||||
use rocket::State;
|
||||
|
@ -30,7 +30,7 @@ pub fn check_login(login_form: LoginForm, settings: &State<Settings>) -> Result<
|
|||
if result{
|
||||
return Err(LoginError::MaxLoginAttemptsExceeded)
|
||||
}else{
|
||||
match add_login_attempt(settings, login_form.login_email.to_lowercase()){
|
||||
match add_login_attempt_username(settings, login_form.login_email.to_lowercase()){
|
||||
Ok(_) => return Err(LoginError::UserNotFound),
|
||||
Err(_) => return Err(LoginError::DatabaseError)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
addresses (id) {
|
||||
id -> Uuid,
|
||||
|
@ -15,7 +15,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
addresses_entities (address_id, entitiy_id) {
|
||||
address_id -> Uuid,
|
||||
|
@ -25,7 +25,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
appointment_types (type_id) {
|
||||
type_id -> Uuid,
|
||||
|
@ -37,7 +37,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
appointments (id) {
|
||||
id -> Uuid,
|
||||
|
@ -49,7 +49,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
buildings (entity_id) {
|
||||
entity_id -> Uuid,
|
||||
|
@ -60,7 +60,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
communication_targets (target_id) {
|
||||
target_id -> Uuid,
|
||||
|
@ -73,7 +73,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
communication_types (type_id) {
|
||||
type_id -> Uuid,
|
||||
|
@ -83,7 +83,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
cost_centres (short_id) {
|
||||
short_id -> Int4,
|
||||
|
@ -93,7 +93,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
cost_centres_members (member_entity_id, cost_centre_shortid) {
|
||||
member_entity_id -> Uuid,
|
||||
|
@ -103,7 +103,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
entities (entity_id) {
|
||||
entity_id -> Uuid,
|
||||
|
@ -112,7 +112,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
eu_instances (instance_id) {
|
||||
instance_id -> Uuid,
|
||||
|
@ -124,7 +124,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
eu_position_instances (instance_id, position_id) {
|
||||
instance_id -> Uuid,
|
||||
|
@ -135,7 +135,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
eu_positions (entity_id) {
|
||||
entity_id -> Uuid,
|
||||
|
@ -147,7 +147,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
eu_positions_templates (position_entity_id, template_id) {
|
||||
position_entity_id -> Uuid,
|
||||
|
@ -157,7 +157,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
eu_templates (entity_id) {
|
||||
entity_id -> Uuid,
|
||||
|
@ -168,7 +168,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
eu_vehicle_positions (entity_id) {
|
||||
entity_id -> Uuid,
|
||||
|
@ -181,7 +181,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
event_organisers (entity_id) {
|
||||
entity_id -> Uuid,
|
||||
|
@ -196,7 +196,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
event_types (type_id) {
|
||||
type_id -> Uuid,
|
||||
|
@ -208,7 +208,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
events (entity_id) {
|
||||
entity_id -> Uuid,
|
||||
|
@ -229,7 +229,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
groups (entity_id) {
|
||||
entity_id -> Uuid,
|
||||
|
@ -240,7 +240,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
groups_entities (group_id, entity_id) {
|
||||
group_id -> Uuid,
|
||||
|
@ -250,7 +250,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
license_categories (name) {
|
||||
name -> Text,
|
||||
|
@ -260,7 +260,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
licenses_members (member_id, license_name) {
|
||||
member_id -> Uuid,
|
||||
|
@ -271,7 +271,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
login_attempts (id) {
|
||||
id -> Uuid,
|
||||
|
@ -282,7 +282,18 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
login_attempts_usernames (id) {
|
||||
id -> Uuid,
|
||||
username -> Text,
|
||||
timestamp -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
members (entity_id) {
|
||||
entity_id -> Uuid,
|
||||
|
@ -306,7 +317,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
members_roles (member_id, role_id) {
|
||||
member_id -> Uuid,
|
||||
|
@ -316,7 +327,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
password_resets (token) {
|
||||
token -> Text,
|
||||
|
@ -327,7 +338,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
permissions (permission) {
|
||||
permission -> Text,
|
||||
|
@ -337,7 +348,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
qualification_categories (id) {
|
||||
id -> Uuid,
|
||||
|
@ -348,7 +359,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
qualifications (id) {
|
||||
id -> Uuid,
|
||||
|
@ -360,7 +371,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
qualifications_members (member_id, qualification_id) {
|
||||
member_id -> Uuid,
|
||||
|
@ -370,7 +381,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
roles (id) {
|
||||
id -> Text,
|
||||
|
@ -380,7 +391,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
roles_permissions (role_permission_id) {
|
||||
role_id -> Text,
|
||||
|
@ -391,7 +402,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
roles_permissions_context (role_permission_id, entity) {
|
||||
role_permission_id -> Uuid,
|
||||
|
@ -401,7 +412,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
units (unit_id) {
|
||||
unit_id -> Uuid,
|
||||
|
@ -411,7 +422,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
units_members (unit_id, member_id) {
|
||||
unit_id -> Uuid,
|
||||
|
@ -422,18 +433,19 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
users (id) {
|
||||
id -> Uuid,
|
||||
password -> Nullable<Text>,
|
||||
email -> Text,
|
||||
username -> Nullable<Text>,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
vehicle_categories (id) {
|
||||
id -> Uuid,
|
||||
|
@ -444,7 +456,7 @@ table! {
|
|||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
vehicles (entity_id) {
|
||||
entity_id -> Uuid,
|
||||
|
@ -525,6 +537,7 @@ allow_tables_to_appear_in_same_query!(
|
|||
license_categories,
|
||||
licenses_members,
|
||||
login_attempts,
|
||||
login_attempts_usernames,
|
||||
members,
|
||||
members_roles,
|
||||
password_resets,
|
||||
|
|
Loading…
Reference in New Issue