mirror of https://github.com/LemmyNet/lemmy.git
A few fixes for private sites:
- Added a check_registration_application function. - Only send a verification email if its been changed. - VerifyEmail now returns LoginResponse. - Deleting the old tokens after a successful email verify. - If port is missing on email config, display a better error message.invite_instances
parent
b9978cc141
commit
eecb710104
|
@ -6,6 +6,7 @@ use captcha::{gen, Difficulty};
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
|
check_registration_application,
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
is_admin,
|
is_admin,
|
||||||
password_length_check,
|
password_length_check,
|
||||||
|
@ -97,9 +98,7 @@ impl Perform for Login {
|
||||||
return Err(LemmyError::from_message("email_not_verified"));
|
return Err(LemmyError::from_message("email_not_verified"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if site.require_application && !local_user_view.local_user.accepted_application {
|
check_registration_application(&site, &local_user_view, context.pool()).await?;
|
||||||
return Err(LemmyError::from_message("registration_application_pending"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(LoginResponse {
|
Ok(LoginResponse {
|
||||||
|
@ -188,6 +187,9 @@ impl Perform for SaveUserSettings {
|
||||||
let email = if let Some(email) = &email_deref {
|
let email = if let Some(email) = &email_deref {
|
||||||
let site = blocking(context.pool(), Site::read_simple).await??;
|
let site = blocking(context.pool(), Site::read_simple).await??;
|
||||||
if site.require_email_verification {
|
if site.require_email_verification {
|
||||||
|
if let Some(previous_email) = local_user_view.local_user.email {
|
||||||
|
// Only send the verification email if there was an email change
|
||||||
|
if previous_email.ne(email) {
|
||||||
send_verification_email(
|
send_verification_email(
|
||||||
local_user_view.local_user.id,
|
local_user_view.local_user.id,
|
||||||
email,
|
email,
|
||||||
|
@ -196,6 +198,10 @@ impl Perform for SaveUserSettings {
|
||||||
&context.settings(),
|
&context.settings(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fine to return None here, because the actual email is also in the email_verification
|
||||||
|
// table, and gets set in the function below.
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
diesel_option_overwrite(&email_deref)
|
diesel_option_overwrite(&email_deref)
|
||||||
|
@ -918,7 +924,7 @@ impl Perform for GetUnreadCount {
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for VerifyEmail {
|
impl Perform for VerifyEmail {
|
||||||
type Response = VerifyEmailResponse;
|
type Response = LoginResponse;
|
||||||
|
|
||||||
async fn perform(
|
async fn perform(
|
||||||
&self,
|
&self,
|
||||||
|
@ -953,6 +959,26 @@ impl Perform for VerifyEmail {
|
||||||
|
|
||||||
send_email_verification_success(&local_user_view, &context.settings())?;
|
send_email_verification_success(&local_user_view, &context.settings())?;
|
||||||
|
|
||||||
Ok(VerifyEmailResponse {})
|
blocking(context.pool(), move |conn| {
|
||||||
|
EmailVerification::delete_old_tokens_for_local_user(conn, local_user_id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
let site = blocking(context.pool(), Site::read_simple).await??;
|
||||||
|
check_registration_application(&site, &local_user_view, context.pool()).await?;
|
||||||
|
|
||||||
|
// Return the jwt
|
||||||
|
Ok(LoginResponse {
|
||||||
|
jwt: Some(
|
||||||
|
Claims::jwt(
|
||||||
|
local_user_view.local_user.id.0,
|
||||||
|
&context.secret().jwt_secret,
|
||||||
|
&context.settings().hostname,
|
||||||
|
)?
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
verify_email_sent: false,
|
||||||
|
registration_created: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_db_views::{
|
use lemmy_db_views::{
|
||||||
comment_view::{CommentQueryBuilder, CommentView},
|
comment_view::{CommentQueryBuilder, CommentView},
|
||||||
|
local_user_view::LocalUserView,
|
||||||
post_view::{PostQueryBuilder, PostView},
|
post_view::{PostQueryBuilder, PostView},
|
||||||
registration_application_view::{
|
registration_application_view::{
|
||||||
RegistrationApplicationQueryBuilder,
|
RegistrationApplicationQueryBuilder,
|
||||||
|
@ -662,7 +663,11 @@ impl Perform for ApproveRegistrationApplication {
|
||||||
.require_email_verification;
|
.require_email_verification;
|
||||||
|
|
||||||
if require_email_verification && data.approve {
|
if require_email_verification && data.approve {
|
||||||
send_application_approved_email(&local_user_view, &context.settings())?;
|
let approved_local_user_view = blocking(context.pool(), move |conn| {
|
||||||
|
LocalUserView::read(conn, approved_user_id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
send_application_approved_email(&approved_local_user_view, &context.settings())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the view
|
// Read the view
|
||||||
|
|
|
@ -14,6 +14,7 @@ use lemmy_db_schema::{
|
||||||
password_reset_request::PasswordResetRequest,
|
password_reset_request::PasswordResetRequest,
|
||||||
person_block::PersonBlock,
|
person_block::PersonBlock,
|
||||||
post::{Post, PostRead, PostReadForm},
|
post::{Post, PostRead, PostReadForm},
|
||||||
|
registration_application::RegistrationApplication,
|
||||||
secret::Secret,
|
secret::Secret,
|
||||||
site::Site,
|
site::Site,
|
||||||
},
|
},
|
||||||
|
@ -426,8 +427,8 @@ pub async fn send_verification_email(
|
||||||
let body = format!(
|
let body = format!(
|
||||||
concat!(
|
concat!(
|
||||||
"Please click the link below to verify your email address ",
|
"Please click the link below to verify your email address ",
|
||||||
"for the account @{}@{}. Ignore this email if the account isn't yours.\n\n",
|
"for the account @{}@{}. Ignore this email if the account isn't yours.<br><br>",
|
||||||
"<a href=\"{}\"></a>"
|
"<a href=\"{}\">Verify your email</a>"
|
||||||
),
|
),
|
||||||
username, settings.hostname, verify_link
|
username, settings.hostname, verify_link
|
||||||
);
|
);
|
||||||
|
@ -441,7 +442,7 @@ pub fn send_email_verification_success(
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let email = &local_user_view.local_user.email.to_owned().expect("email");
|
let email = &local_user_view.local_user.email.to_owned().expect("email");
|
||||||
let subject = &format!("Email verified for {}", local_user_view.person.name);
|
let subject = &format!("Email verified for {}", local_user_view.person.actor_id);
|
||||||
let html = "Your email has been verified.";
|
let html = "Your email has been verified.";
|
||||||
send_email(subject, email, &local_user_view.person.name, html, settings)
|
send_email(subject, email, &local_user_view.person.name, html, settings)
|
||||||
}
|
}
|
||||||
|
@ -452,8 +453,8 @@ pub fn send_application_approved_email(
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let email = &local_user_view.local_user.email.to_owned().expect("email");
|
let email = &local_user_view.local_user.email.to_owned().expect("email");
|
||||||
let subject = &format!(
|
let subject = &format!(
|
||||||
"Registration to {} approved for {}",
|
"Registration approved for {}",
|
||||||
settings.hostname, local_user_view.person.name
|
local_user_view.person.actor_id
|
||||||
);
|
);
|
||||||
let html = &format!(
|
let html = &format!(
|
||||||
"Your registration application has been approved. Welcome to {}!",
|
"Your registration application has been approved. Welcome to {}!",
|
||||||
|
@ -461,3 +462,24 @@ pub fn send_application_approved_email(
|
||||||
);
|
);
|
||||||
send_email(subject, email, &local_user_view.person.name, html, settings)
|
send_email(subject, email, &local_user_view.person.name, html, settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn check_registration_application(
|
||||||
|
site: &Site,
|
||||||
|
local_user_view: &LocalUserView,
|
||||||
|
pool: &DbPool,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
if site.require_application && !local_user_view.local_user.accepted_application {
|
||||||
|
// Fetch the registration, see if its denied
|
||||||
|
let local_user_id = local_user_view.local_user.id;
|
||||||
|
let registration = blocking(pool, move |conn| {
|
||||||
|
RegistrationApplication::find_by_local_user_id(conn, local_user_id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
if registration.deny_reason.is_some() {
|
||||||
|
return Err(LemmyError::from_message("registration_denied"));
|
||||||
|
} else {
|
||||||
|
return Err(LemmyError::from_message("registration_application_pending"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -293,6 +293,3 @@ pub struct GetUnreadCountResponse {
|
||||||
pub struct VerifyEmail {
|
pub struct VerifyEmail {
|
||||||
pub token: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct VerifyEmailResponse {}
|
|
||||||
|
|
|
@ -147,12 +147,6 @@ impl PerformCrud for GetSite {
|
||||||
person_blocks,
|
person_blocks,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// If the site is setup, private, and there is no auth, return an error
|
|
||||||
if let Some(site_view) = site_view.to_owned() {
|
|
||||||
if site_view.site.private_instance {
|
|
||||||
return Err(LemmyError::from_message("instance_is_private"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -257,8 +257,24 @@ impl PerformCrud for Register {
|
||||||
.map_err(|e| e.with_message("community_moderator_already_exists"))?;
|
.map_err(|e| e.with_message("community_moderator_already_exists"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the user in directly if email verification is not required
|
let mut login_response = LoginResponse {
|
||||||
let login_response = if email_verification {
|
jwt: None,
|
||||||
|
registration_created: false,
|
||||||
|
verify_email_sent: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Log the user in directly if email verification and application aren't required
|
||||||
|
if !require_application && !email_verification {
|
||||||
|
login_response.jwt = Some(
|
||||||
|
Claims::jwt(
|
||||||
|
inserted_local_user.id.0,
|
||||||
|
&context.secret().jwt_secret,
|
||||||
|
&context.settings().hostname,
|
||||||
|
)?
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if email_verification {
|
||||||
send_verification_email(
|
send_verification_email(
|
||||||
inserted_local_user.id,
|
inserted_local_user.id,
|
||||||
// we check at the beginning of this method that email is set
|
// we check at the beginning of this method that email is set
|
||||||
|
@ -268,31 +284,13 @@ impl PerformCrud for Register {
|
||||||
&context.settings(),
|
&context.settings(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
LoginResponse {
|
login_response.verify_email_sent = true;
|
||||||
jwt: None,
|
|
||||||
verify_email_sent: true,
|
|
||||||
registration_created: false,
|
|
||||||
}
|
}
|
||||||
} else if require_application {
|
|
||||||
LoginResponse {
|
if require_application {
|
||||||
jwt: None,
|
login_response.registration_created = true;
|
||||||
verify_email_sent: false,
|
|
||||||
registration_created: true,
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
LoginResponse {
|
|
||||||
jwt: Some(
|
|
||||||
Claims::jwt(
|
|
||||||
inserted_local_user.id.0,
|
|
||||||
&context.secret().jwt_secret,
|
|
||||||
&context.settings().hostname,
|
|
||||||
)?
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
verify_email_sent: false,
|
|
||||||
registration_created: false,
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Ok(login_response)
|
Ok(login_response)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{source::email_verification::*, traits::Crud};
|
use crate::{newtypes::LocalUserId, source::email_verification::*, traits::Crud};
|
||||||
use diesel::{insert_into, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
|
use diesel::{insert_into, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
|
||||||
|
|
||||||
impl Crud for EmailVerification {
|
impl Crud for EmailVerification {
|
||||||
|
@ -36,4 +36,11 @@ impl EmailVerification {
|
||||||
.filter(verification_token.eq(token))
|
.filter(verification_token.eq(token))
|
||||||
.first::<Self>(conn)
|
.first::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
pub fn delete_old_tokens_for_local_user(
|
||||||
|
conn: &PgConnection,
|
||||||
|
local_user_id_: LocalUserId,
|
||||||
|
) -> Result<usize, Error> {
|
||||||
|
use crate::schema::email_verification::dsl::*;
|
||||||
|
diesel::delete(email_verification.filter(local_user_id.eq(local_user_id_))).execute(conn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{source::registration_application::*, traits::Crud};
|
use crate::{newtypes::LocalUserId, source::registration_application::*, traits::Crud};
|
||||||
use diesel::{insert_into, result::Error, PgConnection, QueryDsl, RunQueryDsl};
|
use diesel::{insert_into, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
|
||||||
|
|
||||||
impl Crud for RegistrationApplication {
|
impl Crud for RegistrationApplication {
|
||||||
type Form = RegistrationApplicationForm;
|
type Form = RegistrationApplicationForm;
|
||||||
|
@ -28,3 +28,15 @@ impl Crud for RegistrationApplication {
|
||||||
diesel::delete(registration_application.find(id_)).execute(conn)
|
diesel::delete(registration_application.find(id_)).execute(conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RegistrationApplication {
|
||||||
|
pub fn find_by_local_user_id(
|
||||||
|
conn: &PgConnection,
|
||||||
|
local_user_id_: LocalUserId,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
use crate::schema::registration_application::dsl::*;
|
||||||
|
registration_application
|
||||||
|
.filter(local_user_id.eq(local_user_id_))
|
||||||
|
.first::<Self>(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -43,7 +43,9 @@ impl fmt::Display for CommentId {
|
||||||
)]
|
)]
|
||||||
pub struct CommunityId(pub i32);
|
pub struct CommunityId(pub i32);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
|
#[derive(
|
||||||
|
Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize, DieselNewType,
|
||||||
|
)]
|
||||||
pub struct LocalUserId(pub i32);
|
pub struct LocalUserId(pub i32);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
|
||||||
|
|
|
@ -29,6 +29,12 @@ pub fn send_email(
|
||||||
|
|
||||||
let (smtp_server, smtp_port) = {
|
let (smtp_server, smtp_port) = {
|
||||||
let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();
|
let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();
|
||||||
|
if email_and_port.len() == 1 {
|
||||||
|
return Err(LemmyError::from_message(
|
||||||
|
"email.smtp_server needs a port, IE smtp.xxx.com:465",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
email_and_port[0],
|
email_and_port[0],
|
||||||
email_and_port[1]
|
email_and_port[1]
|
||||||
|
|
|
@ -11,5 +11,4 @@ create table email_verification (
|
||||||
local_user_id int references local_user(id) on update cascade on delete cascade not null,
|
local_user_id int references local_user(id) on update cascade on delete cascade not null,
|
||||||
email text not null,
|
email text not null,
|
||||||
verification_token text not null
|
verification_token text not null
|
||||||
|
|
||||||
);
|
);
|
Loading…
Reference in New Issue