signup: {
enabled: false,
limit: 5,
+ requiresApproval: false,
requiresEmailVerification: false,
minimumAge: 16
},
signup: {
enabled: true,
limit: 5,
+ requiresApproval: true,
requiresEmailVerification: true
}
}
import { MockSmtpServer } from '@server/tests/shared'
import { HttpStatusCode } from '@shared/models'
-import { cleanupTests, ContactFormCommand, createSingleServer, killallServers, PeerTubeServer } from '@shared/server-commands'
+import {
+ cleanupTests,
+ ConfigCommand,
+ ContactFormCommand,
+ createSingleServer,
+ killallServers,
+ PeerTubeServer
+} from '@shared/server-commands'
describe('Test contact form API validators', function () {
let server: PeerTubeServer
await killallServers([ server ])
// Contact form is disabled
- await server.run({ smtp: { hostname: '127.0.0.1', port: emailPort }, contact_form: { enabled: false } })
+ await server.run({ ...ConfigCommand.getEmailOverrideConfig(emailPort), contact_form: { enabled: false } })
await command.send({ ...defaultBody, expectedStatus: HttpStatusCode.CONFLICT_409 })
})
await killallServers([ server ])
// Email & contact form enabled
- await server.run({ smtp: { hostname: '127.0.0.1', port: emailPort } })
+ await server.run(ConfigCommand.getEmailOverrideConfig(emailPort))
await command.send({ ...defaultBody, fromEmail: 'badEmail', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
await command.send({ ...defaultBody, fromEmail: 'badEmail@', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
import './my-user'
import './plugins'
import './redundancy'
+import './registrations'
import './search'
import './services'
import './transcoding'
import './user-notifications'
import './user-subscriptions'
import './users-admin'
-import './users'
+import './users-emails'
import './video-blacklist'
import './video-captions'
import './video-channel-syncs'
--- /dev/null
+import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '@server/tests/shared'
+import { omit } from '@shared/core-utils'
+import { HttpStatusCode, UserRole } from '@shared/models'
+import { cleanupTests, createSingleServer, makePostBodyRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
+
+describe('Test registrations API validators', function () {
+ let server: PeerTubeServer
+ let userToken: string
+ let moderatorToken: string
+
+ // ---------------------------------------------------------------
+
+ before(async function () {
+ this.timeout(30000)
+
+ server = await createSingleServer(1)
+
+ await setAccessTokensToServers([ server ])
+ await server.config.enableSignup(false);
+
+ ({ token: moderatorToken } = await server.users.generate('moderator', UserRole.MODERATOR));
+ ({ token: userToken } = await server.users.generate('user', UserRole.USER))
+ })
+
+ describe('Register', function () {
+ const registrationPath = '/api/v1/users/register'
+ const registrationRequestPath = '/api/v1/users/registrations/request'
+
+ const baseCorrectParams = {
+ username: 'user3',
+ displayName: 'super user',
+ email: 'test3@example.com',
+ password: 'my super password',
+ registrationReason: 'my super registration reason'
+ }
+
+ describe('When registering a new user or requesting user registration', function () {
+
+ async function check (fields: any, expectedStatus = HttpStatusCode.BAD_REQUEST_400) {
+ await makePostBodyRequest({ url: server.url, path: registrationPath, fields, expectedStatus })
+ await makePostBodyRequest({ url: server.url, path: registrationRequestPath, fields, expectedStatus })
+ }
+
+ it('Should fail with a too small username', async function () {
+ const fields = { ...baseCorrectParams, username: '' }
+
+ await check(fields)
+ })
+
+ it('Should fail with a too long username', async function () {
+ const fields = { ...baseCorrectParams, username: 'super'.repeat(50) }
+
+ await check(fields)
+ })
+
+ it('Should fail with an incorrect username', async function () {
+ const fields = { ...baseCorrectParams, username: 'my username' }
+
+ await check(fields)
+ })
+
+ it('Should fail with a missing email', async function () {
+ const fields = omit(baseCorrectParams, [ 'email' ])
+
+ await check(fields)
+ })
+
+ it('Should fail with an invalid email', async function () {
+ const fields = { ...baseCorrectParams, email: 'test_example.com' }
+
+ await check(fields)
+ })
+
+ it('Should fail with a too small password', async function () {
+ const fields = { ...baseCorrectParams, password: 'bla' }
+
+ await check(fields)
+ })
+
+ it('Should fail with a too long password', async function () {
+ const fields = { ...baseCorrectParams, password: 'super'.repeat(61) }
+
+ await check(fields)
+ })
+
+ it('Should fail if we register a user with the same username', async function () {
+ const fields = { ...baseCorrectParams, username: 'root' }
+
+ await check(fields, HttpStatusCode.CONFLICT_409)
+ })
+
+ it('Should fail with a "peertube" username', async function () {
+ const fields = { ...baseCorrectParams, username: 'peertube' }
+
+ await check(fields, HttpStatusCode.CONFLICT_409)
+ })
+
+ it('Should fail if we register a user with the same email', async function () {
+ const fields = { ...baseCorrectParams, email: 'admin' + server.internalServerNumber + '@example.com' }
+
+ await check(fields, HttpStatusCode.CONFLICT_409)
+ })
+
+ it('Should fail with a bad display name', async function () {
+ const fields = { ...baseCorrectParams, displayName: 'a'.repeat(150) }
+
+ await check(fields)
+ })
+
+ it('Should fail with a bad channel name', async function () {
+ const fields = { ...baseCorrectParams, channel: { name: '[]azf', displayName: 'toto' } }
+
+ await check(fields)
+ })
+
+ it('Should fail with a bad channel display name', async function () {
+ const fields = { ...baseCorrectParams, channel: { name: 'toto', displayName: '' } }
+
+ await check(fields)
+ })
+
+ it('Should fail with a channel name that is the same as username', async function () {
+ const source = { username: 'super_user', channel: { name: 'super_user', displayName: 'display name' } }
+ const fields = { ...baseCorrectParams, ...source }
+
+ await check(fields)
+ })
+
+ it('Should fail with an existing channel', async function () {
+ const attributes = { name: 'existing_channel', displayName: 'hello', description: 'super description' }
+ await server.channels.create({ attributes })
+
+ const fields = { ...baseCorrectParams, channel: { name: 'existing_channel', displayName: 'toto' } }
+
+ await check(fields, HttpStatusCode.CONFLICT_409)
+ })
+
+ it('Should fail on a server with registration disabled', async function () {
+ this.timeout(60000)
+
+ await server.config.updateCustomSubConfig({
+ newConfig: {
+ signup: {
+ enabled: false
+ }
+ }
+ })
+
+ await server.registrations.register({ username: 'user4', expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ await server.registrations.requestRegistration({
+ username: 'user4',
+ registrationReason: 'reason',
+ expectedStatus: HttpStatusCode.FORBIDDEN_403
+ })
+ })
+
+ it('Should fail if the user limit is reached', async function () {
+ this.timeout(60000)
+
+ const { total } = await server.users.list()
+
+ await server.config.updateCustomSubConfig({ newConfig: { signup: { limit: total } } })
+
+ await server.registrations.register({ username: 'user42', expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ await server.registrations.requestRegistration({
+ username: 'user42',
+ registrationReason: 'reason',
+ expectedStatus: HttpStatusCode.FORBIDDEN_403
+ })
+ })
+ })
+
+ describe('On direct registration', function () {
+
+ it('Should succeed with the correct params', async function () {
+ await server.config.enableSignup(false)
+
+ const fields = {
+ username: 'user_direct_1',
+ displayName: 'super user direct 1',
+ email: 'user_direct_1@example.com',
+ password: 'my super password',
+ channel: { name: 'super_user_direct_1_channel', displayName: 'super user direct 1 channel' }
+ }
+
+ await makePostBodyRequest({ url: server.url, path: registrationPath, fields, expectedStatus: HttpStatusCode.NO_CONTENT_204 })
+ })
+
+ it('Should fail if the instance requires approval', async function () {
+ this.timeout(60000)
+
+ await server.config.enableSignup(true)
+ await server.registrations.register({ username: 'user42', expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ })
+ })
+
+ describe('On registration request', function () {
+
+ before(async function () {
+ this.timeout(60000)
+
+ await server.config.enableSignup(true)
+ })
+
+ it('Should fail with an invalid registration reason', async function () {
+ for (const registrationReason of [ '', 't', 't'.repeat(5000) ]) {
+ await server.registrations.requestRegistration({
+ username: 'user_request_1',
+ registrationReason,
+ expectedStatus: HttpStatusCode.BAD_REQUEST_400
+ })
+ }
+ })
+
+ it('Should succeed with the correct params', async function () {
+ await server.registrations.requestRegistration({
+ username: 'user_request_2',
+ registrationReason: 'tt',
+ channel: {
+ displayName: 'my user request 2 channel',
+ name: 'user_request_2_channel'
+ }
+ })
+ })
+
+ it('Should fail if the user is already awaiting registration approval', async function () {
+ await server.registrations.requestRegistration({
+ username: 'user_request_2',
+ registrationReason: 'tt',
+ channel: {
+ displayName: 'my user request 42 channel',
+ name: 'user_request_42_channel'
+ },
+ expectedStatus: HttpStatusCode.CONFLICT_409
+ })
+ })
+
+ it('Should fail if the channel is already awaiting registration approval', async function () {
+ await server.registrations.requestRegistration({
+ username: 'user42',
+ registrationReason: 'tt',
+ channel: {
+ displayName: 'my user request 2 channel',
+ name: 'user_request_2_channel'
+ },
+ expectedStatus: HttpStatusCode.CONFLICT_409
+ })
+ })
+
+ it('Should fail if the instance does not require approval', async function () {
+ this.timeout(60000)
+
+ await server.config.enableSignup(false)
+
+ await server.registrations.requestRegistration({
+ username: 'user42',
+ registrationReason: 'toto',
+ expectedStatus: HttpStatusCode.BAD_REQUEST_400
+ })
+ })
+ })
+ })
+
+ describe('Registrations accept/reject', function () {
+ let id1: number
+ let id2: number
+
+ before(async function () {
+ this.timeout(60000)
+
+ await server.config.enableSignup(true);
+
+ ({ id: id1 } = await server.registrations.requestRegistration({ username: 'request_2', registrationReason: 'toto' }));
+ ({ id: id2 } = await server.registrations.requestRegistration({ username: 'request_3', registrationReason: 'toto' }))
+ })
+
+ it('Should fail to accept/reject registration without token', async function () {
+ const options = { id: id1, moderationResponse: 'tt', token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }
+ await server.registrations.accept(options)
+ await server.registrations.reject(options)
+ })
+
+ it('Should fail to accept/reject registration with a non moderator user', async function () {
+ const options = { id: id1, moderationResponse: 'tt', token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }
+ await server.registrations.accept(options)
+ await server.registrations.reject(options)
+ })
+
+ it('Should fail to accept/reject registration with a bad registration id', async function () {
+ {
+ const options = { id: 't' as any, moderationResponse: 'tt', token: moderatorToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
+ await server.registrations.accept(options)
+ await server.registrations.reject(options)
+ }
+
+ {
+ const options = { id: 42, moderationResponse: 'tt', token: moderatorToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
+ await server.registrations.accept(options)
+ await server.registrations.reject(options)
+ }
+ })
+
+ it('Should fail to accept/reject registration with a bad moderation resposne', async function () {
+ for (const moderationResponse of [ '', 't', 't'.repeat(5000) ]) {
+ const options = { id: id1, moderationResponse, token: moderatorToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
+ await server.registrations.accept(options)
+ await server.registrations.reject(options)
+ }
+ })
+
+ it('Should succeed to accept a registration', async function () {
+ await server.registrations.accept({ id: id1, moderationResponse: 'tt', token: moderatorToken })
+ })
+
+ it('Should succeed to reject a registration', async function () {
+ await server.registrations.reject({ id: id2, moderationResponse: 'tt', token: moderatorToken })
+ })
+
+ it('Should fail to accept/reject a registration that was already accepted/rejected', async function () {
+ for (const id of [ id1, id2 ]) {
+ const options = { id, moderationResponse: 'tt', token: moderatorToken, expectedStatus: HttpStatusCode.CONFLICT_409 }
+ await server.registrations.accept(options)
+ await server.registrations.reject(options)
+ }
+ })
+ })
+
+ describe('Registrations deletion', function () {
+ let id1: number
+ let id2: number
+ let id3: number
+
+ before(async function () {
+ ({ id: id1 } = await server.registrations.requestRegistration({ username: 'request_4', registrationReason: 'toto' }));
+ ({ id: id2 } = await server.registrations.requestRegistration({ username: 'request_5', registrationReason: 'toto' }));
+ ({ id: id3 } = await server.registrations.requestRegistration({ username: 'request_6', registrationReason: 'toto' }))
+
+ await server.registrations.accept({ id: id2, moderationResponse: 'tt' })
+ await server.registrations.reject({ id: id3, moderationResponse: 'tt' })
+ })
+
+ it('Should fail to delete registration without token', async function () {
+ await server.registrations.delete({ id: id1, token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
+ })
+
+ it('Should fail to delete registration with a non moderator user', async function () {
+ await server.registrations.delete({ id: id1, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ })
+
+ it('Should fail to delete registration with a bad registration id', async function () {
+ await server.registrations.delete({ id: 't' as any, token: moderatorToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
+ await server.registrations.delete({ id: 42, token: moderatorToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
+ })
+
+ it('Should succeed with the correct params', async function () {
+ await server.registrations.delete({ id: id1, token: moderatorToken })
+ await server.registrations.delete({ id: id2, token: moderatorToken })
+ await server.registrations.delete({ id: id3, token: moderatorToken })
+ })
+ })
+
+ describe('Listing registrations', function () {
+ const path = '/api/v1/users/registrations'
+
+ it('Should fail with a bad start pagination', async function () {
+ await checkBadStartPagination(server.url, path, server.accessToken)
+ })
+
+ it('Should fail with a bad count pagination', async function () {
+ await checkBadCountPagination(server.url, path, server.accessToken)
+ })
+
+ it('Should fail with an incorrect sort', async function () {
+ await checkBadSortPagination(server.url, path, server.accessToken)
+ })
+
+ it('Should fail with a non authenticated user', async function () {
+ await server.registrations.list({
+ token: null,
+ expectedStatus: HttpStatusCode.UNAUTHORIZED_401
+ })
+ })
+
+ it('Should fail with a non admin user', async function () {
+ await server.registrations.list({
+ token: userToken,
+ expectedStatus: HttpStatusCode.FORBIDDEN_403
+ })
+ })
+
+ it('Should succeed with the correct params', async function () {
+ await server.registrations.list({
+ token: moderatorToken,
+ search: 'toto'
+ })
+ })
+ })
+
+ after(async function () {
+ await cleanupTests([ server ])
+ })
+})
this.timeout(30000)
const user = { username: 'registered' + randomInt(1, 1500), password: 'password' }
- await server.users.register(user)
+ await server.registrations.register(user)
const userToken = await server.login.getAccessToken(user)
const attributes = { fixture: 'video_short2.webm' }
this.timeout(30000)
const user = { username: 'registered' + randomInt(1, 1500), password: 'password' }
- await server.users.register(user)
+ await server.registrations.register(user)
const userToken = await server.login.getAccessToken(user)
const attributes = { fixture: 'video_short2.webm' }
import { HttpStatusCode, UserAdminFlag, UserRole } from '@shared/models'
import {
cleanupTests,
+ ConfigCommand,
createSingleServer,
killallServers,
makeGetRequest,
await killallServers([ server ])
- const config = {
- smtp: {
- hostname: '127.0.0.1',
- port: emailPort
- }
- }
- await server.run(config)
+ await server.run(ConfigCommand.getEmailOverrideConfig(emailPort))
const fields = {
...baseCorrectParams,
--- /dev/null
+/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
+import { MockSmtpServer } from '@server/tests/shared'
+import { HttpStatusCode, UserRole } from '@shared/models'
+import { cleanupTests, createSingleServer, makePostBodyRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
+
+describe('Test users API validators', function () {
+ let server: PeerTubeServer
+
+ // ---------------------------------------------------------------
+
+ before(async function () {
+ this.timeout(30000)
+
+ server = await createSingleServer(1, {
+ rates_limit: {
+ ask_send_email: {
+ max: 10
+ }
+ }
+ })
+
+ await setAccessTokensToServers([ server ])
+ await server.config.enableSignup(true)
+
+ await server.users.generate('moderator2', UserRole.MODERATOR)
+
+ await server.registrations.requestRegistration({
+ username: 'request1',
+ registrationReason: 'tt'
+ })
+ })
+
+ describe('When asking a password reset', function () {
+ const path = '/api/v1/users/ask-reset-password'
+
+ it('Should fail with a missing email', async function () {
+ const fields = {}
+
+ await makePostBodyRequest({ url: server.url, path, fields })
+ })
+
+ it('Should fail with an invalid email', async function () {
+ const fields = { email: 'hello' }
+
+ await makePostBodyRequest({ url: server.url, path, fields })
+ })
+
+ it('Should success with the correct params', async function () {
+ const fields = { email: 'admin@example.com' }
+
+ await makePostBodyRequest({
+ url: server.url,
+ path,
+ fields,
+ expectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ })
+ })
+
+ describe('When asking for an account verification email', function () {
+ const path = '/api/v1/users/ask-send-verify-email'
+
+ it('Should fail with a missing email', async function () {
+ const fields = {}
+
+ await makePostBodyRequest({ url: server.url, path, fields })
+ })
+
+ it('Should fail with an invalid email', async function () {
+ const fields = { email: 'hello' }
+
+ await makePostBodyRequest({ url: server.url, path, fields })
+ })
+
+ it('Should succeed with the correct params', async function () {
+ const fields = { email: 'admin@example.com' }
+
+ await makePostBodyRequest({
+ url: server.url,
+ path,
+ fields,
+ expectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ })
+ })
+
+ describe('When asking for a registration verification email', function () {
+ const path = '/api/v1/users/registrations/ask-send-verify-email'
+
+ it('Should fail with a missing email', async function () {
+ const fields = {}
+
+ await makePostBodyRequest({ url: server.url, path, fields })
+ })
+
+ it('Should fail with an invalid email', async function () {
+ const fields = { email: 'hello' }
+
+ await makePostBodyRequest({ url: server.url, path, fields })
+ })
+
+ it('Should succeed with the correct params', async function () {
+ const fields = { email: 'request1@example.com' }
+
+ await makePostBodyRequest({
+ url: server.url,
+ path,
+ fields,
+ expectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ })
+ })
+
+ after(async function () {
+ MockSmtpServer.Instance.kill()
+
+ await cleanupTests([ server ])
+ })
+})
+++ /dev/null
-/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
-import { MockSmtpServer } from '@server/tests/shared'
-import { omit } from '@shared/core-utils'
-import { HttpStatusCode, UserRole } from '@shared/models'
-import { cleanupTests, createSingleServer, makePostBodyRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
-
-describe('Test users API validators', function () {
- const path = '/api/v1/users/'
- let server: PeerTubeServer
- let serverWithRegistrationDisabled: PeerTubeServer
-
- // ---------------------------------------------------------------
-
- before(async function () {
- this.timeout(30000)
-
- const res = await Promise.all([
- createSingleServer(1, { signup: { limit: 3 } }),
- createSingleServer(2)
- ])
-
- server = res[0]
- serverWithRegistrationDisabled = res[1]
-
- await setAccessTokensToServers([ server ])
-
- await server.users.generate('moderator2', UserRole.MODERATOR)
- })
-
- describe('When registering a new user', function () {
- const registrationPath = path + '/register'
- const baseCorrectParams = {
- username: 'user3',
- displayName: 'super user',
- email: 'test3@example.com',
- password: 'my super password'
- }
-
- it('Should fail with a too small username', async function () {
- const fields = { ...baseCorrectParams, username: '' }
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail with a too long username', async function () {
- const fields = { ...baseCorrectParams, username: 'super'.repeat(50) }
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail with an incorrect username', async function () {
- const fields = { ...baseCorrectParams, username: 'my username' }
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail with a missing email', async function () {
- const fields = omit(baseCorrectParams, [ 'email' ])
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail with an invalid email', async function () {
- const fields = { ...baseCorrectParams, email: 'test_example.com' }
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail with a too small password', async function () {
- const fields = { ...baseCorrectParams, password: 'bla' }
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail with a too long password', async function () {
- const fields = { ...baseCorrectParams, password: 'super'.repeat(61) }
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail if we register a user with the same username', async function () {
- const fields = { ...baseCorrectParams, username: 'root' }
-
- await makePostBodyRequest({
- url: server.url,
- path: registrationPath,
- token: server.accessToken,
- fields,
- expectedStatus: HttpStatusCode.CONFLICT_409
- })
- })
-
- it('Should fail with a "peertube" username', async function () {
- const fields = { ...baseCorrectParams, username: 'peertube' }
-
- await makePostBodyRequest({
- url: server.url,
- path: registrationPath,
- token: server.accessToken,
- fields,
- expectedStatus: HttpStatusCode.CONFLICT_409
- })
- })
-
- it('Should fail if we register a user with the same email', async function () {
- const fields = { ...baseCorrectParams, email: 'admin' + server.internalServerNumber + '@example.com' }
-
- await makePostBodyRequest({
- url: server.url,
- path: registrationPath,
- token: server.accessToken,
- fields,
- expectedStatus: HttpStatusCode.CONFLICT_409
- })
- })
-
- it('Should fail with a bad display name', async function () {
- const fields = { ...baseCorrectParams, displayName: 'a'.repeat(150) }
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail with a bad channel name', async function () {
- const fields = { ...baseCorrectParams, channel: { name: '[]azf', displayName: 'toto' } }
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail with a bad channel display name', async function () {
- const fields = { ...baseCorrectParams, channel: { name: 'toto', displayName: '' } }
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail with a channel name that is the same as username', async function () {
- const source = { username: 'super_user', channel: { name: 'super_user', displayName: 'display name' } }
- const fields = { ...baseCorrectParams, ...source }
-
- await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
- })
-
- it('Should fail with an existing channel', async function () {
- const attributes = { name: 'existing_channel', displayName: 'hello', description: 'super description' }
- await server.channels.create({ attributes })
-
- const fields = { ...baseCorrectParams, channel: { name: 'existing_channel', displayName: 'toto' } }
-
- await makePostBodyRequest({
- url: server.url,
- path: registrationPath,
- token: server.accessToken,
- fields,
- expectedStatus: HttpStatusCode.CONFLICT_409
- })
- })
-
- it('Should succeed with the correct params', async function () {
- const fields = { ...baseCorrectParams, channel: { name: 'super_channel', displayName: 'toto' } }
-
- await makePostBodyRequest({
- url: server.url,
- path: registrationPath,
- token: server.accessToken,
- fields,
- expectedStatus: HttpStatusCode.NO_CONTENT_204
- })
- })
-
- it('Should fail on a server with registration disabled', async function () {
- const fields = {
- username: 'user4',
- email: 'test4@example.com',
- password: 'my super password 4'
- }
-
- await makePostBodyRequest({
- url: serverWithRegistrationDisabled.url,
- path: registrationPath,
- token: serverWithRegistrationDisabled.accessToken,
- fields,
- expectedStatus: HttpStatusCode.FORBIDDEN_403
- })
- })
- })
-
- describe('When registering multiple users on a server with users limit', function () {
-
- it('Should fail when after 3 registrations', async function () {
- await server.users.register({ username: 'user42', expectedStatus: HttpStatusCode.FORBIDDEN_403 })
- })
-
- })
-
- describe('When asking a password reset', function () {
- const path = '/api/v1/users/ask-reset-password'
-
- it('Should fail with a missing email', async function () {
- const fields = {}
-
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
- })
-
- it('Should fail with an invalid email', async function () {
- const fields = { email: 'hello' }
-
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
- })
-
- it('Should success with the correct params', async function () {
- const fields = { email: 'admin@example.com' }
-
- await makePostBodyRequest({
- url: server.url,
- path,
- token: server.accessToken,
- fields,
- expectedStatus: HttpStatusCode.NO_CONTENT_204
- })
- })
- })
-
- describe('When asking for an account verification email', function () {
- const path = '/api/v1/users/ask-send-verify-email'
-
- it('Should fail with a missing email', async function () {
- const fields = {}
-
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
- })
-
- it('Should fail with an invalid email', async function () {
- const fields = { email: 'hello' }
-
- await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
- })
-
- it('Should succeed with the correct params', async function () {
- const fields = { email: 'admin@example.com' }
-
- await makePostBodyRequest({
- url: server.url,
- path,
- token: server.accessToken,
- fields,
- expectedStatus: HttpStatusCode.NO_CONTENT_204
- })
- })
- })
-
- after(async function () {
- MockSmtpServer.Instance.kill()
-
- await cleanupTests([ server, serverWithRegistrationDisabled ])
- })
-})
import './comments-notifications'
import './moderation-notifications'
import './notifications-api'
+import './registrations-notifications'
import './user-notifications'
checkNewInstanceFollower,
checkNewVideoAbuseForModerators,
checkNewVideoFromSubscription,
- checkUserRegistered,
checkVideoAutoBlacklistForModerators,
checkVideoIsPublished,
MockInstancesIndex,
})
})
- describe('New registration', function () {
- let baseParams: CheckerBaseParams
-
- before(() => {
- baseParams = {
- server: servers[0],
- emails,
- socketNotifications: adminNotifications,
- token: servers[0].accessToken
- }
- })
-
- it('Should send a notification only to moderators when a user registers on the instance', async function () {
- this.timeout(10000)
-
- await servers[0].users.register({ username: 'user_45' })
-
- await waitJobs(servers)
-
- await checkUserRegistered({ ...baseParams, username: 'user_45', checkType: 'presence' })
-
- const userOverride = { socketNotifications: userNotifications, token: userToken1, check: { web: true, mail: false } }
- await checkUserRegistered({ ...baseParams, ...userOverride, username: 'user_45', checkType: 'absence' })
- })
- })
-
describe('New instance follows', function () {
const instanceIndexServer = new MockInstancesIndex()
let config: any
--- /dev/null
+/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
+
+import {
+ CheckerBaseParams,
+ checkRegistrationRequest,
+ checkUserRegistered,
+ MockSmtpServer,
+ prepareNotificationsTest
+} from '@server/tests/shared'
+import { UserNotification } from '@shared/models'
+import { cleanupTests, PeerTubeServer, waitJobs } from '@shared/server-commands'
+
+describe('Test registrations notifications', function () {
+ let server: PeerTubeServer
+ let userToken1: string
+
+ let userNotifications: UserNotification[] = []
+ let adminNotifications: UserNotification[] = []
+ let emails: object[] = []
+
+ let baseParams: CheckerBaseParams
+
+ before(async function () {
+ this.timeout(50000)
+
+ const res = await prepareNotificationsTest(1)
+
+ server = res.servers[0]
+ emails = res.emails
+ userToken1 = res.userAccessToken
+ adminNotifications = res.adminNotifications
+ userNotifications = res.userNotifications
+
+ baseParams = {
+ server,
+ emails,
+ socketNotifications: adminNotifications,
+ token: server.accessToken
+ }
+ })
+
+ describe('New direct registration for moderators', function () {
+
+ before(async function () {
+ await server.config.enableSignup(false)
+ })
+
+ it('Should send a notification only to moderators when a user registers on the instance', async function () {
+ this.timeout(50000)
+
+ await server.registrations.register({ username: 'user_10' })
+
+ await waitJobs([ server ])
+
+ await checkUserRegistered({ ...baseParams, username: 'user_10', checkType: 'presence' })
+
+ const userOverride = { socketNotifications: userNotifications, token: userToken1, check: { web: true, mail: false } }
+ await checkUserRegistered({ ...baseParams, ...userOverride, username: 'user_10', checkType: 'absence' })
+ })
+ })
+
+ describe('New registration request for moderators', function () {
+
+ before(async function () {
+ await server.config.enableSignup(true)
+ })
+
+ it('Should send a notification on new registration request', async function () {
+ this.timeout(50000)
+
+ const registrationReason = 'my reason'
+ await server.registrations.requestRegistration({ username: 'user_11', registrationReason })
+
+ await waitJobs([ server ])
+
+ await checkRegistrationRequest({ ...baseParams, username: 'user_11', registrationReason, checkType: 'presence' })
+
+ const userOverride = { socketNotifications: userNotifications, token: userToken1, check: { web: true, mail: false } }
+ await checkRegistrationRequest({ ...baseParams, ...userOverride, username: 'user_11', registrationReason, checkType: 'absence' })
+ })
+ })
+
+ after(async function () {
+ MockSmtpServer.Instance.kill()
+
+ await cleanupTests([ server ])
+ })
+})
})
it('Should register a user with this default setting', async function () {
- await server.users.register({ username: 'user_p2p_2' })
+ await server.registrations.register({ username: 'user_p2p_2' })
const userToken = await server.login.getAccessToken('user_p2p_2')
})
it('Should register a user with this default setting', async function () {
- await server.users.register({ username: 'user_p2p_4' })
+ await server.registrations.register({ username: 'user_p2p_4' })
const userToken = await server.login.getAccessToken('user_p2p_4')
expect(data.signup.enabled).to.be.true
expect(data.signup.limit).to.equal(4)
expect(data.signup.minimumAge).to.equal(16)
+ expect(data.signup.requiresApproval).to.be.false
expect(data.signup.requiresEmailVerification).to.be.false
expect(data.admin.email).to.equal('admin' + server.internalServerNumber + '@example.com')
expect(data.signup.enabled).to.be.false
expect(data.signup.limit).to.equal(5)
+ expect(data.signup.requiresApproval).to.be.false
expect(data.signup.requiresEmailVerification).to.be.false
expect(data.signup.minimumAge).to.equal(10)
signup: {
enabled: false,
limit: 5,
+ requiresApproval: false,
requiresEmailVerification: false,
minimumAge: 10
},
this.timeout(5000)
await Promise.all([
- server.users.register({ username: 'user1' }),
- server.users.register({ username: 'user2' }),
- server.users.register({ username: 'user3' })
+ server.registrations.register({ username: 'user1' }),
+ server.registrations.register({ username: 'user2' }),
+ server.registrations.register({ username: 'user3' })
])
const data = await server.config.getConfig()
import { HttpStatusCode } from '@shared/models'
import {
cleanupTests,
+ ConfigCommand,
ContactFormCommand,
createSingleServer,
PeerTubeServer,
const port = await MockSmtpServer.Instance.collectEmails(emails)
- const overrideConfig = {
- smtp: {
- hostname: '127.0.0.1',
- port
- }
- }
- server = await createSingleServer(1, overrideConfig)
+ server = await createSingleServer(1, ConfigCommand.getEmailOverrideConfig(port))
await setAccessTokensToServers([ server ])
command = server.contactForm
import { expect } from 'chai'
import { MockSmtpServer } from '@server/tests/shared'
import { HttpStatusCode } from '@shared/models'
-import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/server-commands'
+import {
+ cleanupTests,
+ ConfigCommand,
+ createSingleServer,
+ PeerTubeServer,
+ setAccessTokensToServers,
+ waitJobs
+} from '@shared/server-commands'
describe('Test emails', function () {
let server: PeerTubeServer
username: 'user_1',
password: 'super_password'
}
- let emailPort: number
before(async function () {
this.timeout(50000)
- emailPort = await MockSmtpServer.Instance.collectEmails(emails)
+ const emailPort = await MockSmtpServer.Instance.collectEmails(emails)
+ server = await createSingleServer(1, ConfigCommand.getEmailOverrideConfig(emailPort))
- const overrideConfig = {
- smtp: {
- hostname: '127.0.0.1',
- port: emailPort
- }
- }
- server = await createSingleServer(1, overrideConfig)
await setAccessTokensToServers([ server ])
+ await server.config.enableSignup(true)
{
const created = await server.users.create({ username: user.username, password: user.password })
})
})
+ describe('When verifying a registration email', function () {
+ let registrationId: number
+ let registrationIdEmail: number
+
+ before(async function () {
+ const { id } = await server.registrations.requestRegistration({
+ username: 'request_1',
+ email: 'request_1@example.com',
+ registrationReason: 'tt'
+ })
+ registrationId = id
+ })
+
+ it('Should ask to send the verification email', async function () {
+ this.timeout(10000)
+
+ await server.registrations.askSendVerifyEmail({ email: 'request_1@example.com' })
+
+ await waitJobs(server)
+ expect(emails).to.have.lengthOf(9)
+
+ const email = emails[8]
+
+ expect(email['from'][0]['name']).equal('PeerTube')
+ expect(email['from'][0]['address']).equal('test-admin@127.0.0.1')
+ expect(email['to'][0]['address']).equal('request_1@example.com')
+ expect(email['subject']).contains('Verify')
+
+ const verificationStringMatches = /verificationString=([a-z0-9]+)/.exec(email['text'])
+ expect(verificationStringMatches).not.to.be.null
+
+ verificationString = verificationStringMatches[1]
+ expect(verificationString).to.not.be.undefined
+ expect(verificationString).to.have.length.above(2)
+
+ const registrationIdMatches = /registrationId=([0-9]+)/.exec(email['text'])
+ expect(registrationIdMatches).not.to.be.null
+
+ registrationIdEmail = parseInt(registrationIdMatches[1], 10)
+
+ expect(registrationId).to.equal(registrationIdEmail)
+ })
+
+ it('Should not verify the email with an invalid verification string', async function () {
+ await server.registrations.verifyEmail({
+ registrationId: registrationIdEmail,
+ verificationString: verificationString + 'b',
+ expectedStatus: HttpStatusCode.FORBIDDEN_403
+ })
+ })
+
+ it('Should verify the email', async function () {
+ await server.registrations.verifyEmail({ registrationId: registrationIdEmail, verificationString })
+ })
+ })
+
after(async function () {
MockSmtpServer.Instance.kill()
it('Should rate limit signup', async function () {
for (let i = 0; i < 10; i++) {
try {
- await server.users.register({ username: 'test' + i })
+ await server.registrations.register({ username: 'test' + i })
} catch {
// empty
}
}
- await server.users.register({ username: 'test42', expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
+ await server.registrations.register({ username: 'test42', expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
})
it('Should not rate limit failed signup', async function () {
await wait(7000)
for (let i = 0; i < 3; i++) {
- await server.users.register({ username: 'test' + i, expectedStatus: HttpStatusCode.CONFLICT_409 })
+ await server.registrations.register({ username: 'test' + i, expectedStatus: HttpStatusCode.CONFLICT_409 })
}
- await server.users.register({ username: 'test43', expectedStatus: HttpStatusCode.NO_CONTENT_204 })
+ await server.registrations.register({ username: 'test43', expectedStatus: HttpStatusCode.NO_CONTENT_204 })
})
import './oauth'
+import './registrations`'
import './two-factor'
import './user-subscriptions'
import './user-videos'
import './users'
import './users-multiple-servers'
-import './users-verification'
+import './users-email-verification'
--- /dev/null
+/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
+
+import { expect } from 'chai'
+import { MockSmtpServer } from '@server/tests/shared'
+import { UserRegistrationState, UserRole } from '@shared/models'
+import {
+ cleanupTests,
+ ConfigCommand,
+ createSingleServer,
+ PeerTubeServer,
+ setAccessTokensToServers,
+ waitJobs
+} from '@shared/server-commands'
+
+describe('Test registrations', function () {
+ let server: PeerTubeServer
+
+ const emails: object[] = []
+ let emailPort: number
+
+ before(async function () {
+ this.timeout(30000)
+
+ emailPort = await MockSmtpServer.Instance.collectEmails(emails)
+
+ server = await createSingleServer(1, ConfigCommand.getEmailOverrideConfig(emailPort))
+
+ await setAccessTokensToServers([ server ])
+ await server.config.enableSignup(false)
+ })
+
+ describe('Direct registrations of a new user', function () {
+ let user1Token: string
+
+ it('Should register a new user', async function () {
+ const user = { displayName: 'super user 1', username: 'user_1', password: 'my super password' }
+ const channel = { name: 'my_user_1_channel', displayName: 'my channel rocks' }
+
+ await server.registrations.register({ ...user, channel })
+ })
+
+ it('Should be able to login with this registered user', async function () {
+ const user1 = { username: 'user_1', password: 'my super password' }
+
+ user1Token = await server.login.getAccessToken(user1)
+ })
+
+ it('Should have the correct display name', async function () {
+ const user = await server.users.getMyInfo({ token: user1Token })
+ expect(user.account.displayName).to.equal('super user 1')
+ })
+
+ it('Should have the correct video quota', async function () {
+ const user = await server.users.getMyInfo({ token: user1Token })
+ expect(user.videoQuota).to.equal(5 * 1024 * 1024)
+ })
+
+ it('Should have created the channel', async function () {
+ const { displayName } = await server.channels.get({ channelName: 'my_user_1_channel' })
+
+ expect(displayName).to.equal('my channel rocks')
+ })
+
+ it('Should remove me', async function () {
+ {
+ const { data } = await server.users.list()
+ expect(data.find(u => u.username === 'user_1')).to.not.be.undefined
+ }
+
+ await server.users.deleteMe({ token: user1Token })
+
+ {
+ const { data } = await server.users.list()
+ expect(data.find(u => u.username === 'user_1')).to.be.undefined
+ }
+ })
+ })
+
+ describe('Registration requests', function () {
+ let id2: number
+ let id3: number
+ let id4: number
+
+ let user2Token: string
+ let user3Token: string
+
+ before(async function () {
+ this.timeout(60000)
+
+ await server.config.enableSignup(true)
+
+ {
+ const { id } = await server.registrations.requestRegistration({
+ username: 'user4',
+ registrationReason: 'registration reason 4'
+ })
+
+ id4 = id
+ }
+ })
+
+ it('Should request a registration without a channel', async function () {
+ {
+ const { id } = await server.registrations.requestRegistration({
+ username: 'user2',
+ displayName: 'my super user 2',
+ email: 'user2@example.com',
+ password: 'user2password',
+ registrationReason: 'registration reason 2'
+ })
+
+ id2 = id
+ }
+ })
+
+ it('Should request a registration with a channel', async function () {
+ const { id } = await server.registrations.requestRegistration({
+ username: 'user3',
+ displayName: 'my super user 3',
+ channel: {
+ displayName: 'my user 3 channel',
+ name: 'super_user3_channel'
+ },
+ email: 'user3@example.com',
+ password: 'user3password',
+ registrationReason: 'registration reason 3'
+ })
+
+ id3 = id
+ })
+
+ it('Should list these registration requests', async function () {
+ {
+ const { total, data } = await server.registrations.list({ sort: '-createdAt' })
+ expect(total).to.equal(3)
+ expect(data).to.have.lengthOf(3)
+
+ {
+ expect(data[0].id).to.equal(id3)
+ expect(data[0].username).to.equal('user3')
+ expect(data[0].accountDisplayName).to.equal('my super user 3')
+
+ expect(data[0].channelDisplayName).to.equal('my user 3 channel')
+ expect(data[0].channelHandle).to.equal('super_user3_channel')
+
+ expect(data[0].createdAt).to.exist
+ expect(data[0].updatedAt).to.exist
+
+ expect(data[0].email).to.equal('user3@example.com')
+ expect(data[0].emailVerified).to.be.null
+
+ expect(data[0].moderationResponse).to.be.null
+ expect(data[0].registrationReason).to.equal('registration reason 3')
+ expect(data[0].state.id).to.equal(UserRegistrationState.PENDING)
+ expect(data[0].state.label).to.equal('Pending')
+ expect(data[0].user).to.be.null
+ }
+
+ {
+ expect(data[1].id).to.equal(id2)
+ expect(data[1].username).to.equal('user2')
+ expect(data[1].accountDisplayName).to.equal('my super user 2')
+
+ expect(data[1].channelDisplayName).to.be.null
+ expect(data[1].channelHandle).to.be.null
+
+ expect(data[1].createdAt).to.exist
+ expect(data[1].updatedAt).to.exist
+
+ expect(data[1].email).to.equal('user2@example.com')
+ expect(data[1].emailVerified).to.be.null
+
+ expect(data[1].moderationResponse).to.be.null
+ expect(data[1].registrationReason).to.equal('registration reason 2')
+ expect(data[1].state.id).to.equal(UserRegistrationState.PENDING)
+ expect(data[1].state.label).to.equal('Pending')
+ expect(data[1].user).to.be.null
+ }
+
+ {
+ expect(data[2].username).to.equal('user4')
+ }
+ }
+
+ {
+ const { total, data } = await server.registrations.list({ count: 1, start: 1, sort: 'createdAt' })
+
+ expect(total).to.equal(3)
+ expect(data).to.have.lengthOf(1)
+ expect(data[0].id).to.equal(id2)
+ }
+
+ {
+ const { total, data } = await server.registrations.list({ search: 'user3' })
+ expect(total).to.equal(1)
+ expect(data).to.have.lengthOf(1)
+ expect(data[0].id).to.equal(id3)
+ }
+ })
+
+ it('Should reject a registration request', async function () {
+ await server.registrations.reject({ id: id4, moderationResponse: 'I do not want id 4 on this instance' })
+ })
+
+ it('Should have sent an email to the user explanining the registration has been rejected', async function () {
+ this.timeout(50000)
+
+ await waitJobs([ server ])
+
+ const email = emails.find(e => e['to'][0]['address'] === 'user4@example.com')
+ expect(email).to.exist
+
+ expect(email['subject']).to.contain('been rejected')
+ expect(email['text']).to.contain('been rejected')
+ expect(email['text']).to.contain('I do not want id 4 on this instance')
+ })
+
+ it('Should accept registration requests', async function () {
+ await server.registrations.accept({ id: id2, moderationResponse: 'Welcome id 2' })
+ await server.registrations.accept({ id: id3, moderationResponse: 'Welcome id 3' })
+ })
+
+ it('Should have sent an email to the user explanining the registration has been accepted', async function () {
+ this.timeout(50000)
+
+ await waitJobs([ server ])
+
+ {
+ const email = emails.find(e => e['to'][0]['address'] === 'user2@example.com')
+ expect(email).to.exist
+
+ expect(email['subject']).to.contain('been accepted')
+ expect(email['text']).to.contain('been accepted')
+ expect(email['text']).to.contain('Welcome id 2')
+ }
+
+ {
+ const email = emails.find(e => e['to'][0]['address'] === 'user3@example.com')
+ expect(email).to.exist
+
+ expect(email['subject']).to.contain('been accepted')
+ expect(email['text']).to.contain('been accepted')
+ expect(email['text']).to.contain('Welcome id 3')
+ }
+ })
+
+ it('Should login with these users', async function () {
+ user2Token = await server.login.getAccessToken({ username: 'user2', password: 'user2password' })
+ user3Token = await server.login.getAccessToken({ username: 'user3', password: 'user3password' })
+ })
+
+ it('Should have created the appropriate attributes for user 2', async function () {
+ const me = await server.users.getMyInfo({ token: user2Token })
+
+ expect(me.username).to.equal('user2')
+ expect(me.account.displayName).to.equal('my super user 2')
+ expect(me.videoQuota).to.equal(5 * 1024 * 1024)
+ expect(me.videoChannels[0].name).to.equal('user2_channel')
+ expect(me.videoChannels[0].displayName).to.equal('Main user2 channel')
+ expect(me.role.id).to.equal(UserRole.USER)
+ expect(me.email).to.equal('user2@example.com')
+ })
+
+ it('Should have created the appropriate attributes for user 3', async function () {
+ const me = await server.users.getMyInfo({ token: user3Token })
+
+ expect(me.username).to.equal('user3')
+ expect(me.account.displayName).to.equal('my super user 3')
+ expect(me.videoQuota).to.equal(5 * 1024 * 1024)
+ expect(me.videoChannels[0].name).to.equal('super_user3_channel')
+ expect(me.videoChannels[0].displayName).to.equal('my user 3 channel')
+ expect(me.role.id).to.equal(UserRole.USER)
+ expect(me.email).to.equal('user3@example.com')
+ })
+
+ it('Should list these accepted/rejected registration requests', async function () {
+ const { data } = await server.registrations.list({ sort: 'createdAt' })
+ const { data: users } = await server.users.list()
+
+ {
+ expect(data[0].id).to.equal(id4)
+ expect(data[0].state.id).to.equal(UserRegistrationState.REJECTED)
+ expect(data[0].state.label).to.equal('Rejected')
+
+ expect(data[0].moderationResponse).to.equal('I do not want id 4 on this instance')
+ expect(data[0].user).to.be.null
+
+ expect(users.find(u => u.username === 'user4')).to.not.exist
+ }
+
+ {
+ expect(data[1].id).to.equal(id2)
+ expect(data[1].state.id).to.equal(UserRegistrationState.ACCEPTED)
+ expect(data[1].state.label).to.equal('Accepted')
+
+ expect(data[1].moderationResponse).to.equal('Welcome id 2')
+ expect(data[1].user).to.exist
+
+ const user2 = users.find(u => u.username === 'user2')
+ expect(data[1].user.id).to.equal(user2.id)
+ }
+
+ {
+ expect(data[2].id).to.equal(id3)
+ expect(data[2].state.id).to.equal(UserRegistrationState.ACCEPTED)
+ expect(data[2].state.label).to.equal('Accepted')
+
+ expect(data[2].moderationResponse).to.equal('Welcome id 3')
+ expect(data[2].user).to.exist
+
+ const user3 = users.find(u => u.username === 'user3')
+ expect(data[2].user.id).to.equal(user3.id)
+ }
+ })
+
+ it('Shoulde delete a registration', async function () {
+ await server.registrations.delete({ id: id2 })
+ await server.registrations.delete({ id: id3 })
+
+ const { total, data } = await server.registrations.list()
+ expect(total).to.equal(1)
+ expect(data).to.have.lengthOf(1)
+ expect(data[0].id).to.equal(id4)
+
+ const { data: users } = await server.users.list()
+
+ for (const username of [ 'user2', 'user3' ]) {
+ expect(users.find(u => u.username === username)).to.exist
+ }
+ })
+
+ it('Should request a registration without a channel, that will conflict with an already existing channel', async function () {
+ let id1: number
+ let id2: number
+
+ {
+ const { id } = await server.registrations.requestRegistration({
+ registrationReason: 'tt',
+ username: 'user5',
+ password: 'user5password',
+ channel: {
+ displayName: 'channel 6',
+ name: 'user6_channel'
+ }
+ })
+
+ id1 = id
+ }
+
+ {
+ const { id } = await server.registrations.requestRegistration({
+ registrationReason: 'tt',
+ username: 'user6',
+ password: 'user6password'
+ })
+
+ id2 = id
+ }
+
+ await server.registrations.accept({ id: id1, moderationResponse: 'tt' })
+ await server.registrations.accept({ id: id2, moderationResponse: 'tt' })
+
+ const user5Token = await server.login.getAccessToken('user5', 'user5password')
+ const user6Token = await server.login.getAccessToken('user6', 'user6password')
+
+ const user5 = await server.users.getMyInfo({ token: user5Token })
+ const user6 = await server.users.getMyInfo({ token: user6Token })
+
+ expect(user5.videoChannels[0].name).to.equal('user6_channel')
+ expect(user6.videoChannels[0].name).to.equal('user6_channel-1')
+ })
+ })
+
+ after(async function () {
+ MockSmtpServer.Instance.kill()
+
+ await cleanupTests([ server ])
+ })
+})
import { expect } from 'chai'
import { MockSmtpServer } from '@server/tests/shared'
import { HttpStatusCode } from '@shared/models'
-import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/server-commands'
-
-describe('Test users account verification', function () {
+import {
+ cleanupTests,
+ ConfigCommand,
+ createSingleServer,
+ PeerTubeServer,
+ setAccessTokensToServers,
+ waitJobs
+} from '@shared/server-commands'
+
+describe('Test users email verification', function () {
let server: PeerTubeServer
let userId: number
let userAccessToken: string
this.timeout(30000)
const port = await MockSmtpServer.Instance.collectEmails(emails)
-
- const overrideConfig = {
- smtp: {
- hostname: '127.0.0.1',
- port
- }
- }
- server = await createSingleServer(1, overrideConfig)
+ server = await createSingleServer(1, ConfigCommand.getEmailOverrideConfig(port))
await setAccessTokensToServers([ server ])
})
it('Should register user and send verification email if verification required', async function () {
this.timeout(30000)
- await server.config.updateCustomSubConfig({
+ await server.config.updateExistingSubConfig({
newConfig: {
signup: {
enabled: true,
+ requiresApproval: false,
requiresEmailVerification: true,
limit: 10
}
}
})
- await server.users.register(user1)
+ await server.registrations.register(user1)
await waitJobs(server)
expectedEmailsLength++
it('Should register user not requiring email verification if setting not enabled', async function () {
this.timeout(5000)
- await server.config.updateCustomSubConfig({
+ await server.config.updateExistingSubConfig({
newConfig: {
signup: {
- enabled: true,
- requiresEmailVerification: false,
- limit: 10
+ requiresEmailVerification: false
}
}
})
- await server.users.register(user2)
+ await server.registrations.register(user2)
await waitJobs(server)
expect(emails).to.have.lengthOf(expectedEmailsLength)
await server.config.updateCustomSubConfig({
newConfig: {
signup: {
- enabled: true,
- requiresEmailVerification: true,
- limit: 10
+ requiresEmailVerification: true
}
}
})
})
})
- describe('Registering a new user', function () {
- let user15AccessToken: string
-
- it('Should register a new user', async function () {
- const user = { displayName: 'super user 15', username: 'user_15', password: 'my super password' }
- const channel = { name: 'my_user_15_channel', displayName: 'my channel rocks' }
-
- await server.users.register({ ...user, channel })
- })
-
- it('Should be able to login with this registered user', async function () {
- const user15 = {
- username: 'user_15',
- password: 'my super password'
- }
-
- user15AccessToken = await server.login.getAccessToken(user15)
- })
-
- it('Should have the correct display name', async function () {
- const user = await server.users.getMyInfo({ token: user15AccessToken })
- expect(user.account.displayName).to.equal('super user 15')
- })
-
- it('Should have the correct video quota', async function () {
- const user = await server.users.getMyInfo({ token: user15AccessToken })
- expect(user.videoQuota).to.equal(5 * 1024 * 1024)
- })
-
- it('Should have created the channel', async function () {
- const { displayName } = await server.channels.get({ channelName: 'my_user_15_channel' })
-
- expect(displayName).to.equal('my channel rocks')
- })
-
- it('Should remove me', async function () {
- {
- const { data } = await server.users.list()
- expect(data.find(u => u.username === 'user_15')).to.not.be.undefined
- }
-
- await server.users.deleteMe({ token: user15AccessToken })
-
- {
- const { data } = await server.users.list()
- expect(data.find(u => u.username === 'user_15')).to.be.undefined
- }
- })
- })
-
describe('User blocking', function () {
let user16Id: number
let user16AccessToken: string
})
it('Should allow signup', async function () {
- await servers[0].users.register({
+ await servers[0].registrations.register({
username: 'user1',
displayName: 'user 1'
})
})
it('Should detect a signup as SPAM', async function () {
- await servers[0].users.register({
+ await servers[0].registrations.register({
username: 'user2',
displayName: 'user 2',
email: 'akismet-guaranteed-spam@example.com',
}
})
- registerHook({
- target: 'filter:api.user.signup.allowed.result',
- handler: (result, params) => {
- if (params && params.body && params.body.email && params.body.email.includes('jma')) {
- return { allowed: false, errorMessage: 'No jma' }
+ {
+ registerHook({
+ target: 'filter:api.user.signup.allowed.result',
+ handler: (result, params) => {
+ if (params && params.body && params.body.email && params.body.email.includes('jma 1')) {
+ return { allowed: false, errorMessage: 'No jma 1' }
+ }
+
+ return result
}
+ })
- return result
- }
- })
+ registerHook({
+ target: 'filter:api.user.request-signup.allowed.result',
+ handler: (result, params) => {
+ if (params && params.body && params.body.email && params.body.email.includes('jma 2')) {
+ return { allowed: false, errorMessage: 'No jma 2' }
+ }
+
+ return result
+ }
+ })
+ }
registerHook({
target: 'filter:api.download.torrent.allowed.result',
let userId: number
it('Should run action:api.user.registered', async function () {
- await servers[0].users.register({ username: 'registered_user' })
+ await servers[0].registrations.register({ username: 'registered_user' })
await checkHook('action:api.user.registered')
})
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import { expect } from 'chai'
-import { HttpStatusCode, VideoDetails, VideoImportState, VideoPlaylist, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
+import {
+ HttpStatusCode,
+ PeerTubeProblemDocument,
+ VideoDetails,
+ VideoImportState,
+ VideoPlaylist,
+ VideoPlaylistPrivacy,
+ VideoPrivacy
+} from '@shared/models'
import {
cleanupTests,
createMultipleServers,
describe('Should run filter:api.user.signup.allowed.result', function () {
+ before(async function () {
+ await servers[0].config.updateExistingSubConfig({ newConfig: { signup: { requiresApproval: false } } })
+ })
+
it('Should run on config endpoint', async function () {
const body = await servers[0].config.getConfig()
expect(body.signup.allowed).to.be.true
})
it('Should allow a signup', async function () {
- await servers[0].users.register({ username: 'john', password: 'password' })
+ await servers[0].registrations.register({ username: 'john1' })
})
it('Should not allow a signup', async function () {
- const res = await servers[0].users.register({
- username: 'jma',
- password: 'password',
+ const res = await servers[0].registrations.register({
+ username: 'jma 1',
+ expectedStatus: HttpStatusCode.FORBIDDEN_403
+ })
+
+ expect(res.body.error).to.equal('No jma 1')
+ })
+ })
+
+ describe('Should run filter:api.user.request-signup.allowed.result', function () {
+
+ before(async function () {
+ await servers[0].config.updateExistingSubConfig({ newConfig: { signup: { requiresApproval: true } } })
+ })
+
+ it('Should run on config endpoint', async function () {
+ const body = await servers[0].config.getConfig()
+ expect(body.signup.allowed).to.be.true
+ })
+
+ it('Should allow a signup request', async function () {
+ await servers[0].registrations.requestRegistration({ username: 'john2', registrationReason: 'tt' })
+ })
+
+ it('Should not allow a signup request', async function () {
+ const body = await servers[0].registrations.requestRegistration({
+ username: 'jma 2',
+ registrationReason: 'tt',
expectedStatus: HttpStatusCode.FORBIDDEN_403
})
- expect(res.body.error).to.equal('No jma')
+ expect((body as unknown as PeerTubeProblemDocument).error).to.equal('No jma 2')
})
})
UserNotificationType
} from '@shared/models'
import {
+ ConfigCommand,
createMultipleServers,
doubleFollow,
PeerTubeServer,
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
}
+// ---------------------------------------------------------------------------
+
async function checkUserRegistered (options: CheckerBaseParams & {
username: string
checkType: CheckerType
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
}
+async function checkRegistrationRequest (options: CheckerBaseParams & {
+ username: string
+ registrationReason: string
+ checkType: CheckerType
+}) {
+ const { username, registrationReason } = options
+ const notificationType = UserNotificationType.NEW_USER_REGISTRATION_REQUEST
+
+ function notificationChecker (notification: UserNotification, checkType: CheckerType) {
+ if (checkType === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ expect(notification.registration.username).to.equal(username)
+ } else {
+ expect(notification).to.satisfy(n => n.type !== notificationType || n.registration.username !== username)
+ }
+ }
+
+ function emailNotificationFinder (email: object) {
+ const text: string = email['text']
+
+ return text.includes(' wants to register ') && text.includes(username) && text.includes(registrationReason)
+ }
+
+ await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
+}
+
+// ---------------------------------------------------------------------------
+
async function checkNewActorFollow (options: CheckerBaseParams & {
followType: 'channel' | 'account'
followerName: string
const port = await MockSmtpServer.Instance.collectEmails(emails)
const overrideConfig = {
- smtp: {
- hostname: '127.0.0.1',
- port
- },
+ ...ConfigCommand.getEmailOverrideConfig(port),
+
signup: {
limit: 20
}
userAccessToken,
emails,
servers,
- channelId
+ channelId,
+ baseOverrideConfig: overrideConfig
}
}
checkNewAccountAbuseForModerators,
checkNewPeerTubeVersion,
checkNewPluginVersion,
- checkVideoStudioEditionIsFinished
+ checkVideoStudioEditionIsFinished,
+ checkRegistrationRequest
}
// ---------------------------------------------------------------------------
}
}
+ // ---------------------------------------------------------------------------
+
+ static getEmailOverrideConfig (emailPort: number) {
+ return {
+ smtp: {
+ hostname: '127.0.0.1',
+ port: emailPort
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+
+ enableSignup (requiresApproval: boolean) {
+ return this.updateExistingSubConfig({
+ newConfig: {
+ signup: {
+ enabled: true,
+ requiresApproval,
+ limit: -1
+ }
+ }
+ })
+ }
+
+ // ---------------------------------------------------------------------------
+
disableImports () {
return this.setImportsEnabled(false)
}
})
}
+ // ---------------------------------------------------------------------------
+
+ enableChannelSync () {
+ return this.setChannelSyncEnabled(true)
+ }
+
+ disableChannelSync () {
+ return this.setChannelSyncEnabled(false)
+ }
+
private setChannelSyncEnabled (enabled: boolean) {
return this.updateExistingSubConfig({
newConfig: {
})
}
- enableChannelSync () {
- return this.setChannelSyncEnabled(true)
- }
-
- disableChannelSync () {
- return this.setChannelSyncEnabled(false)
- }
+ // ---------------------------------------------------------------------------
enableLive (options: {
allowReplay?: boolean
})
}
+ // ---------------------------------------------------------------------------
+
enableStudio () {
return this.updateExistingSubConfig({
newConfig: {
})
}
+ // ---------------------------------------------------------------------------
+
getConfig (options: OverrideCommandOptions = {}) {
const path = '/api/v1/config'
signup: {
enabled: false,
limit: 5,
+ requiresApproval: true,
requiresEmailVerification: false,
minimumAge: 16
},
BlocklistCommand,
LoginCommand,
NotificationsCommand,
+ RegistrationsCommand,
SubscriptionsCommand,
TwoFactorCommand,
UsersCommand
views?: ViewsCommand
twoFactor?: TwoFactorCommand
videoToken?: VideoTokenCommand
+ registrations?: RegistrationsCommand
constructor (options: { serverNumber: number } | { url: string }) {
if ((options as any).url) {
this.views = new ViewsCommand(this)
this.twoFactor = new TwoFactorCommand(this)
this.videoToken = new VideoTokenCommand(this)
+ this.registrations = new RegistrationsCommand(this)
}
}
export * from './login'
export * from './login-command'
export * from './notifications-command'
+export * from './registrations-command'
export * from './subscriptions-command'
export * from './two-factor-command'
export * from './users-command'
--- /dev/null
+import { pick } from '@shared/core-utils'
+import { HttpStatusCode, ResultList, UserRegistration, UserRegistrationRequest } from '@shared/models'
+import { unwrapBody } from '../requests'
+import { AbstractCommand, OverrideCommandOptions } from '../shared'
+
+export class RegistrationsCommand extends AbstractCommand {
+
+ register (options: OverrideCommandOptions & Partial<UserRegistrationRequest> & Pick<UserRegistrationRequest, 'username'>) {
+ const { password = 'password', email = options.username + '@example.com' } = options
+ const path = '/api/v1/users/register'
+
+ return this.postBodyRequest({
+ ...options,
+
+ path,
+ fields: {
+ ...pick(options, [ 'username', 'displayName', 'channel' ]),
+
+ password,
+ email
+ },
+ implicitToken: false,
+ defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ }
+
+ requestRegistration (
+ options: OverrideCommandOptions & Partial<UserRegistrationRequest> & Pick<UserRegistrationRequest, 'username' | 'registrationReason'>
+ ) {
+ const { password = 'password', email = options.username + '@example.com' } = options
+ const path = '/api/v1/users/registrations/request'
+
+ return unwrapBody<UserRegistration>(this.postBodyRequest({
+ ...options,
+
+ path,
+ fields: {
+ ...pick(options, [ 'username', 'displayName', 'channel', 'registrationReason' ]),
+
+ password,
+ email
+ },
+ implicitToken: false,
+ defaultExpectedStatus: HttpStatusCode.OK_200
+ }))
+ }
+
+ // ---------------------------------------------------------------------------
+
+ accept (options: OverrideCommandOptions & {
+ id: number
+ moderationResponse: string
+ }) {
+ const { id, moderationResponse } = options
+ const path = '/api/v1/users/registrations/' + id + '/accept'
+
+ return this.postBodyRequest({
+ ...options,
+
+ path,
+ fields: { moderationResponse },
+ implicitToken: true,
+ defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ }
+
+ reject (options: OverrideCommandOptions & {
+ id: number
+ moderationResponse: string
+ }) {
+ const { id, moderationResponse } = options
+ const path = '/api/v1/users/registrations/' + id + '/reject'
+
+ return this.postBodyRequest({
+ ...options,
+
+ path,
+ fields: { moderationResponse },
+ implicitToken: true,
+ defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ }
+
+ // ---------------------------------------------------------------------------
+
+ delete (options: OverrideCommandOptions & {
+ id: number
+ }) {
+ const { id } = options
+ const path = '/api/v1/users/registrations/' + id
+
+ return this.deleteRequest({
+ ...options,
+
+ path,
+ implicitToken: true,
+ defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ }
+
+ // ---------------------------------------------------------------------------
+
+ list (options: OverrideCommandOptions & {
+ start?: number
+ count?: number
+ sort?: string
+ search?: string
+ } = {}) {
+ const path = '/api/v1/users/registrations'
+
+ return this.getRequestBody<ResultList<UserRegistration>>({
+ ...options,
+
+ path,
+ query: pick(options, [ 'start', 'count', 'sort', 'search' ]),
+ implicitToken: true,
+ defaultExpectedStatus: HttpStatusCode.OK_200
+ })
+ }
+
+ // ---------------------------------------------------------------------------
+
+ askSendVerifyEmail (options: OverrideCommandOptions & {
+ email: string
+ }) {
+ const { email } = options
+ const path = '/api/v1/users/registrations/ask-send-verify-email'
+
+ return this.postBodyRequest({
+ ...options,
+
+ path,
+ fields: { email },
+ implicitToken: false,
+ defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ }
+
+ verifyEmail (options: OverrideCommandOptions & {
+ registrationId: number
+ verificationString: string
+ }) {
+ const { registrationId, verificationString } = options
+ const path = '/api/v1/users/registrations/' + registrationId + '/verify-email'
+
+ return this.postBodyRequest({
+ ...options,
+
+ path,
+ fields: {
+ verificationString
+ },
+ implicitToken: false,
+ defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ }
+}
return this.server.login.getAccessToken({ username, password })
}
- register (options: OverrideCommandOptions & {
- username: string
- password?: string
- displayName?: string
- email?: string
- channel?: {
- name: string
- displayName: string
- }
- }) {
- const { username, password = 'password', displayName, channel, email = username + '@example.com' } = options
- const path = '/api/v1/users/register'
-
- return this.postBodyRequest({
- ...options,
-
- path,
- fields: {
- username,
- password,
- email,
- displayName,
- channel
- },
- implicitToken: false,
- defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
- })
- }
-
// ---------------------------------------------------------------------------
getMyInfo (options: OverrideCommandOptions = {}) {