]> Untitled Git - lemmy.git/blob - crates/api_crud/src/user/create.rs
Making community_follower.pending column not null.
[lemmy.git] / crates / api_crud / src / user / create.rs
1 use crate::PerformCrud;
2 use actix_web::web::Data;
3 use lemmy_api_common::{
4   person::{LoginResponse, Register},
5   utils::{blocking, honeypot_check, password_length_check, send_verification_email},
6 };
7 use lemmy_apub::{
8   generate_followers_url,
9   generate_inbox_url,
10   generate_local_apub_endpoint,
11   generate_shared_inbox_url,
12   EndpointType,
13 };
14 use lemmy_db_schema::{
15   aggregates::structs::PersonAggregates,
16   newtypes::CommunityId,
17   source::{
18     community::{
19       Community,
20       CommunityFollower,
21       CommunityFollowerForm,
22       CommunityForm,
23       CommunityModerator,
24       CommunityModeratorForm,
25     },
26     local_user::{LocalUser, LocalUserForm},
27     person::{Person, PersonForm},
28     registration_application::{RegistrationApplication, RegistrationApplicationForm},
29     site::Site,
30   },
31   traits::{Crud, Followable, Joinable},
32 };
33 use lemmy_db_views::structs::LocalUserView;
34 use lemmy_db_views_actor::structs::PersonViewSafe;
35 use lemmy_utils::{
36   apub::generate_actor_keypair,
37   claims::Claims,
38   utils::{check_slurs, is_valid_actor_name},
39   ConnectionId,
40   LemmyError,
41 };
42 use lemmy_websocket::{messages::CheckCaptcha, LemmyContext};
43
44 #[async_trait::async_trait(?Send)]
45 impl PerformCrud for Register {
46   type Response = LoginResponse;
47
48   #[tracing::instrument(skip(self, context, _websocket_id))]
49   async fn perform(
50     &self,
51     context: &Data<LemmyContext>,
52     _websocket_id: Option<ConnectionId>,
53   ) -> Result<LoginResponse, LemmyError> {
54     let data: &Register = self;
55
56     // no email verification, or applications if the site is not setup yet
57     let (mut email_verification, mut require_application) = (false, false);
58
59     // Make sure site has open registration
60     if let Ok(site) = blocking(context.pool(), Site::read_local_site).await? {
61       if !site.open_registration {
62         return Err(LemmyError::from_message("registration_closed"));
63       }
64       email_verification = site.require_email_verification;
65       require_application = site.require_application;
66     }
67
68     password_length_check(&data.password)?;
69     honeypot_check(&data.honeypot)?;
70
71     if email_verification && data.email.is_none() {
72       return Err(LemmyError::from_message("email_required"));
73     }
74
75     if require_application && data.answer.is_none() {
76       return Err(LemmyError::from_message(
77         "registration_application_answer_required",
78       ));
79     }
80
81     // Make sure passwords match
82     if data.password != data.password_verify {
83       return Err(LemmyError::from_message("passwords_dont_match"));
84     }
85
86     // Check if there are admins. False if admins exist
87     let no_admins = blocking(context.pool(), move |conn| {
88       PersonViewSafe::admins(conn).map(|a| a.is_empty())
89     })
90     .await??;
91
92     // If its not the admin, check the captcha
93     if !no_admins && context.settings().captcha.enabled {
94       let check = context
95         .chat_server()
96         .send(CheckCaptcha {
97           uuid: data
98             .captcha_uuid
99             .to_owned()
100             .unwrap_or_else(|| "".to_string()),
101           answer: data
102             .captcha_answer
103             .to_owned()
104             .unwrap_or_else(|| "".to_string()),
105         })
106         .await?;
107       if !check {
108         return Err(LemmyError::from_message("captcha_incorrect"));
109       }
110     }
111
112     check_slurs(&data.username, &context.settings().slur_regex())?;
113
114     let actor_keypair = generate_actor_keypair()?;
115     if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) {
116       return Err(LemmyError::from_message("invalid_username"));
117     }
118     let actor_id = generate_local_apub_endpoint(
119       EndpointType::Person,
120       &data.username,
121       &context.settings().get_protocol_and_hostname(),
122     )?;
123
124     // We have to create both a person, and local_user
125
126     // Register the new person
127     let person_form = PersonForm {
128       name: data.username.to_owned(),
129       actor_id: Some(actor_id.clone()),
130       private_key: Some(Some(actor_keypair.private_key)),
131       public_key: actor_keypair.public_key,
132       inbox_url: Some(generate_inbox_url(&actor_id)?),
133       shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
134       admin: Some(no_admins),
135       ..PersonForm::default()
136     };
137
138     // insert the person
139     let inserted_person = blocking(context.pool(), move |conn| {
140       Person::create(conn, &person_form)
141     })
142     .await?
143     .map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?;
144
145     // Create the local user
146     let local_user_form = LocalUserForm {
147       person_id: Some(inserted_person.id),
148       email: Some(data.email.as_deref().map(|s| s.to_owned())),
149       password_encrypted: Some(data.password.to_string()),
150       show_nsfw: Some(data.show_nsfw),
151       email_verified: Some(false),
152       ..LocalUserForm::default()
153     };
154
155     let inserted_local_user = match blocking(context.pool(), move |conn| {
156       LocalUser::register(conn, &local_user_form)
157     })
158     .await?
159     {
160       Ok(lu) => lu,
161       Err(e) => {
162         let err_type = if e.to_string()
163           == "duplicate key value violates unique constraint \"local_user_email_key\""
164         {
165           "email_already_exists"
166         } else {
167           "user_already_exists"
168         };
169
170         // If the local user creation errored, then delete that person
171         blocking(context.pool(), move |conn| {
172           Person::delete(conn, inserted_person.id)
173         })
174         .await??;
175
176         return Err(LemmyError::from_error_message(e, err_type));
177       }
178     };
179
180     if require_application {
181       // Create the registration application
182       let form = RegistrationApplicationForm {
183         local_user_id: Some(inserted_local_user.id),
184         // We already made sure answer was not null above
185         answer: data.answer.to_owned(),
186         ..RegistrationApplicationForm::default()
187       };
188
189       blocking(context.pool(), move |conn| {
190         RegistrationApplication::create(conn, &form)
191       })
192       .await??;
193     }
194
195     let main_community_keypair = generate_actor_keypair()?;
196
197     // Create the main community if it doesn't exist
198     let protocol_and_hostname = context.settings().get_protocol_and_hostname();
199     let main_community = match blocking(context.pool(), move |conn| {
200       Community::read(conn, CommunityId(2))
201     })
202     .await?
203     {
204       Ok(c) => c,
205       Err(_e) => {
206         let default_community_name = "main";
207         let actor_id = generate_local_apub_endpoint(
208           EndpointType::Community,
209           default_community_name,
210           &protocol_and_hostname,
211         )?;
212         let community_form = CommunityForm {
213           name: default_community_name.to_string(),
214           title: "The Default Community".to_string(),
215           description: Some("The Default Community".to_string()),
216           actor_id: Some(actor_id.to_owned()),
217           private_key: Some(Some(main_community_keypair.private_key)),
218           public_key: main_community_keypair.public_key,
219           followers_url: Some(generate_followers_url(&actor_id)?),
220           inbox_url: Some(generate_inbox_url(&actor_id)?),
221           shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
222           ..CommunityForm::default()
223         };
224         blocking(context.pool(), move |conn| {
225           Community::create(conn, &community_form)
226         })
227         .await??
228       }
229     };
230
231     // Sign them up for main community no matter what
232     let community_follower_form = CommunityFollowerForm {
233       community_id: main_community.id,
234       person_id: inserted_person.id,
235       pending: Some(false),
236     };
237
238     let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
239     blocking(context.pool(), follow)
240       .await?
241       .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
242
243     // If its an admin, add them as a mod and follower to main
244     if no_admins {
245       let community_moderator_form = CommunityModeratorForm {
246         community_id: main_community.id,
247         person_id: inserted_person.id,
248       };
249
250       let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
251       blocking(context.pool(), join)
252         .await?
253         .map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?;
254     }
255
256     let mut login_response = LoginResponse {
257       jwt: None,
258       registration_created: false,
259       verify_email_sent: false,
260     };
261
262     // Log the user in directly if email verification and application aren't required
263     if !require_application && !email_verification {
264       login_response.jwt = Some(
265         Claims::jwt(
266           inserted_local_user.id.0,
267           &context.secret().jwt_secret,
268           &context.settings().hostname,
269         )?
270         .into(),
271       );
272     } else {
273       if email_verification {
274         let local_user_view = LocalUserView {
275           local_user: inserted_local_user,
276           person: inserted_person,
277           counts: PersonAggregates::default(),
278         };
279         // we check at the beginning of this method that email is set
280         let email = local_user_view
281           .local_user
282           .email
283           .clone()
284           .expect("email was provided");
285         send_verification_email(
286           &local_user_view,
287           &email,
288           context.pool(),
289           &context.settings(),
290         )
291         .await?;
292         login_response.verify_email_sent = true;
293       }
294
295       if require_application {
296         login_response.registration_created = true;
297       }
298     }
299
300     Ok(login_response)
301   }
302 }