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