]> Untitled Git - lemmy.git/blob - crates/api_crud/src/user/create.rs
Adding email admins for new applications. Fixes #2271 (#2390)
[lemmy.git] / crates / api_crud / src / user / create.rs
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::{
7     blocking,
8     honeypot_check,
9     password_length_check,
10     send_new_applicant_email_to_admins,
11     send_verification_email,
12   },
13 };
14 use lemmy_apub::{
15   generate_inbox_url,
16   generate_local_apub_endpoint,
17   generate_shared_inbox_url,
18   EndpointType,
19 };
20 use lemmy_db_schema::{
21   aggregates::structs::PersonAggregates,
22   source::{
23     local_user::{LocalUser, LocalUserForm},
24     person::{Person, PersonForm},
25     registration_application::{RegistrationApplication, RegistrationApplicationForm},
26     site::Site,
27   },
28   traits::Crud,
29 };
30 use lemmy_db_views::structs::LocalUserView;
31 use lemmy_db_views_actor::structs::PersonViewSafe;
32 use lemmy_utils::{
33   claims::Claims,
34   error::LemmyError,
35   utils::{check_slurs, check_slurs_opt, is_valid_actor_name},
36   ConnectionId,
37 };
38 use lemmy_websocket::{messages::CheckCaptcha, LemmyContext};
39
40 #[async_trait::async_trait(?Send)]
41 impl PerformCrud for Register {
42   type Response = LoginResponse;
43
44   #[tracing::instrument(skip(self, context, _websocket_id))]
45   async fn perform(
46     &self,
47     context: &Data<LemmyContext>,
48     _websocket_id: Option<ConnectionId>,
49   ) -> Result<LoginResponse, LemmyError> {
50     let data: &Register = self;
51
52     // no email verification, or applications if the site is not setup yet
53     let (mut email_verification, mut require_application) = (false, false);
54
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"));
60       }
61       email_verification = site.require_email_verification;
62       require_application = site.require_application;
63     }
64
65     password_length_check(&data.password)?;
66     honeypot_check(&data.honeypot)?;
67
68     if email_verification && data.email.is_none() {
69       return Err(LemmyError::from_message("email_required"));
70     }
71
72     if require_application && data.answer.is_none() {
73       return Err(LemmyError::from_message(
74         "registration_application_answer_required",
75       ));
76     }
77
78     // Make sure passwords match
79     if data.password != data.password_verify {
80       return Err(LemmyError::from_message("passwords_dont_match"));
81     }
82
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())
86     })
87     .await??;
88
89     // If its not the admin, check the captcha
90     if !no_admins && context.settings().captcha.enabled {
91       let check = context
92         .chat_server()
93         .send(CheckCaptcha {
94           uuid: data
95             .captcha_uuid
96             .to_owned()
97             .unwrap_or_else(|| "".to_string()),
98           answer: data
99             .captcha_answer
100             .to_owned()
101             .unwrap_or_else(|| "".to_string()),
102         })
103         .await?;
104       if !check {
105         return Err(LemmyError::from_message("captcha_incorrect"));
106       }
107     }
108
109     let slur_regex = &context.settings().slur_regex();
110     check_slurs(&data.username, slur_regex)?;
111     check_slurs_opt(&data.answer, slur_regex)?;
112
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"));
116     }
117     let actor_id = generate_local_apub_endpoint(
118       EndpointType::Person,
119       &data.username,
120       &context.settings().get_protocol_and_hostname(),
121     )?;
122
123     // We have to create both a person, and local_user
124
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()
135     };
136
137     // insert the person
138     let inserted_person = blocking(context.pool(), move |conn| {
139       Person::create(conn, &person_form)
140     })
141     .await?
142     .map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?;
143
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()
152     };
153
154     let inserted_local_user = match blocking(context.pool(), move |conn| {
155       LocalUser::register(conn, &local_user_form)
156     })
157     .await?
158     {
159       Ok(lu) => lu,
160       Err(e) => {
161         let err_type = if e.to_string()
162           == "duplicate key value violates unique constraint \"local_user_email_key\""
163         {
164           "email_already_exists"
165         } else {
166           "user_already_exists"
167         };
168
169         // If the local user creation errored, then delete that person
170         blocking(context.pool(), move |conn| {
171           Person::delete(conn, inserted_person.id)
172         })
173         .await??;
174
175         return Err(LemmyError::from_error_message(e, err_type));
176       }
177     };
178
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()
186       };
187
188       blocking(context.pool(), move |conn| {
189         RegistrationApplication::create(conn, &form)
190       })
191       .await??;
192     }
193
194     // Email the admins
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())
197         .await?;
198     }
199
200     let mut login_response = LoginResponse {
201       jwt: None,
202       registration_created: false,
203       verify_email_sent: false,
204     };
205
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(
209         Claims::jwt(
210           inserted_local_user.id.0,
211           &context.secret().jwt_secret,
212           &context.settings().hostname,
213         )?
214         .into(),
215       );
216     } else {
217       if email_verification {
218         let local_user_view = LocalUserView {
219           local_user: inserted_local_user,
220           person: inserted_person,
221           counts: PersonAggregates::default(),
222         };
223         // we check at the beginning of this method that email is set
224         let email = local_user_view
225           .local_user
226           .email
227           .clone()
228           .expect("email was provided");
229         send_verification_email(&local_user_view, &email, context.pool(), context.settings())
230           .await?;
231         login_response.verify_email_sent = true;
232       }
233
234       if require_application {
235         login_response.registration_created = true;
236       }
237     }
238
239     Ok(login_response)
240   }
241 }