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