corund_lib/
user.rs

1use diesel::prelude::*;
2use serde::{Deserialize, Serialize};
3use serde_json;
4
5use crate::{
6    db::{Con, Id},
7    password::hash_password,
8    quco::{Collection, Query},
9    ryz::{err, res::Res},
10    schema,
11    user_change::{self, ChangeAction, NewUserChange},
12    InsertReg, Reg,
13};
14
15#[derive(Serialize, Deserialize, Debug)]
16pub struct User {
17    pub id: Id,
18    pub username: String,
19    pub firstname: Option<String>,
20    pub patronym: Option<String>,
21    pub surname: Option<String>,
22    pub rt: Option<String>,
23}
24
25#[derive(Queryable, Selectable)]
26#[diesel(table_name = schema::appuser)]
27#[diesel(check_for_backend(diesel::pg::Pg))]
28pub struct UserTable {
29    pub id: Id,
30    pub hpassword: String,
31    pub username: String,
32    pub firstname: Option<String>,
33    pub patronym: Option<String>,
34    pub surname: Option<String>,
35    pub rt: Option<String>,
36}
37
38impl Collection<User> for UserTable {
39    fn to_msg(&self) -> User {
40        User {
41            id: self.id.to_owned(),
42            username: self.username.to_owned(),
43            firstname: self.firstname.to_owned(),
44            patronym: self.patronym.to_owned(),
45            surname: self.surname.to_owned(),
46            rt: self.rt.to_owned(),
47        }
48    }
49}
50
51pub fn new(reg: &Reg, con: &mut Con) -> Res<User> {
52    if reg.username.starts_with("archive::") {
53        return err::res_msg("cannot accept archived usernames");
54    }
55    let hpassword = hash_password(&reg.password).unwrap();
56    let user: UserTable = diesel::insert_into(schema::appuser::table)
57        .values(&InsertReg {
58            username: reg.username.to_owned(),
59            hpassword: hpassword.to_owned(),
60            firstname: reg.firstname.to_owned(),
61            patronym: reg.patronym.to_owned(),
62            surname: reg.surname.to_owned(),
63        })
64        .returning(UserTable::as_returning())
65        .get_result(con)
66        .unwrap();
67
68    user_change::new(
69        &NewUserChange {
70            user_id: user.id,
71            action: ChangeAction::New,
72        },
73        con,
74    )
75    .unwrap();
76
77    Ok(user.to_msg())
78}
79
80/// Instead of deletion, users are archived, their usernames are changed to
81/// be `archive::<username>` and they are no more accessible. This needs to be
82/// done due to user_change synchronization needs, the changes will still point
83/// to the archived user.
84pub fn del(sq: &Query, con: &mut Con) -> Res<()> {
85    let id = sq.get("id");
86    let username = sq.get("username");
87    let mut q = schema::appuser::table.into_boxed();
88    if id.is_some() {
89        let val = serde_json::from_value::<Id>(id.unwrap().clone()).unwrap();
90        q = q.filter(schema::appuser::id.eq(val));
91    }
92    if username.is_some() {
93        let username =
94            serde_json::from_value::<String>(username.unwrap().clone())
95                .unwrap();
96        if username.starts_with("archive::") {
97            return err::res_msg("cannot accept archived usernames");
98        }
99        q = q.filter(schema::appuser::username.eq(username));
100    }
101
102    let username = q
103        .select(schema::appuser::username)
104        .get_result::<String>(con)
105        .unwrap();
106    let archived_username = "archived::".to_string() + username.as_str();
107
108    let id = diesel::update(schema::appuser::table)
109        .filter(schema::appuser::username.eq(username))
110        .set(schema::appuser::username.eq(archived_username))
111        .returning(schema::appuser::id)
112        .get_result::<Id>(con)
113        .unwrap();
114
115    user_change::new(
116        &NewUserChange {
117            user_id: id,
118            action: ChangeAction::Del,
119        },
120        con,
121    )
122    .unwrap();
123
124    Ok(())
125}
126
127pub fn get_by_id(id: i32, con: &mut Con) -> Res<User> {
128    Ok(schema::appuser::table
129        .filter(schema::appuser::id.eq(id))
130        .filter(schema::appuser::username.not_like("archived::%"))
131        .select(UserTable::as_select())
132        .first(con)
133        .unwrap()
134        .to_msg())
135}
136
137pub fn get_by_username(
138    username: &String,
139    con: &mut Con,
140) -> Res<(User, String)> {
141    if username.starts_with("archive::") {
142        return err::res_msg("cannot accept archived usernames");
143    }
144    let user: UserTable = schema::appuser::table
145        .filter(schema::appuser::username.eq(username))
146        .select(UserTable::as_select())
147        .first(con)
148        .unwrap();
149    Ok((user.to_msg(), user.hpassword))
150}
151
152pub fn get_by_rt(rt: &String, con: &mut Con) -> Res<(User, String)> {
153    let user: UserTable = schema::appuser::table
154        .filter(schema::appuser::rt.eq(rt))
155        .filter(schema::appuser::username.not_like("archived::%"))
156        .select(UserTable::as_select())
157        .first(con)
158        .unwrap();
159    Ok((user.to_msg(), user.hpassword))
160}
161
162pub fn del_rt(rt: &String, con: &mut Con) -> Res<()> {
163    // here we can delete rt even for archived users
164    diesel::update(schema::appuser::table.filter(schema::appuser::rt.eq(rt)))
165        .set(schema::appuser::rt.eq::<Option<String>>(None))
166        .execute(con)
167        .unwrap();
168    Ok(())
169}
170
171pub fn set_rt_for_username(
172    username: &String,
173    rt: &String,
174    con: &mut Con,
175) -> Res<()> {
176    if username.starts_with("archive::") {
177        return err::res_msg("cannot accept archived usernames");
178    }
179    diesel::update(
180        schema::appuser::table.filter(schema::appuser::username.eq(username)),
181    )
182    .set(schema::appuser::rt.eq::<Option<String>>(Some(rt.to_owned())))
183    .execute(con)
184    .unwrap();
185    Ok(())
186}
187
188pub fn get_many_as_ids(con: &mut Con) -> Res<Vec<Id>> {
189    let ids = schema::appuser::table
190        .filter(schema::appuser::username.not_like("archived::%"))
191        .select(schema::appuser::id)
192        .get_results::<Id>(con)
193        .unwrap();
194    Ok(ids)
195}