Use enum for registration mode setting (#2604)

* Use enum for registration mode setting

* fix tests
This commit is contained in:
Nutomic 2023-01-05 01:42:30 +00:00 committed by GitHub
parent 0630d214e3
commit ceff2ec686
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 186 additions and 72 deletions

View File

@ -18,7 +18,7 @@
"eslint": "^8.25.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^27.0.6",
"lemmy-js-client": "0.17.0-rc.60",
"lemmy-js-client": "0.17.0-rc.61",
"node-fetch": "^2.6.1",
"prettier": "^2.7.1",
"ts-jest": "^27.0.3",

View File

@ -64,6 +64,7 @@ import {
GetCommentsResponse,
FeaturePost,
PostFeatureType,
RegistrationMode,
} from "lemmy-js-client";
export interface API {
@ -145,7 +146,7 @@ export async function setupLogins() {
// Registration applications are now enabled by default, need to disable them
let editSiteForm: EditSite = {
require_application: false,
registration_mode: RegistrationMode.Open,
federation_debug: true,
rate_limit_message: 999,
rate_limit_post: 999,

View File

@ -2363,10 +2363,10 @@ kleur@^3.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
lemmy-js-client@0.17.0-rc.60:
version "0.17.0-rc.60"
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.60.tgz#6cabac42d842eb1f152d230be018090050476614"
integrity sha512-Nl+DUBJde0KpKywNkX5wof9PDCmr6RuJlgukVfQRmW5rznYRKYnI1V+awf+9mUx1eNQ8jhnjVO+6XxOzMjloZA==
lemmy-js-client@0.17.0-rc.61:
version "0.17.0-rc.61"
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.61.tgz#c01e129a3d4c3483ecf337f1e4acf0ad91f9684f"
integrity sha512-xauBCD5i4vlUEWqsTMIXLCXeIjAK7ivVIN3C/g+RMAM7mD3CTcRkDZUerwnvLipIfr7V/4iYLWZW0orBaiV1CQ==
dependencies:
node-fetch "2.6.6"

View File

@ -6,6 +6,7 @@ use lemmy_api_common::{
utils::password_length_check,
};
use lemmy_db_schema::source::{
local_site::RegistrationMode,
local_user::LocalUser,
password_reset_request::PasswordResetRequest,
};
@ -45,19 +46,20 @@ impl Perform for PasswordChangeAfterReset {
// Return the jwt if login is allowed
let site_view = SiteView::read_local(context.pool()).await?;
let jwt =
if site_view.local_site.require_application && !updated_local_user.accepted_application {
None
} else {
Some(
Claims::jwt(
updated_local_user.id.0,
&context.secret().jwt_secret,
&context.settings().hostname,
)?
.into(),
)
};
let jwt = if site_view.local_site.registration_mode == RegistrationMode::RequireApplication
&& !updated_local_user.accepted_application
{
None
} else {
Some(
Claims::jwt(
updated_local_user.id.0,
&context.secret().jwt_secret,
&context.settings().hostname,
)?
.into(),
)
};
Ok(LoginResponse {
jwt,

View File

@ -1,7 +1,7 @@
use crate::sensitive::Sensitive;
use lemmy_db_schema::{
newtypes::{CommentId, CommunityId, LanguageId, PersonId, PostId},
source::{language::Language, tagline::Tagline},
source::{language::Language, local_site::RegistrationMode, tagline::Tagline},
ListingType,
ModlogActionType,
SearchType,
@ -116,11 +116,9 @@ pub struct CreateSite {
pub icon: Option<String>,
pub banner: Option<String>,
pub enable_downvotes: Option<bool>,
pub open_registration: Option<bool>,
pub enable_nsfw: Option<bool>,
pub community_creation_admin_only: Option<bool>,
pub require_email_verification: Option<bool>,
pub require_application: Option<bool>,
pub application_question: Option<String>,
pub private_instance: Option<bool>,
pub default_theme: Option<String>,
@ -151,6 +149,7 @@ pub struct CreateSite {
pub allowed_instances: Option<Vec<String>>,
pub blocked_instances: Option<Vec<String>>,
pub taglines: Option<Vec<String>>,
pub registration_mode: Option<RegistrationMode>,
pub auth: Sensitive<String>,
}
@ -162,11 +161,9 @@ pub struct EditSite {
pub icon: Option<String>,
pub banner: Option<String>,
pub enable_downvotes: Option<bool>,
pub open_registration: Option<bool>,
pub enable_nsfw: Option<bool>,
pub community_creation_admin_only: Option<bool>,
pub require_email_verification: Option<bool>,
pub require_application: Option<bool>,
pub application_question: Option<String>,
pub private_instance: Option<bool>,
pub default_theme: Option<String>,
@ -197,6 +194,7 @@ pub struct EditSite {
pub allowed_instances: Option<Vec<String>>,
pub blocked_instances: Option<Vec<String>>,
pub taglines: Option<Vec<String>>,
pub registration_mode: Option<RegistrationMode>,
pub auth: Sensitive<String>,
}

View File

@ -9,7 +9,7 @@ use lemmy_db_schema::{
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},
@ -488,7 +488,7 @@ pub async fn check_registration_application(
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
{

View File

@ -1,6 +1,5 @@
use actix_web::web::Data;
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_utils::{error::LemmyError, ConnectionId};
mod comment;
@ -20,18 +19,3 @@ pub trait PerformCrud {
websocket_id: Option<ConnectionId>,
) -> Result<Self::Response, LemmyError>;
}
/// Make sure if applications are required, that there is an application questionnaire
pub fn check_application_question(
application_question: &Option<Option<String>>,
local_site: &LocalSite,
require_application: &Option<bool>,
) -> Result<(), LemmyError> {
if require_application.unwrap_or(false)
&& (application_question == &Some(None)
|| (application_question.is_none() && local_site.application_question.is_none()))
{
return Err(LemmyError::from_message("application_question_required"));
}
Ok(())
}

View File

@ -1,4 +1,4 @@
use crate::{check_application_question, PerformCrud};
use crate::{site::check_application_question, PerformCrud};
use activitypub_federation::core::signatures::generate_actor_keypair;
use actix_web::web::Data;
use lemmy_api_common::{
@ -71,8 +71,9 @@ impl PerformCrud for CreateSite {
let application_question = diesel_option_overwrite(&data.application_question);
check_application_question(
&application_question,
&local_site,
&data.require_application,
data
.registration_mode
.unwrap_or(local_site.registration_mode),
)?;
let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
@ -99,11 +100,10 @@ impl PerformCrud for CreateSite {
// Set the site setup to true
.site_setup(Some(true))
.enable_downvotes(data.enable_downvotes)
.open_registration(data.open_registration)
.registration_mode(data.registration_mode)
.enable_nsfw(data.enable_nsfw)
.community_creation_admin_only(data.community_creation_admin_only)
.require_email_verification(data.require_email_verification)
.require_application(data.require_application)
.application_question(application_question)
.private_instance(data.private_instance)
.default_theme(data.default_theme.clone())

View File

@ -1,3 +1,19 @@
use lemmy_db_schema::source::local_site::RegistrationMode;
use lemmy_utils::error::LemmyError;
mod create;
mod read;
mod update;
pub fn check_application_question(
application_question: &Option<Option<String>>,
registration_mode: RegistrationMode,
) -> Result<(), LemmyError> {
if registration_mode == RegistrationMode::RequireApplication
&& application_question.as_ref().unwrap_or(&None).is_none()
{
Err(LemmyError::from_message("application_question_required"))
} else {
Ok(())
}
}

View File

@ -1,4 +1,4 @@
use crate::{check_application_question, PerformCrud};
use crate::{site::check_application_question, PerformCrud};
use actix_web::web::Data;
use lemmy_api_common::{
context::LemmyContext,
@ -17,7 +17,7 @@ use lemmy_db_schema::{
actor_language::SiteLanguage,
federation_allowlist::FederationAllowList,
federation_blocklist::FederationBlockList,
local_site::{LocalSite, LocalSiteUpdateForm},
local_site::{LocalSite, LocalSiteUpdateForm, RegistrationMode},
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm},
local_user::LocalUser,
site::{Site, SiteUpdateForm},
@ -61,8 +61,9 @@ impl PerformCrud for EditSite {
let application_question = diesel_option_overwrite(&data.application_question);
check_application_question(
&application_question,
&local_site,
&data.require_application,
data
.registration_mode
.unwrap_or(local_site.registration_mode),
)?;
if let Some(default_post_listing_type) = &data.default_post_listing_type {
@ -99,11 +100,10 @@ impl PerformCrud for EditSite {
let local_site_form = LocalSiteUpdateForm::builder()
.enable_downvotes(data.enable_downvotes)
.open_registration(data.open_registration)
.registration_mode(data.registration_mode)
.enable_nsfw(data.enable_nsfw)
.community_creation_admin_only(data.community_creation_admin_only)
.require_email_verification(data.require_email_verification)
.require_application(data.require_application)
.application_question(application_question)
.private_instance(data.private_instance)
.default_theme(data.default_theme.clone())
@ -155,11 +155,13 @@ impl PerformCrud for EditSite {
// will be able to log in. It really only wants this to be a requirement for NEW signups.
// So if it was set from false, to true, you need to update all current users columns to be verified.
let old_require_application =
local_site.registration_mode == RegistrationMode::RequireApplication;
let new_require_application = update_local_site
.as_ref()
.map(|ols| ols.require_application)
.map(|ols| ols.registration_mode == RegistrationMode::RequireApplication)
.unwrap_or(false);
if !local_site.require_application && new_require_application {
if !old_require_application && new_require_application {
LocalUser::set_all_users_registration_applications_accepted(context.pool())
.await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_set_all_registrations_accepted"))?;

View File

@ -19,6 +19,7 @@ use lemmy_api_common::{
use lemmy_db_schema::{
aggregates::structs::PersonAggregates,
source::{
local_site::RegistrationMode,
local_user::{LocalUser, LocalUserInsertForm},
person::{Person, PersonInsertForm},
registration_application::{RegistrationApplication, RegistrationApplicationInsertForm},
@ -47,8 +48,10 @@ impl PerformCrud for Register {
let site_view = SiteView::read_local(context.pool()).await?;
let local_site = site_view.local_site;
let require_registration_application =
local_site.registration_mode == RegistrationMode::RequireApplication;
if !local_site.open_registration {
if local_site.registration_mode == RegistrationMode::Closed {
return Err(LemmyError::from_message("registration_closed"));
}
@ -59,7 +62,7 @@ impl PerformCrud for Register {
return Err(LemmyError::from_message("email_required"));
}
if local_site.site_setup && local_site.require_application && data.answer.is_none() {
if local_site.site_setup && require_registration_application && data.answer.is_none() {
return Err(LemmyError::from_message(
"registration_application_answer_required",
));
@ -141,7 +144,7 @@ impl PerformCrud for Register {
}
};
if local_site.site_setup && local_site.require_application {
if local_site.site_setup && require_registration_application {
// Create the registration application
let form = RegistrationApplicationInsertForm {
local_user_id: inserted_local_user.id,
@ -166,7 +169,7 @@ impl PerformCrud for Register {
// Log the user in directly if the site is not setup, or email verification and application aren't required
if !local_site.site_setup
|| (!local_site.require_application && !local_site.require_email_verification)
|| (!require_registration_application && !local_site.require_email_verification)
{
login_response.jwt = Some(
Claims::jwt(
@ -195,7 +198,7 @@ impl PerformCrud for Register {
login_response.verify_email_sent = true;
}
if local_site.require_application {
if require_registration_application {
login_response.registration_created = true;
}
}

View File

@ -1,10 +1,25 @@
use crate::{
schema::local_site::dsl::local_site,
source::local_site::{LocalSite, LocalSiteInsertForm, LocalSiteUpdateForm},
source::local_site::{
LocalSite,
LocalSiteInsertForm,
LocalSiteUpdateForm,
RegistrationMode,
RegistrationModeType,
},
utils::{get_conn, DbPool},
};
use diesel::{dsl::insert_into, result::Error};
use diesel::{
deserialize,
deserialize::FromSql,
dsl::insert_into,
pg::{Pg, PgValue},
result::Error,
serialize,
serialize::{IsNull, Output, ToSql},
};
use diesel_async::RunQueryDsl;
use std::io::Write;
impl LocalSite {
pub async fn create(pool: &DbPool, form: &LocalSiteInsertForm) -> Result<Self, Error> {
@ -30,3 +45,25 @@ impl LocalSite {
diesel::delete(local_site).execute(conn).await
}
}
impl ToSql<RegistrationModeType, Pg> for RegistrationMode {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
match *self {
RegistrationMode::Closed => out.write_all(b"closed")?,
RegistrationMode::RequireApplication => out.write_all(b"require_application")?,
RegistrationMode::Open => out.write_all(b"open")?,
}
Ok(IsNull::No)
}
}
impl FromSql<RegistrationModeType, Pg> for RegistrationMode {
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
match bytes.as_bytes() {
b"closed" => Ok(RegistrationMode::Closed),
b"require_application" => Ok(RegistrationMode::RequireApplication),
b"open" => Ok(RegistrationMode::Open),
_ => Err("Unrecognized enum variant".into()),
}
}
}

View File

@ -672,16 +672,17 @@ table! {
}
table! {
use crate::source::local_site::RegistrationModeType;
use diesel::sql_types::*;
local_site(id) {
id -> Int4,
site_id -> Int4,
site_setup -> Bool,
enable_downvotes -> Bool,
open_registration -> Bool,
enable_nsfw -> Bool,
community_creation_admin_only -> Bool,
require_email_verification -> Bool,
require_application -> Bool,
application_question -> Nullable<Text>,
private_instance -> Bool,
default_theme -> Text,
@ -696,6 +697,7 @@ table! {
federation_worker_count -> Int4,
captcha_enabled -> Bool,
captcha_difficulty -> Text,
registration_mode -> RegistrationModeType,
published -> Timestamp,
updated -> Nullable<Timestamp>,
}

View File

@ -13,11 +13,9 @@ pub struct LocalSite {
pub site_id: SiteId,
pub site_setup: bool,
pub enable_downvotes: bool,
pub open_registration: bool,
pub enable_nsfw: bool,
pub community_creation_admin_only: bool,
pub require_email_verification: bool,
pub require_application: bool,
pub application_question: Option<String>,
pub private_instance: bool,
pub default_theme: String,
@ -32,6 +30,7 @@ pub struct LocalSite {
pub federation_worker_count: i32,
pub captcha_enabled: bool,
pub captcha_difficulty: String,
pub registration_mode: RegistrationMode,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
}
@ -45,11 +44,9 @@ pub struct LocalSiteInsertForm {
pub site_id: SiteId,
pub site_setup: Option<bool>,
pub enable_downvotes: Option<bool>,
pub open_registration: Option<bool>,
pub enable_nsfw: Option<bool>,
pub community_creation_admin_only: Option<bool>,
pub require_email_verification: Option<bool>,
pub require_application: Option<bool>,
pub application_question: Option<String>,
pub private_instance: Option<bool>,
pub default_theme: Option<String>,
@ -64,6 +61,7 @@ pub struct LocalSiteInsertForm {
pub federation_worker_count: Option<i32>,
pub captcha_enabled: Option<bool>,
pub captcha_difficulty: Option<String>,
pub registration_mode: Option<RegistrationMode>,
}
#[derive(Clone, TypedBuilder)]
@ -73,11 +71,9 @@ pub struct LocalSiteInsertForm {
pub struct LocalSiteUpdateForm {
pub site_setup: Option<bool>,
pub enable_downvotes: Option<bool>,
pub open_registration: Option<bool>,
pub enable_nsfw: Option<bool>,
pub community_creation_admin_only: Option<bool>,
pub require_email_verification: Option<bool>,
pub require_application: Option<bool>,
pub application_question: Option<Option<String>>,
pub private_instance: Option<bool>,
pub default_theme: Option<String>,
@ -92,5 +88,21 @@ pub struct LocalSiteUpdateForm {
pub federation_worker_count: Option<i32>,
pub captcha_enabled: Option<bool>,
pub captcha_difficulty: Option<String>,
pub registration_mode: Option<RegistrationMode>,
pub updated: Option<Option<chrono::NaiveDateTime>>,
}
#[cfg(feature = "full")]
#[derive(SqlType)]
#[diesel(postgres_type(name = "registration_mode_enum"))]
pub struct RegistrationModeType;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "full", derive(FromSqlRow, AsExpression))]
#[cfg_attr(feature = "full", diesel(sql_type = RegistrationModeType))]
#[serde(rename_all = "lowercase")]
pub enum RegistrationMode {
Closed,
RequireApplication,
Open,
}

View File

@ -1,6 +1,7 @@
use actix_web::{error::ErrorBadRequest, web, Error, HttpResponse, Result};
use anyhow::anyhow;
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::source::local_site::RegistrationMode;
use lemmy_db_views::structs::SiteView;
use lemmy_utils::{error::LemmyError, version};
use serde::{Deserialize, Serialize};
@ -37,7 +38,7 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
} else {
vec![]
};
let open_registrations = site_view.local_site.registration_mode == RegistrationMode::Open;
let json = NodeInfo {
version: "2.0".to_string(),
software: NodeInfoSoftware {
@ -54,7 +55,7 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
local_posts: site_view.counts.posts,
local_comments: site_view.counts.comments,
},
open_registrations: site_view.local_site.open_registration,
open_registrations,
};
Ok(HttpResponse::Ok().json(json))

View File

@ -0,0 +1,31 @@
-- add back old registration columns
alter table local_site add column open_registration boolean not null default true;
alter table local_site add column require_application boolean not null default true;
-- regenerate their values
with subquery as (
select registration_mode,
case
when registration_mode='closed' then false
else true
end
from local_site
)
update local_site
set open_registration = subquery.case
from subquery;
with subquery as (
select registration_mode,
case
when registration_mode='open' then false
else true
end
from local_site
)
update local_site
set require_application = subquery.case
from subquery;
-- drop new column and type
alter table local_site drop column registration_mode;
drop type registration_mode_enum;

View File

@ -0,0 +1,25 @@
-- create enum for registration modes
create type registration_mode_enum as enum
('closed', 'require_application', 'open');
-- use this enum for registration mode setting
alter table local_site add column
registration_mode registration_mode_enum not null default 'require_application';
-- generate registration mode value from previous settings
with subquery as (
select open_registration, require_application,
case
when open_registration=false then 'closed'::registration_mode_enum
when open_registration=true and require_application=true then 'require_application'
else 'open'
end
from local_site
)
update local_site
set registration_mode = subquery.case
from subquery;
-- drop old registration settings
alter table local_site drop column open_registration;
alter table local_site drop column require_application;