]> Untitled Git - lemmy.git/blob - crates/api_crud/src/user/create.rs
Merge branch 'remove_settings_and_secret_singletons_squashed'
[lemmy.git] / crates / api_crud / src / user / create.rs
1 use crate::PerformCrud;
2 use actix_web::web::Data;
3 use lemmy_api_common::{blocking, password_length_check, person::*};
4 use lemmy_apub::{
5   generate_apub_endpoint,
6   generate_followers_url,
7   generate_inbox_url,
8   generate_shared_inbox_url,
9   EndpointType,
10 };
11 use lemmy_db_queries::{
12   source::{local_user::LocalUser_, site::Site_},
13   Crud,
14   Followable,
15   Joinable,
16   ListingType,
17   SortType,
18 };
19 use lemmy_db_schema::{
20   source::{
21     community::*,
22     local_user::{LocalUser, LocalUserForm},
23     person::*,
24     site::*,
25   },
26   CommunityId,
27 };
28 use lemmy_db_views_actor::person_view::PersonViewSafe;
29 use lemmy_utils::{
30   apub::generate_actor_keypair,
31   claims::Claims,
32   utils::{check_slurs, is_valid_actor_name},
33   ApiError,
34   ConnectionId,
35   LemmyError,
36 };
37 use lemmy_websocket::{messages::CheckCaptcha, LemmyContext};
38
39 #[async_trait::async_trait(?Send)]
40 impl PerformCrud for Register {
41   type Response = LoginResponse;
42
43   async fn perform(
44     &self,
45     context: &Data<LemmyContext>,
46     _websocket_id: Option<ConnectionId>,
47   ) -> Result<LoginResponse, LemmyError> {
48     let data: &Register = self;
49
50     // Make sure site has open registration
51     if let Ok(site) = blocking(context.pool(), move |conn| Site::read_simple(conn)).await? {
52       if !site.open_registration {
53         return Err(ApiError::err("registration_closed").into());
54       }
55     }
56
57     password_length_check(&data.password)?;
58
59     // Make sure passwords match
60     if data.password != data.password_verify {
61       return Err(ApiError::err("passwords_dont_match").into());
62     }
63
64     // Check if there are admins. False if admins exist
65     let no_admins = blocking(context.pool(), move |conn| {
66       PersonViewSafe::admins(conn).map(|a| a.is_empty())
67     })
68     .await??;
69
70     // If its not the admin, check the captcha
71     if !no_admins && context.settings().captcha.enabled {
72       let check = context
73         .chat_server()
74         .send(CheckCaptcha {
75           uuid: data
76             .captcha_uuid
77             .to_owned()
78             .unwrap_or_else(|| "".to_string()),
79           answer: data
80             .captcha_answer
81             .to_owned()
82             .unwrap_or_else(|| "".to_string()),
83         })
84         .await?;
85       if !check {
86         return Err(ApiError::err("captcha_incorrect").into());
87       }
88     }
89
90     check_slurs(&data.username, &context.settings().slur_regex())?;
91
92     let actor_keypair = generate_actor_keypair()?;
93     if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) {
94       return Err(ApiError::err("invalid_username").into());
95     }
96     let actor_id = generate_apub_endpoint(
97       EndpointType::Person,
98       &data.username,
99       &context.settings().get_protocol_and_hostname(),
100     )?;
101
102     // We have to create both a person, and local_user
103
104     // Register the new person
105     let person_form = PersonForm {
106       name: data.username.to_owned(),
107       actor_id: Some(actor_id.clone()),
108       private_key: Some(Some(actor_keypair.private_key)),
109       public_key: Some(Some(actor_keypair.public_key)),
110       inbox_url: Some(generate_inbox_url(&actor_id)?),
111       shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
112       admin: Some(no_admins),
113       ..PersonForm::default()
114     };
115
116     // insert the person
117     let inserted_person = blocking(context.pool(), move |conn| {
118       Person::create(conn, &person_form)
119     })
120     .await?
121     .map_err(|_| ApiError::err("user_already_exists"))?;
122
123     // Create the local user
124     // TODO some of these could probably use the DB defaults
125     let local_user_form = LocalUserForm {
126       person_id: inserted_person.id,
127       email: Some(data.email.to_owned()),
128       password_encrypted: data.password.to_owned(),
129       show_nsfw: Some(data.show_nsfw),
130       show_bot_accounts: Some(true),
131       theme: Some("browser".into()),
132       default_sort_type: Some(SortType::Active as i16),
133       default_listing_type: Some(ListingType::Subscribed as i16),
134       lang: Some("browser".into()),
135       show_avatars: Some(true),
136       show_scores: Some(true),
137       show_read_posts: Some(true),
138       show_new_post_notifs: Some(false),
139       send_notifications_to_email: Some(false),
140     };
141
142     let inserted_local_user = match blocking(context.pool(), move |conn| {
143       LocalUser::register(conn, &local_user_form)
144     })
145     .await?
146     {
147       Ok(lu) => lu,
148       Err(e) => {
149         let err_type = if e.to_string()
150           == "duplicate key value violates unique constraint \"local_user_email_key\""
151         {
152           "email_already_exists"
153         } else {
154           "user_already_exists"
155         };
156
157         // If the local user creation errored, then delete that person
158         blocking(context.pool(), move |conn| {
159           Person::delete(conn, inserted_person.id)
160         })
161         .await??;
162
163         return Err(ApiError::err(err_type).into());
164       }
165     };
166
167     let main_community_keypair = generate_actor_keypair()?;
168
169     // Create the main community if it doesn't exist
170     let protocol_and_hostname = context.settings().get_protocol_and_hostname();
171     let main_community = match blocking(context.pool(), move |conn| {
172       Community::read(conn, CommunityId(2))
173     })
174     .await?
175     {
176       Ok(c) => c,
177       Err(_e) => {
178         let default_community_name = "main";
179         let actor_id = generate_apub_endpoint(
180           EndpointType::Community,
181           default_community_name,
182           &protocol_and_hostname,
183         )?;
184         let community_form = CommunityForm {
185           name: default_community_name.to_string(),
186           title: "The Default Community".to_string(),
187           description: Some("The Default Community".to_string()),
188           actor_id: Some(actor_id.to_owned()),
189           private_key: Some(main_community_keypair.private_key),
190           public_key: Some(main_community_keypair.public_key),
191           followers_url: Some(generate_followers_url(&actor_id)?),
192           inbox_url: Some(generate_inbox_url(&actor_id)?),
193           shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
194           ..CommunityForm::default()
195         };
196         blocking(context.pool(), move |conn| {
197           Community::create(conn, &community_form)
198         })
199         .await??
200       }
201     };
202
203     // Sign them up for main community no matter what
204     let community_follower_form = CommunityFollowerForm {
205       community_id: main_community.id,
206       person_id: inserted_person.id,
207       pending: false,
208     };
209
210     let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
211     if blocking(context.pool(), follow).await?.is_err() {
212       return Err(ApiError::err("community_follower_already_exists").into());
213     };
214
215     // If its an admin, add them as a mod and follower to main
216     if no_admins {
217       let community_moderator_form = CommunityModeratorForm {
218         community_id: main_community.id,
219         person_id: inserted_person.id,
220       };
221
222       let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
223       if blocking(context.pool(), join).await?.is_err() {
224         return Err(ApiError::err("community_moderator_already_exists").into());
225       }
226     }
227
228     // Return the jwt
229     Ok(LoginResponse {
230       jwt: Claims::jwt(
231         inserted_local_user.id.0,
232         &context.secret().jwt_secret,
233         &context.settings().hostname,
234       )?,
235     })
236   }
237 }