]> Untitled Git - lemmy.git/blob - crates/api_crud/src/user/create.rs
Rewrite remaining federation actions, get rid of PerformCrud trait (#3794)
[lemmy.git] / crates / api_crud / src / user / create.rs
1 use activitypub_federation::{config::Data, http_signatures::generate_actor_keypair};
2 use actix_web::web::Json;
3 use lemmy_api_common::{
4   context::LemmyContext,
5   person::{LoginResponse, Register},
6   utils::{
7     generate_inbox_url,
8     generate_local_apub_endpoint,
9     generate_shared_inbox_url,
10     honeypot_check,
11     local_site_to_slur_regex,
12     password_length_check,
13     sanitize_html,
14     send_new_applicant_email_to_admins,
15     send_verification_email,
16     EndpointType,
17   },
18 };
19 use lemmy_db_schema::{
20   aggregates::structs::PersonAggregates,
21   source::{
22     captcha_answer::{CaptchaAnswer, CheckCaptchaAnswer},
23     local_user::{LocalUser, LocalUserInsertForm},
24     person::{Person, PersonInsertForm},
25     registration_application::{RegistrationApplication, RegistrationApplicationInsertForm},
26   },
27   traits::Crud,
28   RegistrationMode,
29 };
30 use lemmy_db_views::structs::{LocalUserView, SiteView};
31 use lemmy_utils::{
32   claims::Claims,
33   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
34   utils::{
35     slurs::{check_slurs, check_slurs_opt},
36     validation::is_valid_actor_name,
37   },
38 };
39
40 #[tracing::instrument(skip(context))]
41 pub async fn register(
42   data: Json<Register>,
43   context: Data<LemmyContext>,
44 ) -> Result<Json<LoginResponse>, LemmyError> {
45   let site_view = SiteView::read_local(&mut context.pool()).await?;
46   let local_site = site_view.local_site;
47   let require_registration_application =
48     local_site.registration_mode == RegistrationMode::RequireApplication;
49
50   if local_site.registration_mode == RegistrationMode::Closed {
51     return Err(LemmyErrorType::RegistrationClosed)?;
52   }
53
54   password_length_check(&data.password)?;
55   honeypot_check(&data.honeypot)?;
56
57   if local_site.require_email_verification && data.email.is_none() {
58     return Err(LemmyErrorType::EmailRequired)?;
59   }
60
61   if local_site.site_setup && require_registration_application && data.answer.is_none() {
62     return Err(LemmyErrorType::RegistrationApplicationAnswerRequired)?;
63   }
64
65   // Make sure passwords match
66   if data.password != data.password_verify {
67     return Err(LemmyErrorType::PasswordsDoNotMatch)?;
68   }
69
70   if local_site.site_setup && local_site.captcha_enabled {
71     if let Some(captcha_uuid) = &data.captcha_uuid {
72       let uuid = uuid::Uuid::parse_str(captcha_uuid)?;
73       let check = CaptchaAnswer::check_captcha(
74         &mut context.pool(),
75         CheckCaptchaAnswer {
76           uuid,
77           answer: data.captcha_answer.clone().unwrap_or_default(),
78         },
79       )
80       .await?;
81       if !check {
82         return Err(LemmyErrorType::CaptchaIncorrect)?;
83       }
84     } else {
85       return Err(LemmyErrorType::CaptchaIncorrect)?;
86     }
87   }
88
89   let slur_regex = local_site_to_slur_regex(&local_site);
90   check_slurs(&data.username, &slur_regex)?;
91   check_slurs_opt(&data.answer, &slur_regex)?;
92   let username = sanitize_html(&data.username);
93
94   let actor_keypair = generate_actor_keypair()?;
95   is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize)?;
96   let actor_id = generate_local_apub_endpoint(
97     EndpointType::Person,
98     &data.username,
99     &context.settings().get_protocol_and_hostname(),
100   )?;
101
102   if let Some(email) = &data.email {
103     if LocalUser::is_email_taken(&mut context.pool(), email).await? {
104       return Err(LemmyErrorType::EmailAlreadyExists)?;
105     }
106   }
107
108   // We have to create both a person, and local_user
109
110   // Register the new person
111   let person_form = PersonInsertForm::builder()
112     .name(username)
113     .actor_id(Some(actor_id.clone()))
114     .private_key(Some(actor_keypair.private_key))
115     .public_key(actor_keypair.public_key)
116     .inbox_url(Some(generate_inbox_url(&actor_id)?))
117     .shared_inbox_url(Some(generate_shared_inbox_url(&actor_id)?))
118     // If its the initial site setup, they are an admin
119     .admin(Some(!local_site.site_setup))
120     .instance_id(site_view.site.instance_id)
121     .build();
122
123   // insert the person
124   let inserted_person = Person::create(&mut context.pool(), &person_form)
125     .await
126     .with_lemmy_type(LemmyErrorType::UserAlreadyExists)?;
127
128   // Automatically set their application as accepted, if they created this with open registration.
129   // Also fixes a bug which allows users to log in when registrations are changed to closed.
130   let accepted_application = Some(!require_registration_application);
131
132   // Create the local user
133   let local_user_form = LocalUserInsertForm::builder()
134     .person_id(inserted_person.id)
135     .email(data.email.as_deref().map(str::to_lowercase))
136     .password_encrypted(data.password.to_string())
137     .show_nsfw(Some(data.show_nsfw))
138     .accepted_application(accepted_application)
139     .default_listing_type(Some(local_site.default_post_listing_type))
140     .build();
141
142   let inserted_local_user = LocalUser::create(&mut context.pool(), &local_user_form).await?;
143
144   if local_site.site_setup && require_registration_application {
145     // Create the registration application
146     let form = RegistrationApplicationInsertForm {
147       local_user_id: inserted_local_user.id,
148       // We already made sure answer was not null above
149       answer: data.answer.clone().expect("must have an answer"),
150     };
151
152     RegistrationApplication::create(&mut context.pool(), &form).await?;
153   }
154
155   // Email the admins
156   if local_site.application_email_admins {
157     send_new_applicant_email_to_admins(&data.username, &mut context.pool(), context.settings())
158       .await?;
159   }
160
161   let mut login_response = LoginResponse {
162     jwt: None,
163     registration_created: false,
164     verify_email_sent: false,
165   };
166
167   // Log the user in directly if the site is not setup, or email verification and application aren't required
168   if !local_site.site_setup
169     || (!require_registration_application && !local_site.require_email_verification)
170   {
171     login_response.jwt = Some(
172       Claims::jwt(
173         inserted_local_user.id.0,
174         &context.secret().jwt_secret,
175         &context.settings().hostname,
176       )?
177       .into(),
178     );
179   } else {
180     if local_site.require_email_verification {
181       let local_user_view = LocalUserView {
182         local_user: inserted_local_user,
183         person: inserted_person,
184         counts: PersonAggregates::default(),
185       };
186       // we check at the beginning of this method that email is set
187       let email = local_user_view
188         .local_user
189         .email
190         .clone()
191         .expect("email was provided");
192
193       send_verification_email(
194         &local_user_view,
195         &email,
196         &mut context.pool(),
197         context.settings(),
198       )
199       .await?;
200       login_response.verify_email_sent = true;
201     }
202
203     if require_registration_application {
204       login_response.registration_created = true;
205     }
206   }
207
208   Ok(Json(login_response))
209 }