Implemented configurable minimum signup age
Implements https://github.com/Chocobozzz/PeerTube/issues/3612 Fixed lint and removed debug Fixed another lint error Apply suggestions from code review Co-authored-by: Chocobozzz <chocobozzz@cpy.re> Add tests for min signup age config
This commit is contained in:
parent
f22e0e2c19
commit
1f256e7d3c
|
@ -158,6 +158,20 @@
|
||||||
|
|
||||||
<small i18n *ngIf="hasUnlimitedSignup()" class="text-muted">Signup won't be limited to a fixed number of users.</small>
|
<small i18n *ngIf="hasUnlimitedSignup()" class="text-muted">Signup won't be limited to a fixed number of users.</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div [ngClass]="getDisabledSignupClass()" class="mt-3">
|
||||||
|
<label i18n for="signupMinimumAge">Minimum required age to create an account</label>
|
||||||
|
|
||||||
|
<div class="number-with-unit">
|
||||||
|
<input
|
||||||
|
type="number" min="1" id="signupMinimumAge" class="form-control"
|
||||||
|
formControlName="minimumAge" [ngClass]="{ 'input-error': formErrors['signup.minimumAge'] }"
|
||||||
|
>
|
||||||
|
<span i18n>{form.value['signup']['minimumAge'], plural, =1 {year old} other {years old}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="formErrors.signup.minimumAge" class="form-error">{{ formErrors.signup.minimumAge }}</div>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</my-peertube-checkbox>
|
</my-peertube-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
@ -469,7 +483,7 @@
|
||||||
<ng-container formGroupName="twitter">
|
<ng-container formGroupName="twitter">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label i18n for="signupLimit">Your Twitter username</label>
|
<label for="servicesTwitterUsername" i18n>Your Twitter username</label>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text" id="servicesTwitterUsername" class="form-control"
|
type="text" id="servicesTwitterUsername" class="form-control"
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import { pairwise } from 'rxjs/operators'
|
import { pairwise } from 'rxjs/operators'
|
||||||
import { SelectOptionsItem } from 'src/types/select-options-item.model'
|
import { SelectOptionsItem } from 'src/types/select-options-item.model'
|
||||||
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'
|
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
SEARCH_INDEX_URL_VALIDATOR,
|
SEARCH_INDEX_URL_VALIDATOR,
|
||||||
SERVICES_TWITTER_USERNAME_VALIDATOR,
|
SERVICES_TWITTER_USERNAME_VALIDATOR,
|
||||||
SIGNUP_LIMIT_VALIDATOR,
|
SIGNUP_LIMIT_VALIDATOR,
|
||||||
|
SIGNUP_MINIMUM_AGE_VALIDATOR,
|
||||||
TRANSCODING_THREADS_VALIDATOR
|
TRANSCODING_THREADS_VALIDATOR
|
||||||
} from '@app/shared/form-validators/custom-config-validators'
|
} from '@app/shared/form-validators/custom-config-validators'
|
||||||
import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators'
|
import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators'
|
||||||
|
@ -120,7 +121,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
signup: {
|
signup: {
|
||||||
enabled: null,
|
enabled: null,
|
||||||
limit: SIGNUP_LIMIT_VALIDATOR,
|
limit: SIGNUP_LIMIT_VALIDATOR,
|
||||||
requiresEmailVerification: null
|
requiresEmailVerification: null,
|
||||||
|
minimumAge: SIGNUP_MINIMUM_AGE_VALIDATOR
|
||||||
},
|
},
|
||||||
import: {
|
import: {
|
||||||
videos: {
|
videos: {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<my-peertube-checkbox inputName="terms" formControlName="terms">
|
<my-peertube-checkbox inputName="terms" formControlName="terms">
|
||||||
<ng-template ptTemplate="label">
|
<ng-template ptTemplate="label">
|
||||||
<ng-container i18n>
|
<ng-container i18n>
|
||||||
I am at least 16 years old and agree
|
I am at least {{ minimumAge }} years old and agree
|
||||||
to the <a class="terms-anchor" (click)="onTermsClick($event)" href='#'>Terms</a>
|
to the <a class="terms-anchor" (click)="onTermsClick($event)" href='#'>Terms</a>
|
||||||
<ng-container *ngIf="hasCodeOfConduct"> and to the <a (click)="onCodeOfConductClick($event)" href='#'>Code of Conduct</a></ng-container>
|
<ng-container *ngIf="hasCodeOfConduct"> and to the <a (click)="onCodeOfConductClick($event)" href='#'>Code of Conduct</a></ng-container>
|
||||||
of this instance
|
of this instance
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
|
||||||
})
|
})
|
||||||
export class RegisterStepTermsComponent extends FormReactive implements OnInit {
|
export class RegisterStepTermsComponent extends FormReactive implements OnInit {
|
||||||
@Input() hasCodeOfConduct = false
|
@Input() hasCodeOfConduct = false
|
||||||
|
@Input() minimumAge = 16
|
||||||
|
|
||||||
@Output() formBuilt = new EventEmitter<FormGroup>()
|
@Output() formBuilt = new EventEmitter<FormGroup>()
|
||||||
@Output() termsClick = new EventEmitter<void>()
|
@Output() termsClick = new EventEmitter<void>()
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
<my-register-step-terms
|
<my-register-step-terms
|
||||||
[hasCodeOfConduct]="!!aboutHtml.codeOfConduct"
|
[hasCodeOfConduct]="!!aboutHtml.codeOfConduct"
|
||||||
|
[minimumAge]="minimumAge"
|
||||||
(formBuilt)="onTermsFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()"
|
(formBuilt)="onTermsFormBuilt($event)" (termsClick)="onTermsClick()" (codeOfConductClick)="onCodeOfConductClick()"
|
||||||
></my-register-step-terms>
|
></my-register-step-terms>
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,10 @@ export class RegisterComponent implements OnInit {
|
||||||
return this.serverConfig.signup.requiresEmailVerification
|
return this.serverConfig.signup.requiresEmailVerification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get minimumAge () {
|
||||||
|
return this.serverConfig.signup.minimumAge
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit (): void {
|
ngOnInit (): void {
|
||||||
this.serverConfig = this.route.snapshot.data.serverConfig
|
this.serverConfig = this.route.snapshot.data.serverConfig
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,8 @@ export class ServerService {
|
||||||
signup: {
|
signup: {
|
||||||
allowed: false,
|
allowed: false,
|
||||||
allowedForCurrentIP: false,
|
allowedForCurrentIP: false,
|
||||||
requiresEmailVerification: false
|
requiresEmailVerification: false,
|
||||||
|
minimumAge: 16
|
||||||
},
|
},
|
||||||
transcoding: {
|
transcoding: {
|
||||||
profile: 'default',
|
profile: 'default',
|
||||||
|
|
|
@ -49,6 +49,15 @@ export const SIGNUP_LIMIT_VALIDATOR: BuildFormValidator = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SIGNUP_MINIMUM_AGE_VALIDATOR: BuildFormValidator = {
|
||||||
|
VALIDATORS: [Validators.required, Validators.min(1), Validators.pattern('[0-9]+')],
|
||||||
|
MESSAGES: {
|
||||||
|
'required': $localize`Signup minimum age is required.`,
|
||||||
|
'min': $localize`Signup minimum age must be greater than 1.`,
|
||||||
|
'pattern': $localize`Signup minimum age must be a number.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const ADMIN_EMAIL_VALIDATOR: BuildFormValidator = {
|
export const ADMIN_EMAIL_VALIDATOR: BuildFormValidator = {
|
||||||
VALIDATORS: [Validators.required, Validators.email],
|
VALIDATORS: [Validators.required, Validators.email],
|
||||||
MESSAGES: {
|
MESSAGES: {
|
||||||
|
|
|
@ -229,6 +229,7 @@ contact_form:
|
||||||
signup:
|
signup:
|
||||||
enabled: false
|
enabled: false
|
||||||
limit: 10 # When the limit is reached, registrations are disabled. -1 == unlimited
|
limit: 10 # When the limit is reached, registrations are disabled. -1 == unlimited
|
||||||
|
minimum_age: 16 # Used to configure the signup form
|
||||||
requires_email_verification: false
|
requires_email_verification: false
|
||||||
filters:
|
filters:
|
||||||
cidr: # You can specify CIDR ranges to whitelist (empty = no filtering) or blacklist
|
cidr: # You can specify CIDR ranges to whitelist (empty = no filtering) or blacklist
|
||||||
|
|
|
@ -239,6 +239,7 @@ contact_form:
|
||||||
signup:
|
signup:
|
||||||
enabled: false
|
enabled: false
|
||||||
limit: 10 # When the limit is reached, registrations are disabled. -1 == unlimited
|
limit: 10 # When the limit is reached, registrations are disabled. -1 == unlimited
|
||||||
|
mimimum_age: 16
|
||||||
requires_email_verification: false
|
requires_email_verification: false
|
||||||
filters:
|
filters:
|
||||||
cidr: # You can specify CIDR ranges to whitelist (empty = no filtering) or blacklist
|
cidr: # You can specify CIDR ranges to whitelist (empty = no filtering) or blacklist
|
||||||
|
|
|
@ -174,7 +174,8 @@ function customConfig (): CustomConfig {
|
||||||
signup: {
|
signup: {
|
||||||
enabled: CONFIG.SIGNUP.ENABLED,
|
enabled: CONFIG.SIGNUP.ENABLED,
|
||||||
limit: CONFIG.SIGNUP.LIMIT,
|
limit: CONFIG.SIGNUP.LIMIT,
|
||||||
requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION
|
requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION,
|
||||||
|
minimumAge: CONFIG.SIGNUP.MINIMUM_AGE
|
||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
email: CONFIG.ADMIN.EMAIL
|
email: CONFIG.ADMIN.EMAIL
|
||||||
|
|
|
@ -19,7 +19,7 @@ function checkMissedConfig () {
|
||||||
'csp.enabled', 'csp.report_only', 'csp.report_uri',
|
'csp.enabled', 'csp.report_only', 'csp.report_uri',
|
||||||
'security.frameguard.enabled',
|
'security.frameguard.enabled',
|
||||||
'cache.previews.size', 'cache.captions.size', 'cache.torrents.size', 'admin.email', 'contact_form.enabled',
|
'cache.previews.size', 'cache.captions.size', 'cache.torrents.size', 'admin.email', 'contact_form.enabled',
|
||||||
'signup.enabled', 'signup.limit', 'signup.requires_email_verification',
|
'signup.enabled', 'signup.limit', 'signup.requires_email_verification', 'signup.minimum_age',
|
||||||
'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist',
|
'signup.filters.cidr.whitelist', 'signup.filters.cidr.blacklist',
|
||||||
'redundancy.videos.strategies', 'redundancy.videos.check_interval',
|
'redundancy.videos.strategies', 'redundancy.videos.check_interval',
|
||||||
'transcoding.enabled', 'transcoding.threads', 'transcoding.allow_additional_extensions', 'transcoding.hls.enabled',
|
'transcoding.enabled', 'transcoding.threads', 'transcoding.allow_additional_extensions', 'transcoding.hls.enabled',
|
||||||
|
|
|
@ -185,6 +185,7 @@ const CONFIG = {
|
||||||
get ENABLED () { return config.get<boolean>('signup.enabled') },
|
get ENABLED () { return config.get<boolean>('signup.enabled') },
|
||||||
get LIMIT () { return config.get<number>('signup.limit') },
|
get LIMIT () { return config.get<number>('signup.limit') },
|
||||||
get REQUIRES_EMAIL_VERIFICATION () { return config.get<boolean>('signup.requires_email_verification') },
|
get REQUIRES_EMAIL_VERIFICATION () { return config.get<boolean>('signup.requires_email_verification') },
|
||||||
|
get MINIMUM_AGE () { return config.get<number>('signup.minimum_age') },
|
||||||
FILTERS: {
|
FILTERS: {
|
||||||
CIDR: {
|
CIDR: {
|
||||||
get WHITELIST () { return config.get<string[]>('signup.filters.cidr.whitelist') },
|
get WHITELIST () { return config.get<string[]>('signup.filters.cidr.whitelist') },
|
||||||
|
|
|
@ -216,6 +216,7 @@ class ServerConfigManager {
|
||||||
const signup = {
|
const signup = {
|
||||||
allowed,
|
allowed,
|
||||||
allowedForCurrentIP,
|
allowedForCurrentIP,
|
||||||
|
minimumAge: CONFIG.SIGNUP.MINIMUM_AGE,
|
||||||
requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION
|
requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ const customConfigUpdateValidator = [
|
||||||
body('signup.enabled').isBoolean().withMessage('Should have a valid signup enabled boolean'),
|
body('signup.enabled').isBoolean().withMessage('Should have a valid signup enabled boolean'),
|
||||||
body('signup.limit').isInt().withMessage('Should have a valid signup limit'),
|
body('signup.limit').isInt().withMessage('Should have a valid signup limit'),
|
||||||
body('signup.requiresEmailVerification').isBoolean().withMessage('Should have a valid requiresEmailVerification boolean'),
|
body('signup.requiresEmailVerification').isBoolean().withMessage('Should have a valid requiresEmailVerification boolean'),
|
||||||
|
body('signup.minimumAge').isInt().withMessage("Should have a valid minimum age required"),
|
||||||
|
|
||||||
body('admin.email').isEmail().withMessage('Should have a valid administrator email'),
|
body('admin.email').isEmail().withMessage('Should have a valid administrator email'),
|
||||||
body('contactForm.enabled').isBoolean().withMessage('Should have a valid contact form enabled boolean'),
|
body('contactForm.enabled').isBoolean().withMessage('Should have a valid contact form enabled boolean'),
|
||||||
|
|
|
@ -73,7 +73,8 @@ describe('Test config API validators', function () {
|
||||||
signup: {
|
signup: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
limit: 5,
|
limit: 5,
|
||||||
requiresEmailVerification: false
|
requiresEmailVerification: false,
|
||||||
|
minimumAge: 16
|
||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
email: 'superadmin1@example.com'
|
email: 'superadmin1@example.com'
|
||||||
|
|
|
@ -60,6 +60,7 @@ function checkInitialConfig (server: ServerInfo, data: CustomConfig) {
|
||||||
|
|
||||||
expect(data.signup.enabled).to.be.true
|
expect(data.signup.enabled).to.be.true
|
||||||
expect(data.signup.limit).to.equal(4)
|
expect(data.signup.limit).to.equal(4)
|
||||||
|
expect(data.signup.minimumAge).to.equal(16)
|
||||||
expect(data.signup.requiresEmailVerification).to.be.false
|
expect(data.signup.requiresEmailVerification).to.be.false
|
||||||
|
|
||||||
expect(data.admin.email).to.equal('admin' + server.internalServerNumber + '@example.com')
|
expect(data.admin.email).to.equal('admin' + server.internalServerNumber + '@example.com')
|
||||||
|
@ -151,6 +152,7 @@ function checkUpdatedConfig (data: CustomConfig) {
|
||||||
expect(data.signup.enabled).to.be.false
|
expect(data.signup.enabled).to.be.false
|
||||||
expect(data.signup.limit).to.equal(5)
|
expect(data.signup.limit).to.equal(5)
|
||||||
expect(data.signup.requiresEmailVerification).to.be.false
|
expect(data.signup.requiresEmailVerification).to.be.false
|
||||||
|
expect(data.signup.minimumAge).to.equal(10)
|
||||||
|
|
||||||
// We override admin email in parallel tests, so skip this exception
|
// We override admin email in parallel tests, so skip this exception
|
||||||
if (parallelTests() === false) {
|
if (parallelTests() === false) {
|
||||||
|
@ -316,7 +318,8 @@ describe('Test config', function () {
|
||||||
signup: {
|
signup: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
limit: 5,
|
limit: 5,
|
||||||
requiresEmailVerification: false
|
requiresEmailVerification: false,
|
||||||
|
minimumAge: 10
|
||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
email: 'superadmin1@example.com'
|
email: 'superadmin1@example.com'
|
||||||
|
|
|
@ -98,7 +98,8 @@ function updateCustomSubConfig (url: string, token: string, newConfig: DeepParti
|
||||||
signup: {
|
signup: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
limit: 5,
|
limit: 5,
|
||||||
requiresEmailVerification: false
|
requiresEmailVerification: false,
|
||||||
|
minimumAge: 16
|
||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
email: 'superadmin1@example.com'
|
email: 'superadmin1@example.com'
|
||||||
|
|
|
@ -69,6 +69,7 @@ export interface CustomConfig {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
limit: number
|
limit: number
|
||||||
requiresEmailVerification: boolean
|
requiresEmailVerification: boolean
|
||||||
|
minimumAge: number
|
||||||
}
|
}
|
||||||
|
|
||||||
admin: {
|
admin: {
|
||||||
|
|
|
@ -84,6 +84,7 @@ export interface ServerConfig {
|
||||||
allowed: boolean
|
allowed: boolean
|
||||||
allowedForCurrentIP: boolean
|
allowedForCurrentIP: boolean
|
||||||
requiresEmailVerification: boolean
|
requiresEmailVerification: boolean
|
||||||
|
minimumAge: number
|
||||||
}
|
}
|
||||||
|
|
||||||
transcoding: {
|
transcoding: {
|
||||||
|
|
Loading…
Reference in New Issue