use crate::{request::purge_image_from_pictrs, sensitive::Sensitive, site::FederatedInstances};
+use anyhow::Context;
use chrono::NaiveDateTime;
use lemmy_db_schema::{
impls::person::is_banned,
- newtypes::{CommunityId, LocalUserId, PersonId, PostId},
+ newtypes::{CommunityId, DbUrl, LocalUserId, PersonId, PostId},
source::{
comment::{Comment, CommentUpdateForm},
community::{Community, CommunityUpdateForm},
email_verification::{EmailVerification, EmailVerificationForm},
instance::Instance,
- local_site::LocalSite,
+ local_site::{LocalSite, RegistrationMode},
local_site_rate_limit::LocalSiteRateLimit,
password_reset_request::PasswordResetRequest,
person::{Person, PersonUpdateForm},
CommunityModeratorView,
CommunityPersonBanView,
CommunityView,
+ PersonViewSafe,
};
use lemmy_utils::{
claims::Claims,
email::{send_email, translations::Lang},
error::LemmyError,
+ location_info,
rate_limit::RateLimitConfig,
settings::structs::Settings,
- utils::{build_slur_regex, generate_random_string},
+ utils::slurs::build_slur_regex,
};
use regex::Regex;
use reqwest_middleware::ClientWithMiddleware;
use rosetta_i18n::{Language, LanguageId};
use std::str::FromStr;
use tracing::warn;
+use url::{ParseError, Url};
#[tracing::instrument(skip_all)]
pub async fn is_mod_or_admin(
Ok(())
}
+pub async fn is_top_admin(pool: &DbPool, person_id: PersonId) -> Result<(), LemmyError> {
+ let admins = PersonViewSafe::admins(pool).await?;
+ let top_admin = admins
+ .get(0)
+ .ok_or_else(|| LemmyError::from_message("no admins"))?;
+
+ if top_admin.person.id != person_id {
+ return Err(LemmyError::from_message("not_top_admin"));
+ }
+ Ok(())
+}
+
pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> {
if !local_user_view.person.admin {
return Err(LemmyError::from_message("not_an_admin"));
/// Checks for a honeypot. If this field is filled, fail the rest of the function
pub fn honeypot_check(honeypot: &Option<String>) -> Result<(), LemmyError> {
- if honeypot.is_some() {
+ if honeypot.is_some() && honeypot != &Some(String::new()) {
Err(LemmyError::from_message("honeypot_fail"))
} else {
Ok(())
settings: &Settings,
) -> Result<(), LemmyError> {
// Generate a random token
- let token = generate_random_string();
+ let token = uuid::Uuid::new_v4().to_string();
// Insert the row
let token2 = token.clone();
let local_user_id = user.local_user.id;
PasswordResetRequest::create_token(pool, local_user_id, &token2).await?;
- let email = &user.local_user.email.to_owned().expect("email");
+ let email = &user.local_user.email.clone().expect("email");
let lang = get_interface_language(user);
let subject = &lang.password_reset_subject(&user.person.name);
let protocol_and_hostname = settings.get_protocol_and_hostname();
let form = EmailVerificationForm {
local_user_id: user.local_user.id,
email: new_email.to_string(),
- verification_token: generate_random_string(),
+ verification_token: uuid::Uuid::new_v4().to_string(),
};
let verify_link = format!(
"{}/verify_email/{}",
user: &LocalUserView,
settings: &Settings,
) -> Result<(), LemmyError> {
- let email = &user.local_user.email.to_owned().expect("email");
+ let email = &user.local_user.email.clone().expect("email");
let lang = get_interface_language(user);
let subject = &lang.email_verified_subject(&user.person.actor_id);
let body = &lang.email_verified_body();
user: &LocalUserView,
settings: &Settings,
) -> Result<(), LemmyError> {
- let email = &user.local_user.email.to_owned().expect("email");
+ let email = &user.local_user.email.clone().expect("email");
let lang = get_interface_language(user);
let subject = lang.registration_approved_subject(&user.person.actor_id);
let body = lang.registration_approved_body(&settings.hostname);
);
for admin in &admins {
- let email = &admin.local_user.email.to_owned().expect("email");
+ let email = &admin.local_user.email.clone().expect("email");
let lang = get_interface_language_from_settings(admin);
- let subject = lang.new_application_subject(applicant_username, &settings.hostname);
+ let subject = lang.new_application_subject(&settings.hostname, applicant_username);
let body = lang.new_application_body(applications_link);
send_email(&subject, email, &admin.person.name, &body, settings)?;
}
Ok(())
}
+/// Send a report to all admins
+pub async fn send_new_report_email_to_admins(
+ reporter_username: &str,
+ reported_username: &str,
+ pool: &DbPool,
+ settings: &Settings,
+) -> Result<(), LemmyError> {
+ // Collect the admins with emails
+ let admins = LocalUserSettingsView::list_admins_with_emails(pool).await?;
+
+ let reports_link = &format!("{}/reports", settings.get_protocol_and_hostname(),);
+
+ for admin in &admins {
+ let email = &admin.local_user.email.clone().expect("email");
+ let lang = get_interface_language_from_settings(admin);
+ let subject = lang.new_report_subject(&settings.hostname, reporter_username, reported_username);
+ let body = lang.new_report_body(reports_link);
+ send_email(&subject, email, &admin.person.name, &body, settings)?;
+ }
+ Ok(())
+}
+
pub async fn check_registration_application(
local_user_view: &LocalUserView,
local_site: &LocalSite,
pool: &DbPool,
) -> Result<(), LemmyError> {
- if local_site.require_application
+ if local_site.registration_mode == RegistrationMode::RequireApplication
&& !local_user_view.local_user.accepted_application
&& !local_user_view.person.admin
{
#[cfg(test)]
mod tests {
- use crate::utils::password_length_check;
+ use crate::utils::{honeypot_check, password_length_check};
#[test]
#[rustfmt::skip]
assert!(password_length_check("short").is_err());
assert!(password_length_check("looooooooooooooooooooooooooooooooooooooooooooooooooooooooooong").is_err());
}
+
+ #[test]
+ fn honeypot() {
+ assert!(honeypot_check(&None).is_ok());
+ assert!(honeypot_check(&Some(String::new())).is_ok());
+ assert!(honeypot_check(&Some("1".to_string())).is_err());
+ assert!(honeypot_check(&Some("message".to_string())).is_err());
+ }
+}
+
+pub enum EndpointType {
+ Community,
+ Person,
+ Post,
+ Comment,
+ PrivateMessage,
+}
+
+/// Generates an apub endpoint for a given domain, IE xyz.tld
+pub fn generate_local_apub_endpoint(
+ endpoint_type: EndpointType,
+ name: &str,
+ domain: &str,
+) -> Result<DbUrl, ParseError> {
+ let point = match endpoint_type {
+ EndpointType::Community => "c",
+ EndpointType::Person => "u",
+ EndpointType::Post => "post",
+ EndpointType::Comment => "comment",
+ EndpointType::PrivateMessage => "private_message",
+ };
+
+ Ok(Url::parse(&format!("{domain}/{point}/{name}"))?.into())
+}
+
+pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+ Ok(Url::parse(&format!("{actor_id}/followers"))?.into())
+}
+
+pub fn generate_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+ Ok(Url::parse(&format!("{actor_id}/inbox"))?.into())
+}
+
+pub fn generate_site_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+ let mut actor_id: Url = actor_id.clone().into();
+ actor_id.set_path("site_inbox");
+ Ok(actor_id.into())
+}
+
+pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError> {
+ let actor_id: Url = actor_id.clone().into();
+ let url = format!(
+ "{}://{}{}/inbox",
+ &actor_id.scheme(),
+ &actor_id.host_str().context(location_info!())?,
+ if let Some(port) = actor_id.port() {
+ format!(":{port}")
+ } else {
+ String::new()
+ },
+ );
+ Ok(Url::parse(&url)?.into())
+}
+
+pub fn generate_outbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+ Ok(Url::parse(&format!("{actor_id}/outbox"))?.into())
+}
+
+pub fn generate_featured_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+ Ok(Url::parse(&format!("{actor_id}/featured"))?.into())
+}
+
+pub fn generate_moderators_url(community_id: &DbUrl) -> Result<DbUrl, LemmyError> {
+ Ok(Url::parse(&format!("{community_id}/moderators"))?.into())
}