Server: add database field validations

This commit is contained in:
Chocobozzz 2016-12-28 15:49:23 +01:00
parent 552cc9d646
commit 67bf9b96bb
13 changed files with 162 additions and 73 deletions

View File

@ -5,14 +5,19 @@ const validator = require('express-validator').validator
const miscValidators = require('./misc')
const podsValidators = {
isEachUniqueHostValid
isEachUniqueHostValid,
isHostValid
}
function isHostValid (host) {
return validator.isURL(host) && host.split('://').length === 1
}
function isEachUniqueHostValid (hosts) {
return miscValidators.isArray(hosts) &&
hosts.length !== 0 &&
hosts.every(function (host) {
return validator.isURL(host) && host.split('://').length === 1 && hosts.indexOf(host) === hosts.lastIndexOf(host)
return isHostValid(host) && hosts.indexOf(host) === hosts.lastIndexOf(host)
})
}

View File

@ -15,7 +15,6 @@ const videosValidators = {
isVideoDurationValid,
isVideoInfoHashValid,
isVideoNameValid,
isVideoPodHostValid,
isVideoTagsValid,
isVideoThumbnailValid,
isVideoThumbnail64Valid
@ -74,11 +73,6 @@ function isVideoNameValid (value) {
return validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
}
function isVideoPodHostValid (value) {
// TODO: set options (TLD...)
return validator.isURL(value)
}
function isVideoTagsValid (tags) {
return miscValidators.isArray(tags) &&
validator.isInt(tags.length, VIDEOS_CONSTRAINTS_FIELDS.TAGS) &&

View File

@ -69,7 +69,7 @@ const CONSTRAINTS_FIELDS = {
NAME: { min: 3, max: 50 }, // Length
DESCRIPTION: { min: 3, max: 250 }, // Length
EXTNAME: [ '.mp4', '.ogv', '.webm' ],
INFO_HASH: { min: 10, max: 50 }, // Length
INFO_HASH: { min: 40, max: 40 }, // Length, infohash is 20 bytes length but we represent it in hexa so 20 * 2
DURATION: { min: 1, max: 7200 }, // Number
TAGS: { min: 1, max: 3 }, // Number of total tags
TAG: { min: 2, max: 10 }, // Length

View File

@ -96,6 +96,7 @@ function createOAuthAdminIfNotExist (callback) {
const username = 'root'
const role = constants.USER_ROLES.ADMIN
const createOptions = {}
let password = ''
// Do not generate a random password for tests
@ -105,17 +106,20 @@ function createOAuthAdminIfNotExist (callback) {
if (process.env.NODE_APP_INSTANCE) {
password += process.env.NODE_APP_INSTANCE
}
// Our password is weak so do not validate it
createOptions.validate = false
} else {
password = passwordGenerator(8, true)
}
const user = db.User.build({
const userData = {
username,
password,
role
})
}
user.save().asCallback(function (err, createdUser) {
db.User.create(userData, createOptions).asCallback(function (err, createdUser) {
if (err) return callback(err)
logger.info('Username: ' + username)

View File

@ -1,9 +1,15 @@
'use strict'
module.exports = function (sequelize, DataTypes) {
const Application = sequelize.define('Application',
{
migrationVersion: {
type: DataTypes.INTEGER,
defaultValue: 0
defaultValue: 0,
allowNull: false,
validate: {
isInt: true
}
}
},
{

View File

@ -1,8 +1,19 @@
'use strict'
const customUsersValidators = require('../helpers/custom-validators').users
module.exports = function (sequelize, DataTypes) {
const Author = sequelize.define('Author',
{
name: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false,
validate: {
usernameValid: function (value) {
const res = customUsersValidators.isUserUsernameValid(value)
if (res === false) throw new Error('Username is not valid.')
}
}
}
},
{

View File

@ -1,11 +1,15 @@
'use strict'
module.exports = function (sequelize, DataTypes) {
const OAuthClient = sequelize.define('OAuthClient',
{
clientId: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false
},
clientSecret: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false
},
grants: {
type: DataTypes.ARRAY(DataTypes.STRING)
@ -28,9 +32,6 @@ module.exports = function (sequelize, DataTypes) {
return OAuthClient
}
// TODO: validation
// OAuthClientSchema.path('clientSecret').required(true)
// ---------------------------------------------------------------------------
function associate (models) {

View File

@ -1,3 +1,5 @@
'use strict'
const logger = require('../helpers/logger')
// ---------------------------------------------------------------------------
@ -6,16 +8,20 @@ module.exports = function (sequelize, DataTypes) {
const OAuthToken = sequelize.define('OAuthToken',
{
accessToken: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false
},
accessTokenExpiresAt: {
type: DataTypes.DATE
type: DataTypes.DATE,
allowNull: false
},
refreshToken: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false
},
refreshTokenExpiresAt: {
type: DataTypes.DATE
type: DataTypes.DATE,
allowNull: false
}
},
{
@ -33,11 +39,6 @@ module.exports = function (sequelize, DataTypes) {
return OAuthToken
}
// TODO: validation
// OAuthTokenSchema.path('accessToken').required(true)
// OAuthTokenSchema.path('client').required(true)
// OAuthTokenSchema.path('user').required(true)
// ---------------------------------------------------------------------------
function associate (models) {

View File

@ -3,6 +3,7 @@
const map = require('lodash/map')
const constants = require('../initializers/constants')
const customPodsValidators = require('../helpers/custom-validators').pods
// ---------------------------------------------------------------------------
@ -10,14 +11,27 @@ module.exports = function (sequelize, DataTypes) {
const Pod = sequelize.define('Pod',
{
host: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false,
validate: {
isHost: function (value) {
const res = customPodsValidators.isHostValid(value)
if (res === false) throw new Error('Host not valid.')
}
}
},
publicKey: {
type: DataTypes.STRING(5000)
type: DataTypes.STRING(5000),
allowNull: false
},
score: {
type: DataTypes.INTEGER,
defaultValue: constants.FRIEND_SCORE.BASE
defaultValue: constants.FRIEND_SCORE.BASE,
allowNull: false,
validate: {
isInt: true,
max: constants.FRIEND_SCORE.MAX
}
}
},
{
@ -42,12 +56,6 @@ module.exports = function (sequelize, DataTypes) {
return Pod
}
// TODO: max score -> constants.FRIENDS_SCORE.MAX
// TODO: validation
// PodSchema.path('host').validate(validator.isURL)
// PodSchema.path('publicKey').required(true)
// PodSchema.path('score').validate(function (value) { return !isNaN(value) })
// ------------------------------ METHODS ------------------------------
function toFormatedJSON () {
@ -82,15 +90,17 @@ function incrementScores (ids, value, callback) {
score: this.sequelize.literal('score +' + value)
}
const query = {
const options = {
where: {
id: {
$in: ids
}
}
},
// In this case score is a literal and not an integer so we do not validate it
validate: false
}
return this.update(update, query).asCallback(callback)
return this.update(update, options).asCallback(callback)
}
function list (callback) {

View File

@ -3,6 +3,7 @@
const each = require('async/each')
const eachLimit = require('async/eachLimit')
const waterfall = require('async/waterfall')
const values = require('lodash/values')
const constants = require('../initializers/constants')
const logger = require('../helpers/logger')
@ -17,11 +18,12 @@ module.exports = function (sequelize, DataTypes) {
const Request = sequelize.define('Request',
{
request: {
type: DataTypes.JSON
type: DataTypes.JSON,
allowNull: false
},
endpoint: {
// TODO: enum?
type: DataTypes.STRING
type: DataTypes.ENUM(values(constants.REQUEST_ENDPOINTS)),
allowNull: false
}
},
{
@ -196,7 +198,7 @@ function makeRequests () {
makeRequest(toPod, requestToMake.endpoint, requestToMake.datas, function (success) {
if (success === true) {
logger.debug('Removing requests for %s pod.', requestToMake.toPodId, { requestsIds: requestToMake.ids })
logger.debug('Removing requests for pod %s.', requestToMake.toPodId, { requestsIds: requestToMake.ids })
goodPods.push(requestToMake.toPodId)
@ -261,13 +263,13 @@ function updatePodsScore (goodPods, badPods) {
if (goodPods.length !== 0) {
Pod.incrementScores(goodPods, constants.PODS_SCORE.BONUS, function (err) {
if (err) logger.error('Cannot increment scores of good pods.')
if (err) logger.error('Cannot increment scores of good pods.', { error: err })
})
}
if (badPods.length !== 0) {
Pod.incrementScores(badPods, constants.PODS_SCORE.MALUS, function (err) {
if (err) logger.error('Cannot decrement scores of bad pods.')
if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
removeBadPods.call(self)
})
}

View File

@ -6,7 +6,8 @@ module.exports = function (sequelize, DataTypes) {
const Tag = sequelize.define('Tag',
{
name: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false
}
},
{

View File

@ -1,5 +1,11 @@
'use strict'
const values = require('lodash/values')
const modelUtils = require('./utils')
const constants = require('../initializers/constants')
const peertubeCrypto = require('../helpers/peertube-crypto')
const customUsersValidators = require('../helpers/custom-validators').users
// ---------------------------------------------------------------------------
@ -7,13 +13,28 @@ module.exports = function (sequelize, DataTypes) {
const User = sequelize.define('User',
{
password: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false,
validate: {
passwordValid: function (value) {
const res = customUsersValidators.isUserPasswordValid(value)
if (res === false) throw new Error('Password not valid.')
}
}
},
username: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false,
validate: {
usernameValid: function (value) {
const res = customUsersValidators.isUserUsernameValid(value)
if (res === false) throw new Error('Username not valid.')
}
}
},
role: {
type: DataTypes.STRING
type: DataTypes.ENUM(values(constants.USER_ROLES)),
allowNull: false
}
},
{
@ -41,11 +62,6 @@ module.exports = function (sequelize, DataTypes) {
return User
}
// TODO: Validation
// UserSchema.path('password').required(customUsersValidators.isUserPasswordValid)
// UserSchema.path('username').required(customUsersValidators.isUserUsernameValid)
// UserSchema.path('role').validate(customUsersValidators.isUserRoleValid)
function beforeCreateOrUpdate (user, options, next) {
peertubeCrypto.cryptPassword(user.password, function (err, hash) {
if (err) return next(err)

View File

@ -8,10 +8,12 @@ const map = require('lodash/map')
const parallel = require('async/parallel')
const parseTorrent = require('parse-torrent')
const pathUtils = require('path')
const values = require('lodash/values')
const constants = require('../initializers/constants')
const logger = require('../helpers/logger')
const modelUtils = require('./utils')
const customVideosValidators = require('../helpers/custom-validators').videos
// ---------------------------------------------------------------------------
@ -22,26 +24,61 @@ module.exports = function (sequelize, DataTypes) {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
primaryKey: true,
validate: {
isUUID: 4
}
},
name: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false,
validate: {
nameValid: function (value) {
const res = customVideosValidators.isVideoNameValid(value)
if (res === false) throw new Error('Video name is not valid.')
}
}
},
extname: {
// TODO: enum?
type: DataTypes.STRING
type: DataTypes.ENUM(values(constants.CONSTRAINTS_FIELDS.VIDEOS.EXTNAME)),
allowNull: false
},
remoteId: {
type: DataTypes.UUID
type: DataTypes.UUID,
allowNull: true,
validate: {
isUUID: 4
}
},
description: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false,
validate: {
descriptionValid: function (value) {
const res = customVideosValidators.isVideoDescriptionValid(value)
if (res === false) throw new Error('Video description is not valid.')
}
}
},
infoHash: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: false,
validate: {
infoHashValid: function (value) {
const res = customVideosValidators.isVideoInfoHashValid(value)
if (res === false) throw new Error('Video info hash is not valid.')
}
}
},
duration: {
type: DataTypes.INTEGER
type: DataTypes.INTEGER,
allowNull: false,
validate: {
durationValid: function (value) {
const res = customVideosValidators.isVideoDurationValid(value)
if (res === false) throw new Error('Video duration is not valid.')
}
}
}
},
{
@ -71,6 +108,7 @@ module.exports = function (sequelize, DataTypes) {
toRemoteJSON
},
hooks: {
beforeValidate,
beforeCreate,
afterDestroy
}
@ -80,13 +118,14 @@ module.exports = function (sequelize, DataTypes) {
return Video
}
// TODO: Validation
// VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid)
// VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid)
// VideoSchema.path('podHost').validate(customVideosValidators.isVideoPodHostValid)
// VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid)
// VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid)
// VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid)
function beforeValidate (video, options, next) {
if (video.isOwned()) {
// 40 hexa length
video.infoHash = '0123456789abcdef0123456789abcdef01234567'
}
return next(null)
}
function beforeCreate (video, options, next) {
const tasks = []
@ -113,9 +152,8 @@ function beforeCreate (video, options, next) {
if (err) return callback(err)
const parsedTorrent = parseTorrent(torrent)
video.infoHash = parsedTorrent.infoHash
callback(null)
video.set('infoHash', parsedTorrent.infoHash)
video.validate().asCallback(callback)
})
})
},