]> Untitled Git - lemmy.git/blob - server/src/db/user.rs
Adding emoji support.
[lemmy.git] / server / src / db / user.rs
1 use crate::schema::user_;
2 use crate::schema::user_::dsl::*;
3 use super::*;
4 use crate::{Settings, is_email_regex};
5 use jsonwebtoken::{encode, decode, Header, Validation, TokenData};
6 use bcrypt::{DEFAULT_COST, hash};
7
8 #[derive(Queryable, Identifiable, PartialEq, Debug)]
9 #[table_name="user_"]
10 pub struct User_ {
11   pub id: i32,
12   pub name: String,
13   pub fedi_name: String,
14   pub preferred_username: Option<String>,
15   pub password_encrypted: String,
16   pub email: Option<String>,
17   pub icon: Option<Vec<u8>>,
18   pub admin: bool,
19   pub banned: bool,
20   pub published: chrono::NaiveDateTime,
21   pub updated: Option<chrono::NaiveDateTime>,
22   pub show_nsfw: bool,
23 }
24
25 #[derive(Insertable, AsChangeset, Clone)]
26 #[table_name="user_"]
27 pub struct UserForm {
28     pub name: String,
29     pub fedi_name: String,
30     pub preferred_username: Option<String>,
31     pub password_encrypted: String,
32     pub admin: bool,
33     pub banned: bool,
34     pub email: Option<String>,
35     pub updated: Option<chrono::NaiveDateTime>,
36     pub show_nsfw: bool,
37 }
38
39 impl Crud<UserForm> for User_ {
40   fn read(conn: &PgConnection, user_id: i32) -> Result<Self, Error> {
41     use crate::schema::user_::dsl::*;
42     user_.find(user_id)
43       .first::<Self>(conn)
44   }
45   fn delete(conn: &PgConnection, user_id: i32) -> Result<usize, Error> {
46     diesel::delete(user_.find(user_id))
47       .execute(conn)
48   }
49   fn create(conn: &PgConnection, form: &UserForm) -> Result<Self, Error> {
50     insert_into(user_)
51       .values(form)
52       .get_result::<Self>(conn)
53   }
54   fn update(conn: &PgConnection, user_id: i32, form: &UserForm) -> Result<Self, Error> {
55     diesel::update(user_.find(user_id))
56       .set(form)
57       .get_result::<Self>(conn)
58   }
59 }
60
61 impl User_ {
62   pub fn register(conn: &PgConnection, form: &UserForm) -> Result<Self, Error> {
63     let mut edited_user = form.clone();
64     let password_hash = hash(&form.password_encrypted, DEFAULT_COST)
65       .expect("Couldn't hash password");
66     edited_user.password_encrypted = password_hash;
67
68     Self::create(&conn, &edited_user)
69
70   }
71   pub fn read_from_name(conn: &PgConnection, from_user_name: String) -> Result<Self, Error> {
72     user_.filter(name.eq(from_user_name))
73       .first::<Self>(conn)
74   }
75 }
76
77 #[derive(Debug, Serialize, Deserialize)]
78 pub struct Claims {
79   pub id: i32,
80   pub username: String,
81   pub iss: String,
82   pub show_nsfw: bool,
83 }
84
85 impl Claims {
86   pub fn decode(jwt: &str) -> Result<TokenData<Claims>, jsonwebtoken::errors::Error> {
87     let v = Validation {
88       validate_exp: false,
89       ..Validation::default()
90     };
91     decode::<Claims>(&jwt, Settings::get().jwt_secret.as_ref(), &v)
92   }
93 }
94
95 type Jwt = String;
96 impl User_ {
97   pub fn jwt(&self) -> Jwt {
98     let my_claims = Claims {
99       id: self.id,
100       username: self.name.to_owned(),
101       iss: self.fedi_name.to_owned(),
102       show_nsfw: self.show_nsfw,
103     };
104     encode(&Header::default(), &my_claims, Settings::get().jwt_secret.as_ref()).unwrap()
105   }
106
107   pub fn find_by_email_or_username(conn: &PgConnection, username_or_email: &str) -> Result<Self, Error> {
108     if is_email_regex(username_or_email) {
109       user_.filter(email.eq(username_or_email))
110         .first::<User_>(conn)
111     } else {
112       user_.filter(name.eq(username_or_email))
113         .first::<User_>(conn)
114     }
115   }
116
117   pub fn find_by_jwt(conn: &PgConnection, jwt: &str) -> Result<Self, Error> {
118     let claims: Claims = Claims::decode(&jwt).expect("Invalid token").claims;
119     Self::read(&conn, claims.id)
120   }
121
122 }
123
124
125 #[cfg(test)]
126 mod tests {
127   use super::*;
128  #[test]
129   fn test_crud() {
130     let conn = establish_connection();
131     
132     let new_user = UserForm {
133       name: "thommy".into(),
134       fedi_name: "rrf".into(),
135       preferred_username: None,
136       password_encrypted: "nope".into(),
137       email: None,
138       admin: false,
139       banned: false,
140       updated: None,
141       show_nsfw: false,
142     };
143
144     let inserted_user = User_::create(&conn, &new_user).unwrap();
145
146     let expected_user = User_ {
147       id: inserted_user.id,
148       name: "thommy".into(),
149       fedi_name: "rrf".into(),
150       preferred_username: None,
151       password_encrypted: "nope".into(),
152       email: None,
153       icon: None,
154       admin: false,
155       banned: false,
156       published: inserted_user.published,
157       updated: None,
158       show_nsfw: false,
159     };
160     
161     let read_user = User_::read(&conn, inserted_user.id).unwrap();
162     let updated_user = User_::update(&conn, inserted_user.id, &new_user).unwrap();
163     let num_deleted = User_::delete(&conn, inserted_user.id).unwrap();
164
165     assert_eq!(expected_user, read_user);
166     assert_eq!(expected_user, inserted_user);
167     assert_eq!(expected_user, updated_user);
168     assert_eq!(1, num_deleted);
169   }
170 }