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},
6 utils::{blocking, honeypot_check, password_length_check, send_verification_email},
10 generate_local_apub_endpoint,
11 generate_shared_inbox_url,
14 use lemmy_db_schema::{
15 aggregates::structs::PersonAggregates,
17 local_user::{LocalUser, LocalUserForm},
18 local_user_language::LocalUserLanguage,
19 person::{Person, PersonForm},
20 registration_application::{RegistrationApplication, RegistrationApplicationForm},
25 use lemmy_db_views::structs::LocalUserView;
26 use lemmy_db_views_actor::structs::PersonViewSafe;
30 utils::{check_slurs, is_valid_actor_name},
33 use lemmy_websocket::{messages::CheckCaptcha, LemmyContext};
35 #[async_trait::async_trait(?Send)]
36 impl PerformCrud for Register {
37 type Response = LoginResponse;
39 #[tracing::instrument(skip(self, context, _websocket_id))]
42 context: &Data<LemmyContext>,
43 _websocket_id: Option<ConnectionId>,
44 ) -> Result<LoginResponse, LemmyError> {
45 let data: &Register = self;
47 // no email verification, or applications if the site is not setup yet
48 let (mut email_verification, mut require_application) = (false, false);
50 // Make sure site has open registration
51 if let Ok(site) = blocking(context.pool(), Site::read_local_site).await? {
52 if !site.open_registration {
53 return Err(LemmyError::from_message("registration_closed"));
55 email_verification = site.require_email_verification;
56 require_application = site.require_application;
59 password_length_check(&data.password)?;
60 honeypot_check(&data.honeypot)?;
62 if email_verification && data.email.is_none() {
63 return Err(LemmyError::from_message("email_required"));
66 if require_application && data.answer.is_none() {
67 return Err(LemmyError::from_message(
68 "registration_application_answer_required",
72 // Make sure passwords match
73 if data.password != data.password_verify {
74 return Err(LemmyError::from_message("passwords_dont_match"));
77 // Check if there are admins. False if admins exist
78 let no_admins = blocking(context.pool(), move |conn| {
79 PersonViewSafe::admins(conn).map(|a| a.is_empty())
83 // If its not the admin, check the captcha
84 if !no_admins && context.settings().captcha.enabled {
91 .unwrap_or_else(|| "".to_string()),
95 .unwrap_or_else(|| "".to_string()),
99 return Err(LemmyError::from_message("captcha_incorrect"));
103 check_slurs(&data.username, &context.settings().slur_regex())?;
105 let actor_keypair = generate_actor_keypair()?;
106 if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) {
107 return Err(LemmyError::from_message("invalid_username"));
109 let actor_id = generate_local_apub_endpoint(
110 EndpointType::Person,
112 &context.settings().get_protocol_and_hostname(),
115 // We have to create both a person, and local_user
117 // Register the new person
118 let person_form = PersonForm {
119 name: data.username.to_owned(),
120 actor_id: Some(actor_id.clone()),
121 private_key: Some(Some(actor_keypair.private_key)),
122 public_key: Some(actor_keypair.public_key),
123 inbox_url: Some(generate_inbox_url(&actor_id)?),
124 shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
125 admin: Some(no_admins),
126 ..PersonForm::default()
130 let inserted_person = blocking(context.pool(), move |conn| {
131 Person::create(conn, &person_form)
134 .map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?;
136 // Create the local user
137 let local_user_form = LocalUserForm {
138 person_id: Some(inserted_person.id),
139 email: Some(data.email.as_deref().map(|s| s.to_owned())),
140 password_encrypted: Some(data.password.to_string()),
141 show_nsfw: Some(data.show_nsfw),
142 email_verified: Some(false),
143 ..LocalUserForm::default()
146 let inserted_local_user = match blocking(context.pool(), move |conn| {
147 LocalUser::register(conn, &local_user_form)
153 let err_type = if e.to_string()
154 == "duplicate key value violates unique constraint \"local_user_email_key\""
156 "email_already_exists"
158 "user_already_exists"
161 // If the local user creation errored, then delete that person
162 blocking(context.pool(), move |conn| {
163 Person::delete(conn, inserted_person.id)
167 return Err(LemmyError::from_error_message(e, err_type));
171 // Update the users languages to all by default
172 let local_user_id = inserted_local_user.id;
173 blocking(context.pool(), move |conn| {
174 LocalUserLanguage::update_user_languages(conn, None, local_user_id)
178 if require_application {
179 // Create the registration application
180 let form = RegistrationApplicationForm {
181 local_user_id: Some(local_user_id),
182 // We already made sure answer was not null above
183 answer: data.answer.to_owned(),
184 ..RegistrationApplicationForm::default()
187 blocking(context.pool(), move |conn| {
188 RegistrationApplication::create(conn, &form)
193 let mut login_response = LoginResponse {
195 registration_created: false,
196 verify_email_sent: false,
199 // Log the user in directly if email verification and application aren't required
200 if !require_application && !email_verification {
201 login_response.jwt = Some(
203 inserted_local_user.id.0,
204 &context.secret().jwt_secret,
205 &context.settings().hostname,
210 if email_verification {
211 let local_user_view = LocalUserView {
212 local_user: inserted_local_user,
213 person: inserted_person,
214 counts: PersonAggregates::default(),
216 // we check at the beginning of this method that email is set
217 let email = local_user_view
221 .expect("email was provided");
222 send_verification_email(&local_user_view, &email, context.pool(), context.settings())
224 login_response.verify_email_sent = true;
227 if require_application {
228 login_response.registration_created = true;