1 use crate::PerformCrud;
2 use activitypub_federation::core::signatures::generate_actor_keypair;
3 use actix_web::web::Data;
4 use lemmy_api_common::{
5 person::{LoginResponse, Register},
10 send_new_applicant_email_to_admins,
11 send_verification_email,
16 generate_local_apub_endpoint,
17 generate_shared_inbox_url,
20 use lemmy_db_schema::{
21 aggregates::structs::PersonAggregates,
23 local_user::{LocalUser, LocalUserForm},
24 person::{Person, PersonForm},
25 registration_application::{RegistrationApplication, RegistrationApplicationForm},
30 use lemmy_db_views::structs::LocalUserView;
31 use lemmy_db_views_actor::structs::PersonViewSafe;
35 utils::{check_slurs, check_slurs_opt, is_valid_actor_name},
38 use lemmy_websocket::{messages::CheckCaptcha, LemmyContext};
40 #[async_trait::async_trait(?Send)]
41 impl PerformCrud for Register {
42 type Response = LoginResponse;
44 #[tracing::instrument(skip(self, context, _websocket_id))]
47 context: &Data<LemmyContext>,
48 _websocket_id: Option<ConnectionId>,
49 ) -> Result<LoginResponse, LemmyError> {
50 let data: &Register = self;
52 // no email verification, or applications if the site is not setup yet
53 let (mut email_verification, mut require_application) = (false, false);
55 // Make sure site has open registration
56 let site = blocking(context.pool(), Site::read_local_site).await?;
57 if let Ok(site) = &site {
58 if !site.open_registration {
59 return Err(LemmyError::from_message("registration_closed"));
61 email_verification = site.require_email_verification;
62 require_application = site.require_application;
65 password_length_check(&data.password)?;
66 honeypot_check(&data.honeypot)?;
68 if email_verification && data.email.is_none() {
69 return Err(LemmyError::from_message("email_required"));
72 if require_application && data.answer.is_none() {
73 return Err(LemmyError::from_message(
74 "registration_application_answer_required",
78 // Make sure passwords match
79 if data.password != data.password_verify {
80 return Err(LemmyError::from_message("passwords_dont_match"));
83 // Check if there are admins. False if admins exist
84 let no_admins = blocking(context.pool(), move |conn| {
85 PersonViewSafe::admins(conn).map(|a| a.is_empty())
89 // If its not the admin, check the captcha
90 if !no_admins && context.settings().captcha.enabled {
97 .unwrap_or_else(|| "".to_string()),
101 .unwrap_or_else(|| "".to_string()),
105 return Err(LemmyError::from_message("captcha_incorrect"));
109 let slur_regex = &context.settings().slur_regex();
110 check_slurs(&data.username, slur_regex)?;
111 check_slurs_opt(&data.answer, slur_regex)?;
113 let actor_keypair = generate_actor_keypair()?;
114 if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) {
115 return Err(LemmyError::from_message("invalid_username"));
117 let actor_id = generate_local_apub_endpoint(
118 EndpointType::Person,
120 &context.settings().get_protocol_and_hostname(),
123 // We have to create both a person, and local_user
125 // Register the new person
126 let person_form = PersonForm {
127 name: data.username.to_owned(),
128 actor_id: Some(actor_id.clone()),
129 private_key: Some(Some(actor_keypair.private_key)),
130 public_key: Some(actor_keypair.public_key),
131 inbox_url: Some(generate_inbox_url(&actor_id)?),
132 shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
133 admin: Some(no_admins),
134 ..PersonForm::default()
138 let inserted_person = blocking(context.pool(), move |conn| {
139 Person::create(conn, &person_form)
142 .map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?;
144 // Create the local user
145 let local_user_form = LocalUserForm {
146 person_id: Some(inserted_person.id),
147 email: Some(data.email.as_deref().map(|s| s.to_lowercase())),
148 password_encrypted: Some(data.password.to_string()),
149 show_nsfw: Some(data.show_nsfw),
150 email_verified: Some(false),
151 ..LocalUserForm::default()
154 let inserted_local_user = match blocking(context.pool(), move |conn| {
155 LocalUser::register(conn, &local_user_form)
161 let err_type = if e.to_string()
162 == "duplicate key value violates unique constraint \"local_user_email_key\""
164 "email_already_exists"
166 "user_already_exists"
169 // If the local user creation errored, then delete that person
170 blocking(context.pool(), move |conn| {
171 Person::delete(conn, inserted_person.id)
175 return Err(LemmyError::from_error_message(e, err_type));
179 if require_application {
180 // Create the registration application
181 let form = RegistrationApplicationForm {
182 local_user_id: Some(inserted_local_user.id),
183 // We already made sure answer was not null above
184 answer: data.answer.to_owned(),
185 ..RegistrationApplicationForm::default()
188 blocking(context.pool(), move |conn| {
189 RegistrationApplication::create(conn, &form)
195 if site.map(|s| s.application_email_admins).unwrap_or(false) {
196 send_new_applicant_email_to_admins(&data.username, context.pool(), context.settings())
200 let mut login_response = LoginResponse {
202 registration_created: false,
203 verify_email_sent: false,
206 // Log the user in directly if email verification and application aren't required
207 if !require_application && !email_verification {
208 login_response.jwt = Some(
210 inserted_local_user.id.0,
211 &context.secret().jwt_secret,
212 &context.settings().hostname,
217 if email_verification {
218 let local_user_view = LocalUserView {
219 local_user: inserted_local_user,
220 person: inserted_person,
221 counts: PersonAggregates::default(),
223 // we check at the beginning of this method that email is set
224 let email = local_user_view
228 .expect("email was provided");
229 send_verification_email(&local_user_view, &email, context.pool(), context.settings())
231 login_response.verify_email_sent = true;
234 if require_application {
235 login_response.registration_created = true;