2015-07-20 00:09:47 -07:00
'use strict' ;
exports = module . exports = {
get : get ,
getByUsername : getByUsername ,
getByEmail : getByEmail ,
getByAccessToken : getByAccessToken ,
getByResetToken : getByResetToken ,
2016-01-13 12:28:38 -08:00
getOwner : getOwner ,
2016-02-09 09:37:12 -08:00
getAllWithGroupIds : getAllWithGroupIds ,
2019-01-14 16:08:55 +01:00
getAllWithGroupIdsPaged : getAllWithGroupIdsPaged ,
2015-07-20 00:09:47 -07:00
getAllAdmins : getAllAdmins ,
add : add ,
del : del ,
update : update ,
count : count ,
_clear : clear
} ;
var assert = require ( 'assert' ) ,
database = require ( './database.js' ) ,
debug = require ( 'debug' ) ( 'box:userdb' ) ,
2019-01-15 17:21:40 +01:00
DatabaseError = require ( './databaseerror' ) ,
mysql = require ( 'mysql' ) ;
2015-07-20 00:09:47 -07:00
2018-07-26 17:17:52 -07:00
var USERS _FIELDS = [ 'id' , 'username' , 'email' , 'fallbackEmail' , 'password' , 'salt' , 'createdAt' , 'modifiedAt' , 'resetToken' , 'displayName' ,
'twoFactorAuthenticationEnabled' , 'twoFactorAuthenticationSecret' , 'admin' ] . join ( ',' ) ;
2015-07-20 00:09:47 -07:00
2016-04-05 10:54:09 +02:00
function postProcess ( result ) {
assert . strictEqual ( typeof result , 'object' ) ;
2018-04-25 16:40:17 +02:00
result . twoFactorAuthenticationEnabled = ! ! result . twoFactorAuthenticationEnabled ;
2018-07-26 17:17:52 -07:00
result . admin = ! ! result . admin ;
2018-04-25 16:40:17 +02:00
2016-04-05 10:54:09 +02:00
return result ;
}
2015-07-20 00:09:47 -07:00
function get ( userId , callback ) {
assert . strictEqual ( typeof userId , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
database . query ( 'SELECT ' + USERS _FIELDS + ' FROM users WHERE id = ?' , [ userId ] , function ( error , result ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( result . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
2016-04-05 10:54:09 +02:00
callback ( null , postProcess ( result [ 0 ] ) ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
function getByUsername ( username , callback ) {
assert . strictEqual ( typeof username , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2016-04-14 16:25:46 +02:00
database . query ( 'SELECT ' + USERS _FIELDS + ' FROM users WHERE username = ?' , [ username ] , function ( error , result ) {
2016-04-01 21:43:48 +02:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( result . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
2016-04-05 10:54:09 +02:00
callback ( null , postProcess ( result [ 0 ] ) ) ;
2016-04-01 21:43:48 +02:00
} ) ;
2015-07-20 00:09:47 -07:00
}
function getByEmail ( email , callback ) {
assert . strictEqual ( typeof email , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2016-04-14 16:25:46 +02:00
database . query ( 'SELECT ' + USERS _FIELDS + ' FROM users WHERE email = ?' , [ email ] , function ( error , result ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( result . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
2016-04-05 10:54:09 +02:00
callback ( null , postProcess ( result [ 0 ] ) ) ;
2016-01-13 12:28:38 -08:00
} ) ;
}
function getOwner ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
2017-11-02 14:35:16 -07:00
// the first created user it the 'owner'
2018-07-26 17:17:52 -07:00
database . query ( 'SELECT ' + USERS _FIELDS + ' FROM users WHERE admin=1 ORDER BY createdAt LIMIT 1' , function ( error , result ) {
2018-10-29 14:11:43 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( result . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
2016-01-13 12:28:38 -08:00
2018-10-29 14:11:43 -07:00
callback ( null , postProcess ( result [ 0 ] ) ) ;
} ) ;
2015-07-20 00:09:47 -07:00
}
2018-06-12 17:22:41 -07:00
function getByResetToken ( email , resetToken , callback ) {
assert . strictEqual ( typeof email , 'string' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof resetToken , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
if ( resetToken . length === 0 ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , 'Empty resetToken not allowed' ) ) ;
2018-06-12 17:22:41 -07:00
database . query ( 'SELECT ' + USERS _FIELDS + ' FROM users WHERE email=? AND resetToken=?' , [ email , resetToken ] , function ( error , result ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
if ( result . length === 0 ) return callback ( new DatabaseError ( DatabaseError . NOT _FOUND ) ) ;
2016-04-05 10:54:09 +02:00
callback ( null , postProcess ( result [ 0 ] ) ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2016-02-09 09:37:12 -08:00
function getAllWithGroupIds ( callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2016-02-09 08:52:16 -08:00
database . query ( 'SELECT ' + USERS _FIELDS + ',GROUP_CONCAT(groupMembers.groupId) AS groupIds ' +
' FROM users LEFT OUTER JOIN groupMembers ON users.id = groupMembers.userId ' +
2016-04-06 09:08:59 -07:00
' GROUP BY users.id ORDER BY users.username' , function ( error , results ) {
2019-01-14 16:08:55 +01:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
results . forEach ( function ( result ) {
result . groupIds = result . groupIds ? result . groupIds . split ( ',' ) : [ ] ;
} ) ;
results . forEach ( postProcess ) ;
callback ( null , results ) ;
} ) ;
}
2019-01-15 17:21:40 +01:00
function getAllWithGroupIdsPaged ( search , page , perPage , callback ) {
assert ( typeof search === 'string' || search === null ) ;
2019-01-14 16:08:55 +01:00
assert . strictEqual ( typeof page , 'number' ) ;
assert . strictEqual ( typeof perPage , 'number' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2019-01-15 17:21:40 +01:00
var query = ` SELECT ${ USERS _FIELDS } ,GROUP_CONCAT(groupMembers.groupId) AS groupIds FROM users LEFT OUTER JOIN groupMembers ON users.id = groupMembers.userId ` ;
if ( search ) query += ' WHERE (users.username LIKE ' + mysql . escape ( ` % ${ search } % ` ) + ') ' ;
query += ` GROUP BY users.id ORDER BY users.username ASC LIMIT ${ ( page - 1 ) * perPage } , ${ perPage } ` ;
2019-01-14 16:08:55 +01:00
database . query ( query , function ( error , results ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
2016-02-09 08:52:16 -08:00
results . forEach ( function ( result ) {
result . groupIds = result . groupIds ? result . groupIds . split ( ',' ) : [ ] ;
} ) ;
2016-04-05 10:54:09 +02:00
results . forEach ( postProcess ) ;
2015-07-20 00:09:47 -07:00
callback ( null , results ) ;
} ) ;
}
function getAllAdmins ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
2017-11-02 14:35:16 -07:00
// the mailer code relies on the first object being the 'owner' (thus the ORDER)
2018-07-26 17:17:52 -07:00
database . query ( 'SELECT ' + USERS _FIELDS + ' FROM users WHERE admin=1 ORDER BY createdAt' , function ( error , results ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
2016-04-05 10:54:09 +02:00
results . forEach ( postProcess ) ;
2015-07-20 00:09:47 -07:00
callback ( null , results ) ;
} ) ;
}
function add ( userId , user , callback ) {
assert . strictEqual ( typeof userId , 'string' ) ;
2017-02-02 00:23:11 -08:00
assert ( user . username === null || typeof user . username === 'string' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof user . password , 'string' ) ;
assert . strictEqual ( typeof user . email , 'string' ) ;
2018-01-21 14:25:39 +01:00
assert . strictEqual ( typeof user . fallbackEmail , 'string' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof user . salt , 'string' ) ;
assert . strictEqual ( typeof user . createdAt , 'string' ) ;
assert . strictEqual ( typeof user . modifiedAt , 'string' ) ;
assert . strictEqual ( typeof user . resetToken , 'string' ) ;
2016-01-19 12:39:54 +01:00
assert . strictEqual ( typeof user . displayName , ' string ');
2018-07-26 17:17:52 -07:00
assert.strictEqual(typeof user.admin, ' boolean ');
2015-07-20 00:09:47 -07:00
assert.strictEqual(typeof callback, ' function ');
2018-07-26 17:17:52 -07:00
const query = ' INSERT INTO users ( id , username , password , email , fallbackEmail , salt , createdAt , modifiedAt , resetToken , displayName , admin ) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? ) ';
const args = [ userId, user.username, user.password, user.email, user.fallbackEmail, user.salt, user.createdAt, user.modifiedAt, user.resetToken, user.displayName, user.admin ];
2017-02-14 10:42:32 -08:00
2018-01-26 17:56:07 +01:00
database.query(query, args, function (error) {
if (error && error.code === ' ER _DUP _ENTRY ' && error.sqlMessage.indexOf(' users _email ') !== -1) return callback(new DatabaseError(DatabaseError.ALREADY_EXISTS, ' email already exists '));
if (error && error.code === ' ER _DUP _ENTRY ' && error.sqlMessage.indexOf(' users _username ') !== -1) return callback(new DatabaseError(DatabaseError.ALREADY_EXISTS, ' username already exists '));
2018-05-13 21:02:57 -07:00
if (error && error.code === ' ER _DUP _ENTRY ' && error.sqlMessage.indexOf(' PRIMARY ') !== -1) return callback(new DatabaseError(DatabaseError.ALREADY_EXISTS, ' id already exists '));
2018-01-24 12:56:12 +01:00
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
2015-07-20 00:09:47 -07:00
callback(null);
});
}
function del(userId, callback) {
assert.strictEqual(typeof userId, ' string ');
assert.strictEqual(typeof callback, ' function ');
2016-02-11 12:02:35 +01:00
// also cleanup the groupMembers table
var queries = [];
2016-02-11 12:07:43 +01:00
queries.push({ query: ' DELETE FROM groupMembers WHERE userId = ? ', args: [ userId ] });
2018-08-27 13:58:02 -07:00
queries.push({ query: ' DELETE FROM tokens WHERE identifier = ? ', args: [ userId ] });
2016-02-11 12:02:35 +01:00
queries.push({ query: ' DELETE FROM users WHERE id = ? ', args: [ userId ] });
2016-02-11 12:07:43 +01:00
database.transaction(queries, function (error, result) {
if (error && error.code === ' ER _NO _REFERENCED _ROW _2 ') return callback(new DatabaseError(DatabaseError.NOT_FOUND, error));
2015-07-20 00:09:47 -07:00
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
2018-08-27 13:58:02 -07:00
if (result[2].affectedRows !== 1) return callback(new DatabaseError(DatabaseError.NOT_FOUND));
2015-07-20 00:09:47 -07:00
callback(error);
});
}
function getByAccessToken(accessToken, callback) {
assert.strictEqual(typeof accessToken, ' string ');
assert.strictEqual(typeof callback, ' function ');
debug(' getByAccessToken : ' + accessToken);
database.query(' SELECT ' + USERS_FIELDS + ' FROM users , tokens WHERE tokens . accessToken = ? ', [ accessToken ], function (error, result) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (result.length === 0) return callback(new DatabaseError(DatabaseError.NOT_FOUND));
2016-04-05 10:54:09 +02:00
callback(null, postProcess(result[0]));
2015-07-20 00:09:47 -07:00
});
}
function clear(callback) {
database.query(' DELETE FROM users ', function (error) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
callback(error);
});
}
function update(userId, user, callback) {
assert.strictEqual(typeof userId, ' string ');
assert.strictEqual(typeof user, ' object ');
assert.strictEqual(typeof callback, ' function ');
var args = [ ];
var fields = [ ];
for (var k in user) {
fields.push(k + ' = ? ');
2016-04-05 10:54:09 +02:00
if (k === ' username ') {
2017-02-02 00:23:11 -08:00
assert(user.username === null || typeof user.username === ' string ');
args.push(user.username);
2018-07-26 15:35:41 -07:00
} else if (k === ' email ' || k === ' fallbackEmail ') {
assert.strictEqual(typeof user[k], ' string ');
args.push(user[k]);
2018-07-26 17:17:52 -07:00
} else if (k === ' twoFactorAuthenticationEnabled ' || k === ' admin ') {
2018-07-26 15:35:41 -07:00
assert.strictEqual(typeof user[k], ' boolean ');
args.push(user[k] ? 1 : 0);
2016-04-05 10:54:09 +02:00
} else {
args.push(user[k]);
}
2015-07-20 00:09:47 -07:00
}
args.push(userId);
2018-01-24 12:56:12 +01:00
database.query(' UPDATE users SET ' + fields.join(' , ') + ' WHERE id = ? ', args, function (error) {
2018-01-26 17:56:07 +01:00
if (error && error.code === ' ER _DUP _ENTRY ' && error.sqlMessage.indexOf(' users _email ') !== -1) return callback(new DatabaseError(DatabaseError.ALREADY_EXISTS, ' email already exists '));
if (error && error.code === ' ER _DUP _ENTRY ' && error.sqlMessage.indexOf(' users _username ') !== -1) return callback(new DatabaseError(DatabaseError.ALREADY_EXISTS, ' username already exists '));
2015-07-20 00:09:47 -07:00
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
return callback(null);
});
}
function count(callback) {
assert.strictEqual(typeof callback, ' function ');
database.query(' SELECT COUNT ( * ) AS total FROM users ' , function ( error , result ) {
if ( error ) return callback ( new DatabaseError ( DatabaseError . INTERNAL _ERROR , error ) ) ;
return callback ( null , result [ 0 ] . total ) ;
} ) ;
}