diff --git a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts index 2b7ba353c..468be022c 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-profile/my-account-profile.component.ts @@ -47,7 +47,7 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit { updateMyProfile () { const displayName = this.form.value['display-name'] - const description = this.form.value['description'] + const description = this.form.value['description'] || null this.error = null diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts index 0f03548ad..fab9cacd8 100644 --- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts +++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-create.component.ts @@ -64,8 +64,8 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE const body = this.form.value const videoChannelCreate: VideoChannelCreate = { displayName: body['display-name'], - description: body.description || undefined, - support: body.support || undefined + description: body.description || null, + support: body.support || null } this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe( diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts index c0dc6a939..9adc38691 100644 --- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts +++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts @@ -92,8 +92,8 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE const body = this.form.value const videoChannelUpdate: VideoChannelUpdate = { displayName: body['display-name'], - description: body.description || undefined, - support: body.support || undefined + description: body.description || null, + support: body.support || null } this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.uuid, videoChannelUpdate).subscribe( diff --git a/client/src/app/shared/forms/form-validators/user.ts b/client/src/app/shared/forms/form-validators/user.ts index c6b65e0df..0973f1b00 100644 --- a/client/src/app/shared/forms/form-validators/user.ts +++ b/client/src/app/shared/forms/form-validators/user.ts @@ -60,12 +60,10 @@ export const USER_DISPLAY_NAME = { } export const USER_DESCRIPTION = { VALIDATORS: [ - Validators.required, Validators.minLength(3), Validators.maxLength(250) ], MESSAGES: { - 'required': 'Description is required.', 'minlength': 'Description must be at least 3 characters long.', 'maxlength': 'Description cannot be more than 250 characters long.' } diff --git a/client/src/app/shared/misc/utils.ts b/client/src/app/shared/misc/utils.ts index 99f6b3cf0..b5bf99be2 100644 --- a/client/src/app/shared/misc/utils.ts +++ b/client/src/app/shared/misc/utils.ts @@ -66,7 +66,7 @@ function objectToFormData (obj: any, form?: FormData, namespace?: string) { if (obj[key] === undefined) continue - if (typeof obj[ key ] === 'object' && !(obj[ key ] instanceof File)) { + if (obj[key] !== null && typeof obj[ key ] === 'object' && !(obj[ key ] instanceof File)) { objectToFormData(obj[ key ], fd, key) } else { fd.append(formKey, obj[ key ]) diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index 8870cbee4..b45777c55 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts @@ -54,11 +54,11 @@ export class VideoService { } updateVideo (video: VideoEdit) { - const language = video.language || undefined - const licence = video.licence || undefined - const category = video.category || undefined - const description = video.description || undefined - const support = video.support || undefined + const language = video.language || null + const licence = video.licence || null + const category = video.category || null + const description = video.description || null + const support = video.support || null const body: VideoUpdate = { name: video.name, diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts index 8a270b777..275482fa1 100644 --- a/server/helpers/custom-validators/misc.ts +++ b/server/helpers/custom-validators/misc.ts @@ -25,10 +25,22 @@ function isIdOrUUIDValid (value: string) { return isIdValid(value) || isUUIDValid(value) } -function isBooleanValid (value: string) { +function isBooleanValid (value: any) { return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value)) } +function toIntOrNull (value: string) { + if (value === 'null') return null + + return validator.toInt(value) +} + +function toStringOrNull (value: string) { + if (value === 'null') return null + + return value +} + function isFileValid ( files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], mimeTypeRegex: string, @@ -61,6 +73,8 @@ export { isUUIDValid, isIdOrUUIDValid, isDateValid, + toStringOrNull, isBooleanValid, + toIntOrNull, isFileValid } diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts index e3543ef93..b93dccc50 100644 --- a/server/middlewares/validators/videos.ts +++ b/server/middlewares/validators/videos.ts @@ -2,7 +2,7 @@ import * as express from 'express' import 'express-validator' import { body, param, query } from 'express-validator/check' import { UserRight, VideoPrivacy } from '../../../shared' -import { isBooleanValid, isIdOrUUIDValid, isIdValid, isUUIDValid } from '../../helpers/custom-validators/misc' +import { isBooleanValid, isIdOrUUIDValid, isIdValid, isUUIDValid, toIntOrNull, toStringOrNull } from '../../helpers/custom-validators/misc' import { isVideoAbuseReasonValid, isVideoCategoryValid, @@ -14,7 +14,8 @@ import { isVideoLicenceValid, isVideoNameValid, isVideoPrivacyValid, - isVideoRatingTypeValid, isVideoSupportValid, + isVideoRatingTypeValid, + isVideoSupportValid, isVideoTagsValid } from '../../helpers/custom-validators/videos' import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils' @@ -41,16 +42,40 @@ const videosAddValidator = [ + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') ), body('name').custom(isVideoNameValid).withMessage('Should have a valid name'), - body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'), - body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'), - body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'), - body('nsfw').custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'), - body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), - body('support').optional().custom(isVideoSupportValid).withMessage('Should have a valid support text'), + body('category') + .optional() + .customSanitizer(toIntOrNull) + .custom(isVideoCategoryValid).withMessage('Should have a valid category'), + body('licence') + .optional() + .customSanitizer(toIntOrNull) + .custom(isVideoLicenceValid).withMessage('Should have a valid licence'), + body('language') + .optional() + .customSanitizer(toStringOrNull) + .custom(isVideoLanguageValid).withMessage('Should have a valid language'), + body('nsfw') + .toBoolean() + .custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'), + body('description') + .optional() + .customSanitizer(toStringOrNull) + .custom(isVideoDescriptionValid).withMessage('Should have a valid description'), + body('support') + .optional() + .customSanitizer(toStringOrNull) + .custom(isVideoSupportValid).withMessage('Should have a valid support text'), + body('tags') + .optional() + .custom(isVideoTagsValid).withMessage('Should have correct tags'), + body('commentsEnabled') + .toBoolean() + .custom(isBooleanValid).withMessage('Should have comments enabled boolean'), + body('privacy') + .optional() + .toInt() + .custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), body('channelId').custom(isIdValid).withMessage('Should have correct video channel id'), - body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), - body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), - body('commentsEnabled').custom(isBooleanValid).withMessage('Should have comments enabled boolean'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files }) @@ -110,16 +135,44 @@ const videosUpdateValidator = [ 'This preview file is not supported. Please, make sure it is of the following type : ' + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') ), - body('name').optional().custom(isVideoNameValid).withMessage('Should have a valid name'), - body('category').optional().custom(isVideoCategoryValid).withMessage('Should have a valid category'), - body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'), - body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'), - body('nsfw').optional().custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'), - body('privacy').optional().custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), - body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), - body('support').optional().custom(isVideoSupportValid).withMessage('Should have a valid support text'), - body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), - body('commentsEnabled').optional().custom(isBooleanValid).withMessage('Should have comments enabled boolean'), + body('name') + .optional() + .custom(isVideoNameValid).withMessage('Should have a valid name'), + body('category') + .optional() + .customSanitizer(toIntOrNull) + .custom(isVideoCategoryValid).withMessage('Should have a valid category'), + body('licence') + .optional() + .customSanitizer(toIntOrNull) + .custom(isVideoLicenceValid).withMessage('Should have a valid licence'), + body('language') + .optional() + .customSanitizer(toStringOrNull) + .custom(isVideoLanguageValid).withMessage('Should have a valid language'), + body('nsfw') + .optional() + .toBoolean() + .custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'), + body('privacy') + .optional() + .toInt() + .custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), + body('description') + .optional() + .customSanitizer(toStringOrNull) + .custom(isVideoDescriptionValid).withMessage('Should have a valid description'), + body('support') + .optional() + .customSanitizer(toStringOrNull) + .custom(isVideoSupportValid).withMessage('Should have a valid support text'), + body('tags') + .optional() + .custom(isVideoTagsValid).withMessage('Should have correct tags'), + body('commentsEnabled') + .optional() + .toBoolean() + .custom(isBooleanValid).withMessage('Should have comments enabled boolean'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videosUpdate parameters', { parameters: req.body }) diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts index 499a4fc94..2b341a5b3 100644 --- a/server/tests/api/check-params/videos.ts +++ b/server/tests/api/check-params/videos.ts @@ -231,13 +231,6 @@ describe('Test videos API validator', function () { await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) - it('Should fail with a bad nsfw attribute', async function () { - const fields = immutableAssign(baseCorrectParams, { nsfw: 2 }) - const attaches = baseCorrectAttaches - - await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) - }) - it('Should fail without commentsEnabled attribute', async function () { const fields = omit(baseCorrectParams, 'commentsEnabled') const attaches = baseCorrectAttaches @@ -245,13 +238,6 @@ describe('Test videos API validator', function () { await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) }) - it('Should fail with a bad commentsEnabled attribute', async function () { - const fields = immutableAssign(baseCorrectParams, { commentsEnabled: 2 }) - const attaches = baseCorrectAttaches - - await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) - }) - it('Should fail with a long description', async function () { const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) }) const attaches = baseCorrectAttaches @@ -485,18 +471,6 @@ describe('Test videos API validator', function () { await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) }) - it('Should fail with a bad nsfw attribute', async function () { - const fields = immutableAssign(baseCorrectParams, { nsfw: 2 }) - - await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) - }) - - it('Should fail with a bad commentsEnabled attribute', async function () { - const fields = immutableAssign(baseCorrectParams, { commentsEnabled: 2 }) - - await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) - }) - it('Should fail with a long description', async function () { const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) })