diff --git a/core/user.js b/core/user.js index 73d3927e..54edc3af 100644 --- a/core/user.js +++ b/core/user.js @@ -15,6 +15,7 @@ exports.User = User; exports.getUserIdAndName = getUserIdAndName; exports.getUserName = getUserName; exports.loadProperties = loadProperties; +exports.getUserIdsWithProperty = getUserIdsWithProperty; exports.getUserList = getUserList; function User() { @@ -494,6 +495,26 @@ function loadProperties(options, cb) { }); } +// :TODO: make this much more flexible - propValue should allow for case-insensitive compare, etc. +function getUserIdsWithProperty(propName, propValue, cb) { + var userIds = []; + + userDb.each( + 'SELECT user_id ' + + 'FROM user_property ' + + 'WHERE prop_name = ? AND prop_value = ?;', + [ propName, propValue ], + function rowEntry(err, row) { + if(!err) { + userIds.push(row.user_id); + } + }, + function complete() { + cb(null, userIds); + } + ); +} + function getUserList(options, cb) { var userList = []; diff --git a/mods/apply.js b/mods/apply.js index cf83f58f..8e15d3d5 100644 --- a/mods/apply.js +++ b/mods/apply.js @@ -13,10 +13,12 @@ var sysMenuMethod = require('../core/system_menu_method.js'); var getDefaultMessageArea = require('../core/message_area.js').getDefaultMessageArea; var util = require('util'); +var async = require('async'); exports.submitApplication = submitApplication; function validateApplicationData(formData, cb) { + /* if(formData.value.username.length < Config.users.usernameMin) { cb('Handle too short!', [ 1 ]); return; @@ -62,6 +64,77 @@ function validateApplicationData(formData, cb) { cb(null); } }); + */ + + // :TODO: This entire section should be replaced with a generic form validation system!! + async.waterfall( + [ + function basics(callback) { + if(formData.value.username.length < Config.users.usernameMin) { + cb(new Error('Handle too short!'), [ 1 ]); + return; + } + + if(formData.value.username.length > Config.users.usernameMax) { + cb(new Error('Handle too long!'), [ 1 ]); + return; + } + + var re = new RegExp(Config.users.usernamePattern); + if(!re.test(formData.value.username)) { + cb(new Error('Handle contains invalid characters!'), [ 1 ] ); + return; + } + + var invalidNames = Config.users.newUserNames + Config.users.badUserNames; + if(invalidNames.indexOf(formData.value.username.toLowerCase()) > -1) { + cb(new Error('Handle is blacklisted!'), [ 1 ] ); + return; + } + + if(isNaN(Date.parse(formData.value.birthdate))) { + cb(new Error('Invalid birthdate!'), [ 3 ] ); + return; + } + + if(formData.value.password.length < Config.users.passwordMin) { + cb(new Error('Password too short!'), [ 9, 10 ]); + return; + } + + if(formData.value.password !== formData.value.passwordConfirm) { + cb(new Error('Passwords do not match!'), [ 9, 10 ]); + return; + } + + callback(null); + }, + function email(callback) { + user.getUserIdsWithProperty('email_address', formData.value.email, function userIdsWithEmail(err, uids) { + if(err) { + callback(new Error('Internal system error: ' + err.toString()), [ 1 ]); + } else if(uids.length > 0) { + callback(new Error('Email address not unique!'), [ 7 ] ); + } else { + callback(null); + } + }); + }, + function userName(callback) { + user.getUserIdAndName(formData.value.username, function userIdAndName(err) { + var alreadyExists = !err; + if(alreadyExists) { + callback(new Error('Username unavailable!'), [ 1 ] ); + } else { + cb(null); + } + }); + } + ], + function complete(err, viewIds) { + cb(err, viewIds); + } + ); } function submitApplication(callingMenu, formData, extraArgs) { @@ -76,9 +149,9 @@ function submitApplication(callingMenu, formData, extraArgs) { errorMsg : menuViewController.getView(11) }; - validateApplicationData(formData, function validationResult(errorMsg, viewIds) { - if(errorMsg) { - views.errorMsg.setText(errorMsg); + validateApplicationData(formData, function validationResult(err, viewIds) { + if(err) { + views.errorMsg.setText(err.toString().replace('Error: ', '')); viewIds.forEach(function formId(id) { menuViewController.getView(id).clearText(''); @@ -120,6 +193,7 @@ function submitApplication(callingMenu, formData, extraArgs) { newUser.properties.theme_id = Config.defaults.theme; } + // :TODO: .create() should also validate email uniqueness! newUser.create( { password : formData.value.password }, function created(err) { if(err) { Log.info( { error : err, username : formData.value.username }, 'New user creation failed');