2023-11-22 13:19:31 +01:00
use std ::ops ::{ Deref , DerefMut } ;
2023-04-04 15:16:21 +02:00
2023-04-03 17:32:41 +02:00
use argon2 ::{ password_hash ::SaltString , Argon2 , PasswordHasher } ;
2023-07-31 16:33:44 +02:00
use chrono ::{ Datelike , Local , NaiveDate } ;
2023-07-11 09:16:13 +02:00
use log ::info ;
2023-04-03 22:03:45 +02:00
use rocket ::{
async_trait ,
2023-06-07 00:07:11 +02:00
http ::{ Cookie , Status } ,
2023-04-03 22:03:45 +02:00
request ::{ self , FromRequest , Outcome } ,
2023-06-07 00:07:11 +02:00
time ::{ Duration , OffsetDateTime } ,
2023-07-31 16:33:44 +02:00
Request ,
2023-04-03 22:03:45 +02:00
} ;
use serde ::{ Deserialize , Serialize } ;
2023-10-29 20:41:30 +01:00
use sqlx ::{ FromRow , Sqlite , SqlitePool , Transaction } ;
2023-04-03 16:11:26 +02:00
2024-01-22 19:05:18 +01:00
use super ::{ family ::Family , log ::Log , role ::Role , tripdetails ::TripDetails , Day } ;
2023-11-02 12:15:10 +01:00
use crate ::tera ::admin ::user ::UserEditForm ;
2023-06-08 17:23:23 +02:00
2024-01-19 00:39:15 +01:00
const RENNRUDERBEITRAG : i32 = 11000 ;
const BOAT_STORAGE : i32 = 4500 ;
const FAMILY_TWO : i32 = 30000 ;
const FAMILY_THREE_OR_MORE : i32 = 35000 ;
const STUDENT_OR_PUPIL : i32 = 8000 ;
const REGULAR : i32 = 22000 ;
2024-01-19 00:44:53 +01:00
const UNTERSTUETZEND : i32 = 2500 ;
const FOERDERND : i32 = 8500 ;
2024-01-19 00:39:15 +01:00
2024-03-15 11:41:03 +01:00
#[ derive(FromRow, Debug, Serialize, Deserialize, Clone) ]
2023-04-03 16:11:26 +02:00
pub struct User {
2023-04-04 10:44:14 +02:00
pub id : i64 ,
pub name : String ,
2023-07-24 13:01:39 +02:00
pub pw : Option < String > ,
2023-11-02 12:15:10 +01:00
pub dob : Option < String > ,
pub weight : Option < String > ,
pub sex : Option < String > ,
2023-07-24 13:01:39 +02:00
pub deleted : bool ,
2023-07-31 16:33:44 +02:00
pub last_access : Option < chrono ::NaiveDateTime > ,
2023-12-30 21:21:30 +01:00
pub member_since_date : Option < String > ,
pub birthdate : Option < String > ,
pub mail : Option < String > ,
pub nickname : Option < String > ,
pub notes : Option < String > ,
pub phone : Option < String > ,
pub address : Option < String > ,
2024-01-18 16:37:54 +01:00
pub family_id : Option < i64 > ,
2023-04-03 16:11:26 +02:00
}
2023-12-23 21:27:52 +01:00
#[ derive(Debug, Serialize, Deserialize) ]
pub struct UserWithRoles {
#[ serde(flatten) ]
pub user : User ,
pub roles : Vec < String > ,
}
impl UserWithRoles {
pub async fn from_user ( user : User , db : & SqlitePool ) -> Self {
Self {
roles : user . roles ( db ) . await ,
user ,
}
}
}
2023-09-23 22:13:48 +02:00
#[ derive(Debug, Serialize, Deserialize) ]
pub struct UserWithWaterStatus {
#[ serde(flatten) ]
pub user : User ,
pub on_water : bool ,
2023-12-23 21:27:52 +01:00
pub roles : Vec < String > ,
2023-09-23 22:13:48 +02:00
}
impl UserWithWaterStatus {
pub async fn from_user ( user : User , db : & SqlitePool ) -> Self {
Self {
on_water : user . on_water ( db ) . await ,
2023-12-23 21:27:52 +01:00
roles : user . roles ( db ) . await ,
2023-09-23 22:13:48 +02:00
user ,
}
}
}
2023-07-31 16:59:15 +02:00
impl PartialEq for User {
fn eq ( & self , other : & Self ) -> bool {
self . id = = other . id
}
}
2023-04-03 17:21:34 +02:00
#[ derive(Debug) ]
2023-04-03 16:11:26 +02:00
pub enum LoginError {
InvalidAuthenticationCombo ,
2023-07-27 22:16:12 +02:00
UserNotFound ,
2023-07-28 11:50:11 +02:00
UserDeleted ,
2023-04-03 22:03:45 +02:00
NotLoggedIn ,
2023-04-04 10:44:14 +02:00
NotAnAdmin ,
2023-04-04 15:16:21 +02:00
NotACox ,
2023-08-02 14:29:19 +02:00
NotATech ,
2023-10-24 10:16:26 +02:00
GuestNotAllowed ,
2023-04-04 10:44:14 +02:00
NoPasswordSet ( User ) ,
2023-05-03 15:59:28 +02:00
DeserializationError ,
2023-04-03 16:11:26 +02:00
}
2024-01-19 00:39:15 +01:00
#[ derive(Debug, Serialize) ]
2024-01-22 22:08:05 +01:00
pub struct Fee {
pub sum_in_cents : i32 ,
pub parts : Vec < ( String , i32 ) > ,
pub name : String ,
pub user_ids : String ,
pub paid : bool ,
2024-03-15 11:41:03 +01:00
pub users : Vec < User > ,
2024-01-19 00:39:15 +01:00
}
2024-03-06 13:27:03 +01:00
impl Default for Fee {
fn default ( ) -> Self {
Self ::new ( )
}
}
2024-01-19 00:39:15 +01:00
impl Fee {
pub fn new ( ) -> Self {
Self {
sum_in_cents : 0 ,
name : " " . into ( ) ,
parts : Vec ::new ( ) ,
2024-01-22 19:05:18 +01:00
user_ids : " " . into ( ) ,
2024-03-15 11:41:03 +01:00
users : Vec ::new ( ) ,
2024-01-22 19:05:18 +01:00
paid : false ,
2024-01-19 00:39:15 +01:00
}
}
pub fn add ( & mut self , desc : String , price_in_cents : i32 ) {
self . sum_in_cents + = price_in_cents ;
self . parts . push ( ( desc , price_in_cents ) ) ;
}
2024-01-22 19:05:18 +01:00
pub fn add_person ( & mut self , user : & User ) {
if ! self . name . is_empty ( ) {
self . name . push_str ( " + " ) ;
2024-02-21 14:46:17 +01:00
self . user_ids . push ( '&' ) ;
2024-01-22 19:05:18 +01:00
}
self . name . push_str ( & user . name ) ;
self . user_ids . push_str ( & format! ( " user_ids[]= {} " , user . id ) ) ;
2024-03-15 11:41:03 +01:00
self . users . push ( user . clone ( ) ) ;
2024-01-22 19:05:18 +01:00
}
pub fn paid ( & mut self ) {
self . paid = true ;
2024-01-19 00:39:15 +01:00
}
pub fn merge ( & mut self , fee : Fee ) {
for ( desc , price_in_cents ) in fee . parts {
self . add ( desc , price_in_cents ) ;
}
}
}
2023-04-03 16:11:26 +02:00
impl User {
2024-01-19 00:39:15 +01:00
pub async fn fee ( & self , db : & SqlitePool ) -> Option < Fee > {
if ! self . has_role ( db , " Donau Linz " ) . await {
return None ;
}
2024-02-16 13:13:57 +01:00
if self . deleted {
return None ;
}
2024-01-19 00:39:15 +01:00
let mut fee = Fee ::new ( ) ;
if let Some ( family ) = Family ::find_by_opt_id ( db , self . family_id ) . await {
for member in family . members ( db ) . await {
2024-01-22 19:05:18 +01:00
fee . add_person ( & member ) ;
if member . has_role ( db , " paid " ) . await {
fee . paid ( ) ;
2024-01-19 08:02:09 +01:00
}
2024-01-19 00:39:15 +01:00
fee . merge ( member . fee_without_families ( db ) . await ) ;
}
if family . amount_family_members ( db ) . await > 2 {
fee . add ( " Familie 3+ Personen " . into ( ) , FAMILY_THREE_OR_MORE ) ;
} else {
fee . add ( " Familie 2 Personen " . into ( ) , FAMILY_TWO ) ;
}
} else {
2024-02-21 14:46:17 +01:00
fee . add_person ( self ) ;
2024-01-22 19:05:18 +01:00
if self . has_role ( db , " paid " ) . await {
fee . paid ( ) ;
}
2024-01-19 00:39:15 +01:00
fee . merge ( self . fee_without_families ( db ) . await ) ;
}
Some ( fee )
}
async fn fee_without_families ( & self , db : & SqlitePool ) -> Fee {
let mut fee = Fee ::new ( ) ;
if ! self . has_role ( db , " Donau Linz " ) . await {
return fee ;
}
if self . has_role ( db , " Rennrudern " ) . await {
fee . add ( " Rennruderbeitrag " . into ( ) , RENNRUDERBEITRAG ) ;
}
let amount_boats = self . amount_boats ( db ) . await ;
if amount_boats > 0 {
fee . add (
format! ( " {} x Bootsplatz " , amount_boats ) ,
amount_boats * BOAT_STORAGE ,
) ;
}
2024-01-19 00:44:53 +01:00
if self . has_role ( db , " Unterstützend " ) . await {
fee . add ( " Unterstützendes Mitglied " . into ( ) , UNTERSTUETZEND ) ;
} else if self . has_role ( db , " Förderndes Mitglied " ) . await {
fee . add ( " Förderndes Mitglied " . into ( ) , FOERDERND ) ;
} else if Family ::find_by_opt_id ( db , self . family_id ) . await . is_none ( ) {
2024-01-19 00:39:15 +01:00
if self . has_role ( db , " Student " ) . await | | self . has_role ( db , " Schüler " ) . await {
fee . add ( " Schüler/Student " . into ( ) , STUDENT_OR_PUPIL ) ;
2024-03-08 13:57:45 +01:00
} else if self . has_role ( db , " Ehrenmitglied " ) . await {
fee . add ( " Ehrenmitglied " . into ( ) , 0 ) ;
2024-01-19 00:39:15 +01:00
} else {
fee . add ( " Mitgliedsbeitrag " . into ( ) , REGULAR ) ;
}
}
fee
}
pub async fn amount_boats ( & self , db : & SqlitePool ) -> i32 {
sqlx ::query! (
" SELECT COUNT(*) as count FROM boat WHERE owner = ? " ,
self . id
)
. fetch_one ( db )
. await
. unwrap ( )
. count
}
2023-12-23 21:27:52 +01:00
pub async fn has_role ( & self , db : & SqlitePool , role : & str ) -> bool {
if sqlx ::query! (
" SELECT * FROM user_role WHERE user_id=? AND role_id = (SELECT id FROM role WHERE name = ?) " ,
self . id ,
role
)
. fetch_optional ( db )
. await
. unwrap ( )
. is_some ( )
{
return true ;
}
false
}
pub async fn roles ( & self , db : & SqlitePool ) -> Vec < String > {
sqlx ::query! (
2024-03-04 16:59:44 +01:00
" SELECT r.name FROM role r JOIN user_role ur ON r.id = ur.role_id JOIN user u ON u.id = ur.user_id WHERE ur.user_id = ? AND u.deleted = 0; " ,
2023-12-23 21:27:52 +01:00
self . id
)
. fetch_all ( db )
. await
. unwrap ( )
. into_iter ( ) . map ( | r | r . name ) . collect ( )
}
pub async fn has_role_tx ( & self , db : & mut Transaction < '_ , Sqlite > , role : & str ) -> bool {
if sqlx ::query! (
" SELECT * FROM user_role WHERE user_id=? AND role_id = (SELECT id FROM role WHERE name = ?) " ,
self . id ,
role
)
. fetch_optional ( db . deref_mut ( ) )
. await
. unwrap ( )
. is_some ( )
{
return true ;
}
false
}
2023-04-10 14:25:31 +02:00
pub async fn find_by_id ( db : & SqlitePool , id : i32 ) -> Option < Self > {
2023-07-31 16:33:44 +02:00
sqlx ::query_as! (
Self ,
2023-04-24 14:34:06 +02:00
"
2024-01-18 16:37:54 +01:00
SELECT id , name , pw , deleted , last_access , dob , weight , sex , member_since_date , birthdate , mail , nickname , notes , phone , address , family_id
2023-04-04 10:44:14 +02:00
FROM user
WHERE id like ?
" ,
2023-04-24 14:34:06 +02:00
id
2023-04-04 10:44:14 +02:00
)
2023-04-24 14:34:06 +02:00
. fetch_one ( db )
. await
2023-07-31 16:33:44 +02:00
. ok ( )
2023-04-04 10:44:14 +02:00
}
2023-10-29 20:41:30 +01:00
pub async fn find_by_id_tx ( db : & mut Transaction < '_ , Sqlite > , id : i32 ) -> Option < Self > {
sqlx ::query_as! (
Self ,
"
2024-01-18 16:37:54 +01:00
SELECT id , name , pw , deleted , last_access , dob , weight , sex , member_since_date , birthdate , mail , nickname , notes , phone , address , family_id
2023-10-29 20:41:30 +01:00
FROM user
WHERE id like ?
" ,
id
)
2023-11-22 13:19:31 +01:00
. fetch_one ( db . deref_mut ( ) )
2023-10-29 20:41:30 +01:00
. await
. ok ( )
}
2023-05-24 12:11:55 +02:00
pub async fn find_by_name ( db : & SqlitePool , name : & str ) -> Option < Self > {
2023-07-31 16:33:44 +02:00
sqlx ::query_as! (
Self ,
2023-04-24 14:34:06 +02:00
"
2024-01-18 16:37:54 +01:00
SELECT id , name , pw , deleted , last_access , dob , weight , sex , member_since_date , birthdate , mail , nickname , notes , phone , address , family_id
2023-04-03 16:11:26 +02:00
FROM user
WHERE name like ?
" ,
2023-04-24 14:34:06 +02:00
name
2023-04-03 16:11:26 +02:00
)
2023-04-24 14:34:06 +02:00
. fetch_one ( db )
. await
2023-07-31 16:33:44 +02:00
. ok ( )
2023-04-03 16:11:26 +02:00
}
2023-07-27 15:00:52 +02:00
pub async fn on_water ( & self , db : & SqlitePool ) -> bool {
2023-07-30 22:59:47 +02:00
if sqlx ::query! (
2023-07-27 15:00:52 +02:00
" SELECT * FROM logbook WHERE shipmaster=? AND arrival is null " ,
self . id
)
. fetch_optional ( db )
. await
. unwrap ( )
. is_some ( )
2023-07-30 22:59:47 +02:00
{
return true ;
}
if sqlx ::query! (
" SELECT * FROM logbook JOIN rower ON rower.logbook_id=logbook.id WHERE rower_id=? AND arrival is null " ,
self . id
)
. fetch_optional ( db )
. await
. unwrap ( )
. is_some ( )
{
return true ;
}
false
2023-07-27 15:00:52 +02:00
}
2023-04-26 11:22:22 +02:00
pub async fn all ( db : & SqlitePool ) -> Vec < Self > {
2023-07-31 16:33:44 +02:00
sqlx ::query_as! (
Self ,
2023-04-26 11:22:22 +02:00
"
2024-01-18 16:37:54 +01:00
SELECT id , name , pw , deleted , last_access , dob , weight , sex , member_since_date , birthdate , mail , nickname , notes , phone , address , family_id
2023-04-26 11:22:22 +02:00
FROM user
2023-04-28 19:29:20 +02:00
WHERE deleted = 0
2023-06-06 10:08:54 +02:00
ORDER BY last_access DESC
2023-04-26 11:22:22 +02:00
"
)
. fetch_all ( db )
. await
2023-07-31 16:25:07 +02:00
. unwrap ( )
2023-04-26 11:22:22 +02:00
}
2024-03-04 23:11:44 +01:00
pub async fn all_with_role ( db : & SqlitePool , role : & Role ) -> Vec < Self > {
sqlx ::query_as! (
Self ,
"
SELECT id , name , pw , deleted , last_access , dob , weight , sex , member_since_date , birthdate , mail , nickname , notes , phone , address , family_id
FROM user u
JOIN user_role ur ON u . id = ur . user_id
WHERE ur . role_id = ? AND deleted = 0
ORDER BY name ;
" , role.id
)
. fetch_all ( db )
. await
. unwrap ( )
}
2024-01-19 08:02:09 +01:00
pub async fn all_payer_groups ( db : & SqlitePool ) -> Vec < Self > {
sqlx ::query_as! (
Self ,
"
SELECT id , name , pw , deleted , last_access , dob , weight , sex , member_since_date , birthdate , mail , nickname , notes , phone , address , family_id FROM user
WHERE family_id IS NOT NULL
GROUP BY family_id
UNION
- - Select users with a null family_id , without grouping
SELECT id , name , pw , deleted , last_access , dob , weight , sex , member_since_date , birthdate , mail , nickname , notes , phone , address , family_id FROM user
WHERE family_id IS NULL ;
"
)
. fetch_all ( db )
. await
. unwrap ( )
}
2023-11-02 12:15:10 +01:00
pub async fn ergo ( db : & SqlitePool ) -> Vec < Self > {
sqlx ::query_as! (
Self ,
"
2024-01-18 16:37:54 +01:00
SELECT id , name , pw , deleted , last_access , dob , weight , sex , member_since_date , birthdate , mail , nickname , notes , phone , address , family_id
2023-11-02 12:15:10 +01:00
FROM user
2023-11-20 23:44:08 +01:00
WHERE deleted = 0 AND dob ! = ' ' and weight ! = ' ' and sex ! = ' '
2023-11-02 16:00:41 +01:00
ORDER BY name
2023-11-02 12:15:10 +01:00
"
)
. fetch_all ( db )
. await
. unwrap ( )
}
2023-07-23 12:17:57 +02:00
pub async fn cox ( db : & SqlitePool ) -> Vec < Self > {
2023-07-31 16:33:44 +02:00
sqlx ::query_as! (
Self ,
2023-07-23 12:17:57 +02:00
"
2024-01-18 16:37:54 +01:00
SELECT id , name , pw , deleted , last_access , dob , weight , sex , member_since_date , birthdate , mail , nickname , notes , phone , address , family_id
2023-07-23 12:17:57 +02:00
FROM user
2023-12-23 21:27:52 +01:00
WHERE deleted = 0 AND ( SELECT COUNT ( * ) FROM user_role WHERE user_id = user . id AND role_id = ( SELECT id FROM role WHERE name = ' cox ' ) ) > 0
2023-07-23 12:17:57 +02:00
ORDER BY last_access DESC
"
)
. fetch_all ( db )
. await
2023-07-31 16:25:07 +02:00
. unwrap ( )
2023-07-23 12:17:57 +02:00
}
2023-12-23 21:27:52 +01:00
pub async fn create ( db : & SqlitePool , name : & str ) -> bool {
sqlx ::query! ( " INSERT INTO USER(name) VALUES (?) " , name )
. execute ( db )
. await
. is_ok ( )
2023-04-26 11:22:22 +02:00
}
2023-11-02 12:15:10 +01:00
pub async fn update ( & self , db : & SqlitePool , data : UserEditForm ) {
2024-01-18 16:37:54 +01:00
let mut family_id = data . family_id ;
if family_id . is_some_and ( | x | x = = - 1 ) {
2024-02-21 14:46:17 +01:00
family_id = Some ( Family ::insert ( db ) . await )
2024-01-18 16:37:54 +01:00
}
2023-04-26 11:22:22 +02:00
sqlx ::query! (
2024-01-18 16:37:54 +01:00
" UPDATE user SET dob = ?, weight = ?, sex = ?, member_since_date=?, birthdate=?, mail=?, nickname=?, notes=?, phone=?, address=?, family_id = ? where id = ? " ,
2023-11-02 12:15:10 +01:00
data . dob ,
data . weight ,
data . sex ,
2023-12-30 21:21:30 +01:00
data . member_since_date ,
data . birthdate ,
data . mail ,
data . nickname ,
data . notes ,
data . phone ,
data . address ,
2024-01-18 16:37:54 +01:00
family_id ,
2023-04-26 11:22:22 +02:00
self . id
)
. execute ( db )
. await
. unwrap ( ) ; //Okay, because we can only create a User of a valid id
2023-12-23 21:27:52 +01:00
// handle roles
sqlx ::query! ( " DELETE FROM user_role WHERE user_id = ? " , self . id )
. execute ( db )
. await
. unwrap ( ) ;
for role_id in data . roles . into_keys ( ) {
2024-01-22 19:05:18 +01:00
self . add_role (
db ,
& Role ::find_by_id ( db , role_id . parse ::< i32 > ( ) . unwrap ( ) )
. await
. unwrap ( ) ,
2023-12-23 21:27:52 +01:00
)
2024-01-22 19:05:18 +01:00
. await ;
2023-12-23 21:27:52 +01:00
}
2023-04-04 10:44:14 +02:00
}
2024-01-22 19:05:18 +01:00
pub async fn add_role ( & self , db : & SqlitePool , role : & Role ) {
sqlx ::query! (
" INSERT INTO user_role(user_id, role_id) VALUES (?, ?) " ,
self . id ,
role . id
)
. execute ( db )
. await
. unwrap ( ) ;
}
pub async fn remove_role ( & self , db : & SqlitePool , role : & Role ) {
sqlx ::query! (
" DELETE FROM user_role WHERE user_id = ? and role_id = ? " ,
self . id ,
role . id
)
. execute ( db )
. await
. unwrap ( ) ;
}
2023-05-24 12:11:55 +02:00
pub async fn login ( db : & SqlitePool , name : & str , pw : & str ) -> Result < Self , LoginError > {
2023-07-11 14:37:48 +02:00
let name = name . trim ( ) ; // just to make sure...
2023-04-26 12:52:19 +02:00
let Some ( user ) = User ::find_by_name ( db , name ) . await else {
2024-01-24 13:09:12 +01:00
if ! [
" n-sageder " ,
" p-hofer " ,
" m-birner " ,
" s-sollberger " ,
" d-kortschak " ,
" wwwadmin " ,
" wadminw " ,
" admin " ,
2024-01-31 10:23:33 +01:00
" m sageder " ,
" d kortschak " ,
" a almousa " ,
" p hofer " ,
" s sollberger " ,
" n sageder " ,
" wp-system " ,
" s.sollberger " ,
" m.birner " ,
2024-02-07 12:36:27 +01:00
" m-sageder " ,
" a-almousa " ,
2024-03-05 13:45:19 +01:00
" m.sageder " ,
2024-03-08 10:16:36 +01:00
" n.sageder " ,
2024-03-05 13:45:19 +01:00
" a.almousa " ,
" p.hofer " ,
" d.kortschak " ,
" [login] " ,
2024-01-24 13:09:12 +01:00
]
. contains ( & name )
{
Log ::create ( db , format! ( " Username ( {name} ) not found (tried to login) " ) ) . await ;
}
2023-04-26 12:52:19 +02:00
return Err ( LoginError ::InvalidAuthenticationCombo ) ; // Username not found
2023-04-10 14:25:31 +02:00
} ;
2023-04-03 16:11:26 +02:00
2023-04-28 19:29:20 +02:00
if user . deleted {
2023-07-28 11:50:11 +02:00
Log ::create (
db ,
format! ( " User ( {name} ) already deleted (tried to login). " ) ,
)
. await ;
2023-04-28 19:29:20 +02:00
return Err ( LoginError ::InvalidAuthenticationCombo ) ; //User existed sometime ago; has
//been deleted
}
2023-07-25 13:32:20 +02:00
if let Some ( user_pw ) = user . pw . as_ref ( ) {
let password_hash = & Self ::get_hashed_pw ( pw ) ;
if password_hash = = user_pw {
return Ok ( user ) ;
2023-07-11 09:16:13 +02:00
}
2023-07-25 13:32:20 +02:00
Log ::create ( db , format! ( " User {name} supplied the wrong PW " ) ) . await ;
Err ( LoginError ::InvalidAuthenticationCombo )
} else {
info! ( " User {name} has no PW set " ) ;
Err ( LoginError ::NoPasswordSet ( user ) )
2023-04-03 16:11:26 +02:00
}
2023-04-04 10:44:14 +02:00
}
2023-04-03 16:11:26 +02:00
2023-04-04 10:44:14 +02:00
pub async fn reset_pw ( & self , db : & SqlitePool ) {
sqlx ::query! ( " UPDATE user SET pw = null where id = ? " , self . id )
. execute ( db )
. await
2023-04-26 11:22:22 +02:00
. unwrap ( ) ; //Okay, because we can only create a User of a valid id
2023-04-04 10:44:14 +02:00
}
2023-05-24 12:11:55 +02:00
pub async fn update_pw ( & self , db : & SqlitePool , pw : & str ) {
2023-05-30 14:36:23 +02:00
let pw = Self ::get_hashed_pw ( pw ) ;
2023-04-04 10:44:14 +02:00
sqlx ::query! ( " UPDATE user SET pw = ? where id = ? " , pw , self . id )
. execute ( db )
. await
2023-04-26 11:22:22 +02:00
. unwrap ( ) ; //Okay, because we can only create a User of a valid id
}
fn get_hashed_pw ( pw : & str ) -> String {
let salt = SaltString ::from_b64 ( " dS/X5/sPEKTj4Rzs/CuvzQ " ) . unwrap ( ) ;
let argon2 = Argon2 ::default ( ) ;
argon2
. hash_password ( pw . as_bytes ( ) , & salt )
. unwrap ( )
. to_string ( )
2023-04-03 16:11:26 +02:00
}
2023-04-28 19:29:20 +02:00
2023-05-10 08:57:20 +02:00
pub async fn logged_in ( & self , db : & SqlitePool ) {
sqlx ::query! (
" UPDATE user SET last_access = CURRENT_TIMESTAMP where id = ? " ,
self . id
)
. execute ( db )
. await
. unwrap ( ) ; //Okay, because we can only create a User of a valid id
}
2023-04-28 19:29:20 +02:00
pub async fn delete ( & self , db : & SqlitePool ) {
sqlx ::query! ( " UPDATE user SET deleted=1 WHERE id=? " , self . id )
. execute ( db )
. await
. unwrap ( ) ; //Okay, because we can only create a User of a valid id
}
2023-06-08 17:23:23 +02:00
pub async fn get_days ( & self , db : & SqlitePool ) -> Vec < Day > {
let mut days = Vec ::new ( ) ;
2023-12-23 21:27:52 +01:00
for i in 0 .. self . amount_days_to_show ( db ) . await {
2023-06-08 17:23:23 +02:00
let date = ( Local ::now ( ) + chrono ::Duration ::days ( i ) ) . date_naive ( ) ;
2023-12-23 21:27:52 +01:00
if self . has_role ( db , " scheckbuch " ) . await {
2023-06-08 17:23:23 +02:00
days . push ( Day ::new_guest ( db , date , false ) . await ) ;
} else {
days . push ( Day ::new ( db , date , false ) . await ) ;
}
}
2023-12-23 21:27:52 +01:00
for date in TripDetails ::pinned_days ( db , self . amount_days_to_show ( db ) . await - 1 ) . await {
if self . has_role ( db , " scheckbuch " ) . await {
2023-06-08 17:23:23 +02:00
let day = Day ::new_guest ( db , date , true ) . await ;
if ! day . planned_events . is_empty ( ) {
days . push ( day ) ;
}
} else {
days . push ( Day ::new ( db , date , true ) . await ) ;
}
}
days
}
2023-12-23 21:27:52 +01:00
async fn amount_days_to_show ( & self , db : & SqlitePool ) -> i64 {
if self . has_role ( db , " cox " ) . await {
2023-06-08 17:23:23 +02:00
let end_of_year = NaiveDate ::from_ymd_opt ( Local ::now ( ) . year ( ) , 12 , 31 ) . unwrap ( ) ; //Ok,
//december
//has 31
//days
end_of_year
. signed_duration_since ( Local ::now ( ) . date_naive ( ) )
. num_days ( )
+ 1
} else {
6
}
}
2023-04-03 16:11:26 +02:00
}
2023-04-03 22:03:45 +02:00
#[ async_trait ]
impl < ' r > FromRequest < ' r > for User {
type Error = LoginError ;
async fn from_request ( req : & ' r Request < '_ > ) -> request ::Outcome < Self , Self ::Error > {
match req . cookies ( ) . get_private ( " loggedin_user " ) {
2023-07-27 22:16:12 +02:00
Some ( user_id ) = > match user_id . value ( ) . parse ::< i32 > ( ) {
Ok ( user_id ) = > {
2023-05-10 08:57:20 +02:00
let db = req . rocket ( ) . state ::< SqlitePool > ( ) . unwrap ( ) ;
2023-07-27 22:16:12 +02:00
let Some ( user ) = User ::find_by_id ( db , user_id ) . await else {
2024-01-10 14:08:15 +01:00
return Outcome ::Error ( ( Status ::Forbidden , LoginError ::UserNotFound ) ) ;
2023-07-27 22:16:12 +02:00
} ;
2023-07-28 11:50:11 +02:00
if user . deleted {
2024-01-10 14:08:15 +01:00
return Outcome ::Error ( ( Status ::Forbidden , LoginError ::UserDeleted ) ) ;
2023-07-28 11:50:11 +02:00
}
2023-05-10 08:57:20 +02:00
user . logged_in ( db ) . await ;
2023-06-07 00:07:11 +02:00
2023-07-27 22:16:12 +02:00
let mut cookie = Cookie ::new ( " loggedin_user " , format! ( " {} " , user . id ) ) ;
2024-01-10 14:08:15 +01:00
cookie . set_expires ( OffsetDateTime ::now_utc ( ) + Duration ::weeks ( 2 ) ) ;
2023-06-07 00:07:11 +02:00
req . cookies ( ) . add_private ( cookie ) ;
2023-05-10 08:57:20 +02:00
Outcome ::Success ( user )
}
2024-01-10 14:08:15 +01:00
Err ( _ ) = > Outcome ::Error ( ( Status ::Unauthorized , LoginError ::DeserializationError ) ) ,
2023-05-03 15:59:28 +02:00
} ,
2023-11-08 17:39:39 +01:00
None = > Outcome ::Error ( ( Status ::Unauthorized , LoginError ::NotLoggedIn ) ) ,
2023-04-03 22:03:45 +02:00
}
}
}
2023-08-02 14:29:19 +02:00
pub struct TechUser {
pub ( crate ) user : User ,
}
impl Deref for TechUser {
type Target = User ;
fn deref ( & self ) -> & Self ::Target {
& self . user
}
}
#[ async_trait ]
impl < ' r > FromRequest < ' r > for TechUser {
type Error = LoginError ;
async fn from_request ( req : & ' r Request < '_ > ) -> request ::Outcome < Self , Self ::Error > {
2023-12-23 21:27:52 +01:00
let db = req . rocket ( ) . state ::< SqlitePool > ( ) . unwrap ( ) ;
2023-08-02 14:29:19 +02:00
match User ::from_request ( req ) . await {
2023-12-23 21:27:52 +01:00
Outcome ::Success ( user ) = > {
if user . has_role ( db , " tech " ) . await {
Outcome ::Success ( TechUser { user } )
} else {
2024-01-10 14:08:15 +01:00
Outcome ::Error ( ( Status ::Forbidden , LoginError ::NotACox ) )
2023-12-23 21:27:52 +01:00
}
}
2023-11-08 17:39:39 +01:00
Outcome ::Error ( f ) = > Outcome ::Error ( f ) ,
2023-08-02 14:29:19 +02:00
Outcome ::Forward ( f ) = > Outcome ::Forward ( f ) ,
}
}
}
2023-04-06 08:06:50 +02:00
pub struct CoxUser {
user : User ,
}
impl Deref for CoxUser {
type Target = User ;
fn deref ( & self ) -> & Self ::Target {
& self . user
}
}
2023-12-23 21:27:52 +01:00
impl CoxUser {
pub async fn new ( db : & SqlitePool , user : User ) -> Option < Self > {
if user . has_role ( db , " cox " ) . await {
Some ( CoxUser { user } )
2023-04-06 08:06:50 +02:00
} else {
2023-12-23 21:27:52 +01:00
None
2023-04-06 08:06:50 +02:00
}
}
}
2023-04-04 10:44:14 +02:00
#[ async_trait ]
2023-04-06 08:06:50 +02:00
impl < ' r > FromRequest < ' r > for CoxUser {
2023-04-04 10:44:14 +02:00
type Error = LoginError ;
2023-04-04 15:16:21 +02:00
async fn from_request ( req : & ' r Request < '_ > ) -> request ::Outcome < Self , Self ::Error > {
2023-12-23 21:27:52 +01:00
let db = req . rocket ( ) . state ::< SqlitePool > ( ) . unwrap ( ) ;
2023-05-03 16:06:27 +02:00
match User ::from_request ( req ) . await {
2023-12-23 21:27:52 +01:00
Outcome ::Success ( user ) = > {
if user . has_role ( db , " cox " ) . await {
Outcome ::Success ( CoxUser { user } )
} else {
2024-01-10 14:08:15 +01:00
Outcome ::Error ( ( Status ::Forbidden , LoginError ::NotACox ) )
2023-12-23 21:27:52 +01:00
}
}
2023-11-08 17:39:39 +01:00
Outcome ::Error ( f ) = > Outcome ::Error ( f ) ,
2023-05-03 16:06:27 +02:00
Outcome ::Forward ( f ) = > Outcome ::Forward ( f ) ,
2023-04-04 15:16:21 +02:00
}
}
}
2023-04-06 18:57:10 +02:00
#[ derive(Debug, Serialize, Deserialize) ]
2023-04-06 08:06:50 +02:00
pub struct AdminUser {
2023-04-06 18:57:10 +02:00
pub ( crate ) user : User ,
2023-04-06 08:06:50 +02:00
}
2023-04-04 15:16:21 +02:00
#[ async_trait ]
2023-04-06 08:06:50 +02:00
impl < ' r > FromRequest < ' r > for AdminUser {
2023-04-04 15:16:21 +02:00
type Error = LoginError ;
2023-10-24 10:16:26 +02:00
async fn from_request ( req : & ' r Request < '_ > ) -> request ::Outcome < Self , Self ::Error > {
2023-12-23 21:27:52 +01:00
let db = req . rocket ( ) . state ::< SqlitePool > ( ) . unwrap ( ) ;
2023-10-24 10:16:26 +02:00
match User ::from_request ( req ) . await {
2023-12-23 21:27:52 +01:00
Outcome ::Success ( user ) = > {
if user . has_role ( db , " admin " ) . await {
Outcome ::Success ( AdminUser { user } )
} else {
2024-03-05 09:19:15 +01:00
Outcome ::Forward ( Status ::Forbidden )
2023-12-23 21:27:52 +01:00
}
}
2023-11-08 17:39:39 +01:00
Outcome ::Error ( f ) = > Outcome ::Error ( f ) ,
2023-10-24 10:16:26 +02:00
Outcome ::Forward ( f ) = > Outcome ::Forward ( f ) ,
}
}
}
#[ derive(Debug, Serialize, Deserialize) ]
2024-01-10 14:08:15 +01:00
pub struct AllowedForPlannedTripsUser ( pub ( crate ) User ) ;
#[ async_trait ]
impl < ' r > FromRequest < ' r > for AllowedForPlannedTripsUser {
type Error = LoginError ;
async fn from_request ( req : & ' r Request < '_ > ) -> request ::Outcome < Self , Self ::Error > {
let db = req . rocket ( ) . state ::< SqlitePool > ( ) . unwrap ( ) ;
match User ::from_request ( req ) . await {
Outcome ::Success ( user ) = > {
2024-02-21 14:46:17 +01:00
if user . has_role ( db , " Donau Linz " ) . await | user . has_role ( db , " scheckbuch " ) . await {
2024-01-10 14:08:15 +01:00
Outcome ::Success ( AllowedForPlannedTripsUser ( user ) )
} else {
Outcome ::Error ( ( Status ::Forbidden , LoginError ::NotACox ) )
}
}
Outcome ::Error ( f ) = > Outcome ::Error ( f ) ,
Outcome ::Forward ( f ) = > Outcome ::Forward ( f ) ,
}
}
}
2024-03-06 13:27:03 +01:00
impl From < AllowedForPlannedTripsUser > for User {
fn from ( val : AllowedForPlannedTripsUser ) -> Self {
val . 0
2024-01-10 14:08:15 +01:00
}
}
#[ derive(Debug, Serialize, Deserialize) ]
pub struct DonauLinzUser ( pub ( crate ) User ) ;
2024-03-06 13:27:03 +01:00
impl From < DonauLinzUser > for User {
fn from ( val : DonauLinzUser ) -> Self {
val . 0
2024-01-10 14:08:15 +01:00
}
}
impl Deref for DonauLinzUser {
type Target = User ;
fn deref ( & self ) -> & Self ::Target {
& self . 0
}
2023-10-24 10:16:26 +02:00
}
#[ async_trait ]
2024-01-10 14:08:15 +01:00
impl < ' r > FromRequest < ' r > for DonauLinzUser {
2023-10-24 10:16:26 +02:00
type Error = LoginError ;
2023-04-04 10:44:14 +02:00
async fn from_request ( req : & ' r Request < '_ > ) -> request ::Outcome < Self , Self ::Error > {
2023-12-23 21:27:52 +01:00
let db = req . rocket ( ) . state ::< SqlitePool > ( ) . unwrap ( ) ;
2023-05-03 16:06:27 +02:00
match User ::from_request ( req ) . await {
2023-12-23 21:27:52 +01:00
Outcome ::Success ( user ) = > {
2024-01-18 21:36:38 +01:00
if user . has_role ( db , " Donau Linz " ) . await
& & ! user . has_role ( db , " Unterstützend " ) . await
& & ! user . has_role ( db , " Förderndes Mitglied " ) . await
{
2024-01-10 14:08:15 +01:00
Outcome ::Success ( DonauLinzUser ( user ) )
2023-12-23 21:27:52 +01:00
} else {
2024-01-10 14:08:15 +01:00
Outcome ::Error ( ( Status ::Forbidden , LoginError ::NotACox ) )
2023-12-23 21:27:52 +01:00
}
}
2023-11-08 17:39:39 +01:00
Outcome ::Error ( f ) = > Outcome ::Error ( f ) ,
2023-05-03 16:06:27 +02:00
Outcome ::Forward ( f ) = > Outcome ::Forward ( f ) ,
2023-04-04 10:44:14 +02:00
}
}
}
2024-03-06 15:55:13 +01:00
#[ derive(Debug, Serialize, Deserialize) ]
pub struct SchnupperBetreuerUser ( pub ( crate ) User ) ;
impl From < SchnupperBetreuerUser > for User {
fn from ( val : SchnupperBetreuerUser ) -> Self {
val . 0
}
}
impl Deref for SchnupperBetreuerUser {
type Target = User ;
fn deref ( & self ) -> & Self ::Target {
& self . 0
}
}
#[ async_trait ]
impl < ' r > FromRequest < ' r > for SchnupperBetreuerUser {
type Error = LoginError ;
async fn from_request ( req : & ' r Request < '_ > ) -> request ::Outcome < Self , Self ::Error > {
let db = req . rocket ( ) . state ::< SqlitePool > ( ) . unwrap ( ) ;
match User ::from_request ( req ) . await {
Outcome ::Success ( user ) = > {
if user . has_role ( db , " schnupper-betreuer " ) . await {
Outcome ::Success ( SchnupperBetreuerUser ( user ) )
} else {
Outcome ::Forward ( Status ::Forbidden )
}
}
Outcome ::Error ( f ) = > Outcome ::Error ( f ) ,
Outcome ::Forward ( f ) = > Outcome ::Forward ( f ) ,
}
}
}
2024-01-19 00:39:15 +01:00
#[ derive(Debug, Serialize, Deserialize) ]
pub struct VorstandUser ( pub ( crate ) User ) ;
2024-03-06 13:27:03 +01:00
impl From < VorstandUser > for User {
fn from ( val : VorstandUser ) -> Self {
val . 0
2024-01-19 00:39:15 +01:00
}
}
impl Deref for VorstandUser {
type Target = User ;
fn deref ( & self ) -> & Self ::Target {
& self . 0
}
}
#[ async_trait ]
impl < ' r > FromRequest < ' r > for VorstandUser {
type Error = LoginError ;
async fn from_request ( req : & ' r Request < '_ > ) -> request ::Outcome < Self , Self ::Error > {
let db = req . rocket ( ) . state ::< SqlitePool > ( ) . unwrap ( ) ;
match User ::from_request ( req ) . await {
Outcome ::Success ( user ) = > {
if user . has_role ( db , " Vorstand " ) . await {
Outcome ::Success ( VorstandUser ( user ) )
} else {
2024-03-04 16:37:53 +01:00
Outcome ::Forward ( Status ::Forbidden )
2024-01-19 00:39:15 +01:00
}
}
Outcome ::Error ( f ) = > Outcome ::Error ( f ) ,
Outcome ::Forward ( f ) = > Outcome ::Forward ( f ) ,
}
}
}
2024-01-22 19:27:22 +01:00
#[ derive(Debug, Serialize, Deserialize) ]
pub struct PlannedEventUser ( pub ( crate ) User ) ;
2024-03-06 13:27:03 +01:00
impl From < PlannedEventUser > for User {
fn from ( val : PlannedEventUser ) -> Self {
val . 0
2024-01-22 19:27:22 +01:00
}
}
impl Deref for PlannedEventUser {
type Target = User ;
fn deref ( & self ) -> & Self ::Target {
& self . 0
}
}
#[ async_trait ]
impl < ' r > FromRequest < ' r > for PlannedEventUser {
type Error = LoginError ;
async fn from_request ( req : & ' r Request < '_ > ) -> request ::Outcome < Self , Self ::Error > {
let db = req . rocket ( ) . state ::< SqlitePool > ( ) . unwrap ( ) ;
match User ::from_request ( req ) . await {
Outcome ::Success ( user ) = > {
if user . has_role ( db , " planned_event " ) . await {
Outcome ::Success ( PlannedEventUser ( user ) )
} else {
Outcome ::Error ( ( Status ::Forbidden , LoginError ::NotACox ) )
}
}
Outcome ::Error ( f ) = > Outcome ::Error ( f ) ,
Outcome ::Forward ( f ) = > Outcome ::Forward ( f ) ,
}
}
}
2023-04-03 16:11:26 +02:00
#[ cfg(test) ]
mod test {
2023-12-23 21:27:52 +01:00
use std ::collections ::HashMap ;
2023-11-02 12:25:13 +01:00
use crate ::{ tera ::admin ::user ::UserEditForm , testdb } ;
2023-04-03 22:03:45 +02:00
2023-04-03 17:21:34 +02:00
use super ::User ;
2023-04-03 16:11:26 +02:00
use sqlx ::SqlitePool ;
2023-04-26 11:22:22 +02:00
#[ sqlx::test ]
fn test_find_correct_id ( ) {
let pool = testdb! ( ) ;
let user = User ::find_by_id ( & pool , 1 ) . await . unwrap ( ) ;
assert_eq! ( user . id , 1 ) ;
}
#[ sqlx::test ]
fn test_find_wrong_id ( ) {
let pool = testdb! ( ) ;
let user = User ::find_by_id ( & pool , 1337 ) . await ;
assert! ( user . is_none ( ) ) ;
}
#[ sqlx::test ]
fn test_find_correct_name ( ) {
let pool = testdb! ( ) ;
let user = User ::find_by_name ( & pool , " admin " . into ( ) ) . await . unwrap ( ) ;
assert_eq! ( user . id , 1 ) ;
}
#[ sqlx::test ]
fn test_find_wrong_name ( ) {
let pool = testdb! ( ) ;
let user = User ::find_by_name ( & pool , " name-does-not-exist " . into ( ) ) . await ;
assert! ( user . is_none ( ) ) ;
}
#[ sqlx::test ]
fn test_all ( ) {
let pool = testdb! ( ) ;
let res = User ::all ( & pool ) . await ;
assert! ( res . len ( ) > 3 ) ;
}
2023-07-23 12:17:57 +02:00
#[ sqlx::test ]
fn test_cox ( ) {
let pool = testdb! ( ) ;
let res = User ::cox ( & pool ) . await ;
2023-09-23 18:26:04 +02:00
assert_eq! ( res . len ( ) , 3 ) ;
2023-07-23 12:17:57 +02:00
}
2023-04-26 11:22:22 +02:00
#[ sqlx::test ]
fn test_succ_create ( ) {
let pool = testdb! ( ) ;
2023-12-23 21:27:52 +01:00
assert_eq! ( User ::create ( & pool , " new-user-name " . into ( ) ) . await , true ) ;
2023-04-26 11:22:22 +02:00
}
#[ sqlx::test ]
fn test_duplicate_name_create ( ) {
let pool = testdb! ( ) ;
2023-12-23 21:27:52 +01:00
assert_eq! ( User ::create ( & pool , " admin " . into ( ) ) . await , false ) ;
2023-04-26 11:22:22 +02:00
}
#[ sqlx::test ]
fn test_update ( ) {
let pool = testdb! ( ) ;
let user = User ::find_by_id ( & pool , 1 ) . await . unwrap ( ) ;
2023-11-02 12:25:13 +01:00
user . update (
& pool ,
UserEditForm {
id : 1 ,
dob : None ,
weight : None ,
2023-12-23 21:27:52 +01:00
sex : Some ( " m " . into ( ) ) ,
roles : HashMap ::new ( ) ,
2023-12-30 21:21:30 +01:00
member_since_date : None ,
birthdate : None ,
mail : None ,
nickname : None ,
notes : None ,
phone : None ,
address : None ,
2024-01-18 16:37:54 +01:00
family_id : None ,
2023-11-02 12:25:13 +01:00
} ,
)
. await ;
2023-04-26 11:22:22 +02:00
let user = User ::find_by_id ( & pool , 1 ) . await . unwrap ( ) ;
2023-12-23 21:27:52 +01:00
assert_eq! ( user . sex , Some ( " m " . into ( ) ) ) ;
2023-04-26 11:22:22 +02:00
}
2023-04-03 17:21:34 +02:00
#[ sqlx::test ]
fn succ_login_with_test_db ( ) {
2023-04-03 22:03:45 +02:00
let pool = testdb! ( ) ;
2023-04-03 17:21:34 +02:00
User ::login ( & pool , " admin " . into ( ) , " admin " . into ( ) )
. await
. unwrap ( ) ;
}
#[ sqlx::test ]
fn wrong_pw ( ) {
2023-04-03 22:03:45 +02:00
let pool = testdb! ( ) ;
2023-04-03 17:21:34 +02:00
assert! ( User ::login ( & pool , " admin " . into ( ) , " admi " . into ( ) )
. await
. is_err ( ) ) ;
}
#[ sqlx::test ]
fn wrong_username ( ) {
2023-04-03 22:03:45 +02:00
let pool = testdb! ( ) ;
2023-04-03 17:21:34 +02:00
assert! ( User ::login ( & pool , " admi " . into ( ) , " admin " . into ( ) )
. await
. is_err ( ) ) ;
2023-04-03 16:11:26 +02:00
}
2023-04-26 11:22:22 +02:00
#[ sqlx::test ]
fn reset ( ) {
let pool = testdb! ( ) ;
let user = User ::find_by_id ( & pool , 1 ) . await . unwrap ( ) ;
user . reset_pw ( & pool ) . await ;
let user = User ::find_by_id ( & pool , 1 ) . await . unwrap ( ) ;
assert_eq! ( user . pw , None ) ;
}
#[ sqlx::test ]
fn update_pw ( ) {
let pool = testdb! ( ) ;
let user = User ::find_by_id ( & pool , 1 ) . await . unwrap ( ) ;
assert! ( User ::login ( & pool , " admin " . into ( ) , " abc " . into ( ) )
. await
. is_err ( ) ) ;
user . update_pw ( & pool , " abc " . into ( ) ) . await ;
User ::login ( & pool , " admin " . into ( ) , " abc " . into ( ) )
. await
. unwrap ( ) ;
}
2023-04-03 16:11:26 +02:00
}