From e364e31e25bd1d4b8d801c845a96d6be708f0a18 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 19 Jan 2023 09:27:16 +0100 Subject: Implement signup approval in server --- server/initializers/checker-after-init.ts | 5 ++ server/initializers/checker-before-init.ts | 2 +- server/initializers/config.ts | 1 + server/initializers/constants.ts | 20 ++++++-- server/initializers/database.ts | 6 ++- .../migrations/0750-user-registration.ts | 58 ++++++++++++++++++++++ 6 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 server/initializers/migrations/0750-user-registration.ts (limited to 'server/initializers') diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts index dc46b5126..247bc2ad5 100644 --- a/server/initializers/checker-after-init.ts +++ b/server/initializers/checker-after-init.ts @@ -116,6 +116,11 @@ function checkEmailConfig () { throw new Error('Emailer is disabled but you require signup email verification.') } + if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_APPROVAL) { + // eslint-disable-next-line max-len + logger.warn('Emailer is disabled but signup approval is enabled: PeerTube will not be able to send an email to the user upon acceptance/rejection of the registration request') + } + if (CONFIG.CONTACT_FORM.ENABLED) { logger.warn('Emailer is disabled so the contact form will not work.') } diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts index 57852241c..8b4d49180 100644 --- a/server/initializers/checker-before-init.ts +++ b/server/initializers/checker-before-init.ts @@ -28,7 +28,7 @@ function checkMissedConfig () { 'csp.enabled', 'csp.report_only', 'csp.report_uri', 'security.frameguard.enabled', 'cache.previews.size', 'cache.captions.size', 'cache.torrents.size', 'admin.email', 'contact_form.enabled', - 'signup.enabled', 'signup.limit', 'signup.requires_email_verification', 'signup.minimum_age', + 'signup.enabled', 'signup.limit', 'signup.requires_approval', 'signup.requires_email_verification', 'signup.minimum_age', 'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist', 'redundancy.videos.strategies', 'redundancy.videos.check_interval', 'transcoding.enabled', 'transcoding.threads', 'transcoding.allow_additional_extensions', 'transcoding.hls.enabled', diff --git a/server/initializers/config.ts b/server/initializers/config.ts index 28aaf36a9..9685e7bfc 100644 --- a/server/initializers/config.ts +++ b/server/initializers/config.ts @@ -305,6 +305,7 @@ const CONFIG = { }, SIGNUP: { get ENABLED () { return config.get('signup.enabled') }, + get REQUIRES_APPROVAL () { return config.get('signup.requires_approval') }, get LIMIT () { return config.get('signup.limit') }, get REQUIRES_EMAIL_VERIFICATION () { return config.get('signup.requires_email_verification') }, get MINIMUM_AGE () { return config.get('signup.minimum_age') }, diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 0dab524d9..2ef3da2e7 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -6,6 +6,7 @@ import { randomInt, root } from '@shared/core-utils' import { AbuseState, JobType, + UserRegistrationState, VideoChannelSyncState, VideoImportState, VideoPrivacy, @@ -25,7 +26,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' // --------------------------------------------------------------------------- -const LAST_MIGRATION_VERSION = 745 +const LAST_MIGRATION_VERSION = 750 // --------------------------------------------------------------------------- @@ -78,6 +79,8 @@ const SORTABLE_COLUMNS = { ACCOUNT_FOLLOWERS: [ 'createdAt' ], CHANNEL_FOLLOWERS: [ 'createdAt' ], + USER_REGISTRATIONS: [ 'createdAt', 'state' ], + VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'originallyPublishedAt', 'views', 'likes', 'trending', 'hot', 'best' ], // Don't forget to update peertube-search-index with the same values @@ -290,6 +293,10 @@ const CONSTRAINTS_FIELDS = { ABUSE_MESSAGES: { MESSAGE: { min: 2, max: 3000 } // Length }, + USER_REGISTRATIONS: { + REASON_MESSAGE: { min: 2, max: 3000 }, // Length + MODERATOR_MESSAGE: { min: 2, max: 3000 } // Length + }, VIDEO_BLACKLIST: { REASON: { min: 2, max: 300 } // Length }, @@ -516,6 +523,12 @@ const ABUSE_STATES: { [ id in AbuseState ]: string } = { [AbuseState.ACCEPTED]: 'Accepted' } +const USER_REGISTRATION_STATES: { [ id in UserRegistrationState ]: string } = { + [UserRegistrationState.PENDING]: 'Pending', + [UserRegistrationState.REJECTED]: 'Rejected', + [UserRegistrationState.ACCEPTED]: 'Accepted' +} + const VIDEO_PLAYLIST_PRIVACIES: { [ id in VideoPlaylistPrivacy ]: string } = { [VideoPlaylistPrivacy.PUBLIC]: 'Public', [VideoPlaylistPrivacy.UNLISTED]: 'Unlisted', @@ -660,7 +673,7 @@ const USER_PASSWORD_CREATE_LIFETIME = 60000 * 60 * 24 * 7 // 7 days const TWO_FACTOR_AUTH_REQUEST_TOKEN_LIFETIME = 60000 * 10 // 10 minutes -const USER_EMAIL_VERIFY_LIFETIME = 60000 * 60 // 60 minutes +const EMAIL_VERIFY_LIFETIME = 60000 * 60 // 60 minutes const NSFW_POLICY_TYPES: { [ id: string ]: NSFWPolicyType } = { DO_NOT_LIST: 'do_not_list', @@ -1069,13 +1082,14 @@ export { VIDEO_TRANSCODING_FPS, FFMPEG_NICE, ABUSE_STATES, + USER_REGISTRATION_STATES, LRU_CACHE, REQUEST_TIMEOUTS, MAX_LOCAL_VIEWER_WATCH_SECTIONS, USER_PASSWORD_RESET_LIFETIME, USER_PASSWORD_CREATE_LIFETIME, MEMOIZE_TTL, - USER_EMAIL_VERIFY_LIFETIME, + EMAIL_VERIFY_LIFETIME, OVERVIEWS, SCHEDULER_INTERVALS_MS, REPEAT_JOBS, diff --git a/server/initializers/database.ts b/server/initializers/database.ts index f55f40df0..96145f489 100644 --- a/server/initializers/database.ts +++ b/server/initializers/database.ts @@ -5,7 +5,9 @@ import { TrackerModel } from '@server/models/server/tracker' import { VideoTrackerModel } from '@server/models/server/video-tracker' import { UserModel } from '@server/models/user/user' import { UserNotificationModel } from '@server/models/user/user-notification' +import { UserRegistrationModel } from '@server/models/user/user-registration' import { UserVideoHistoryModel } from '@server/models/user/user-video-history' +import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync' import { VideoJobInfoModel } from '@server/models/video/video-job-info' import { VideoLiveSessionModel } from '@server/models/video/video-live-session' import { VideoSourceModel } from '@server/models/video/video-source' @@ -50,7 +52,6 @@ import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-pla import { VideoTagModel } from '../models/video/video-tag' import { VideoViewModel } from '../models/view/video-view' import { CONFIG } from './config' -import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync' require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string @@ -155,7 +156,8 @@ async function initDatabaseModels (silent: boolean) { PluginModel, ActorCustomPageModel, VideoJobInfoModel, - VideoChannelSyncModel + VideoChannelSyncModel, + UserRegistrationModel ]) // Check extensions exist in the database diff --git a/server/initializers/migrations/0750-user-registration.ts b/server/initializers/migrations/0750-user-registration.ts new file mode 100644 index 000000000..15bbfd3fd --- /dev/null +++ b/server/initializers/migrations/0750-user-registration.ts @@ -0,0 +1,58 @@ + +import * as Sequelize from 'sequelize' + +async function up (utils: { + transaction: Sequelize.Transaction + queryInterface: Sequelize.QueryInterface + sequelize: Sequelize.Sequelize + db: any +}): Promise { + { + const query = ` + CREATE TABLE IF NOT EXISTS "userRegistration" ( + "id" serial, + "state" integer NOT NULL, + "registrationReason" text NOT NULL, + "moderationResponse" text, + "password" varchar(255), + "username" varchar(255) NOT NULL, + "email" varchar(400) NOT NULL, + "emailVerified" boolean, + "accountDisplayName" varchar(255), + "channelHandle" varchar(255), + "channelDisplayName" varchar(255), + "userId" integer REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE CASCADE, + "createdAt" timestamp with time zone NOT NULL, + "updatedAt" timestamp with time zone NOT NULL, + PRIMARY KEY ("id") + ); + ` + await utils.sequelize.query(query, { transaction: utils.transaction }) + } + + { + await utils.queryInterface.addColumn('userNotification', 'userRegistrationId', { + type: Sequelize.INTEGER, + defaultValue: null, + allowNull: true, + references: { + model: 'userRegistration', + key: 'id' + }, + onUpdate: 'CASCADE', + onDelete: 'SET NULL' + }, { transaction: utils.transaction }) + } +} + +async function down (utils: { + queryInterface: Sequelize.QueryInterface + transaction: Sequelize.Transaction +}) { + await utils.queryInterface.dropTable('videoChannelSync', { transaction: utils.transaction }) +} + +export { + up, + down +} -- cgit v1.2.3