2015-07-20 00:09:47 -07:00
/* jslint node:true */
'use strict' ;
exports = module . exports = {
profile : profile ,
info : info ,
update : update ,
list : listUser ,
create : createUser ,
changePassword : changePassword ,
remove : removeUser ,
verifyPassword : verifyPassword ,
2016-01-18 15:37:03 +01:00
requireAdmin : requireAdmin ,
2016-02-09 15:47:02 -08:00
sendInvite : sendInvite ,
setGroups : setGroups
2015-07-20 00:09:47 -07:00
} ;
var assert = require ( 'assert' ) ,
2016-01-20 15:33:11 +01:00
generatePassword = require ( '../password.js' ) . generate ,
2016-02-09 09:37:12 -08:00
groups = require ( '../groups.js' ) ,
2015-07-20 00:09:47 -07:00
HttpError = require ( 'connect-lastmile' ) . HttpError ,
HttpSuccess = require ( 'connect-lastmile' ) . HttpSuccess ,
user = require ( '../user.js' ) ,
tokendb = require ( '../tokendb.js' ) ,
UserError = user . UserError ;
function profile ( req , res , next ) {
assert . strictEqual ( typeof req . user , 'object' ) ;
var result = { } ;
result . id = req . user . id ;
result . tokenType = req . user . tokenType ;
if ( req . user . tokenType === tokendb . TYPE _USER || req . user . tokenType === tokendb . TYPE _DEV ) {
result . username = req . user . username ;
result . email = req . user . email ;
2016-01-25 14:08:11 +01:00
result . displayName = req . user . displayName ;
2015-07-20 00:09:47 -07:00
2016-02-10 13:35:16 +01:00
groups . isMember ( groups . ADMIN _GROUP _ID , req . user . id , function ( error , isAdmin ) {
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
result . admin = isAdmin ;
next ( new HttpSuccess ( 200 , result ) ) ;
} ) ;
} else {
next ( new HttpSuccess ( 200 , result ) ) ;
}
2015-07-20 00:09:47 -07:00
}
function createUser ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
if ( typeof req . body . username !== 'string' ) return next ( new HttpError ( 400 , 'username must be string' ) ) ;
if ( typeof req . body . email !== 'string' ) return next ( new HttpError ( 400 , 'email must be string' ) ) ;
2016-01-18 16:53:51 +01:00
if ( typeof req . body . invite !== 'boolean' ) return next ( new HttpError ( 400 , 'invite must be boolean' ) ) ;
2016-01-20 00:05:06 -08:00
if ( 'displayName' in req . body && typeof req . body . displayName !== 'string' ) return next ( new HttpError ( 400 , 'displayName must be string' ) ) ;
2015-07-20 00:09:47 -07:00
var username = req . body . username ;
2016-01-20 15:33:11 +01:00
var password = generatePassword ( ) ;
2015-07-20 00:09:47 -07:00
var email = req . body . email ;
2016-01-18 16:53:51 +01:00
var sendInvite = req . body . invite ;
2016-01-19 23:34:49 -08:00
var displayName = req . body . displayName || '' ;
2015-07-20 00:09:47 -07:00
2016-02-08 21:05:02 -08:00
user . create ( username , password , email , displayName , { invitor : req . user , sendInvite : sendInvite } , function ( error , user ) {
2015-07-20 00:09:47 -07:00
if ( error && error . reason === UserError . BAD _USERNAME ) return next ( new HttpError ( 400 , 'Invalid username' ) ) ;
if ( error && error . reason === UserError . BAD _EMAIL ) return next ( new HttpError ( 400 , 'Invalid email' ) ) ;
if ( error && error . reason === UserError . BAD _PASSWORD ) return next ( new HttpError ( 400 , 'Invalid password' ) ) ;
if ( error && error . reason === UserError . BAD _FIELD ) return next ( new HttpError ( 400 , error . message ) ) ;
if ( error && error . reason === UserError . ALREADY _EXISTS ) return next ( new HttpError ( 409 , 'Already exists' ) ) ;
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
var userInfo = {
id : user . id ,
username : user . username ,
email : user . email ,
admin : user . admin ,
resetToken : user . resetToken
} ;
next ( new HttpSuccess ( 201 , { userInfo : userInfo } ) ) ;
} ) ;
}
function update ( req , res , next ) {
2016-01-25 14:58:02 +01:00
assert . strictEqual ( typeof req . params . userId , 'string' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof req . user , 'object' ) ;
assert . strictEqual ( typeof req . body , 'object' ) ;
2016-01-25 14:12:09 +01:00
if ( 'email' in req . body && typeof req . body . email !== 'string' ) return next ( new HttpError ( 400 , 'email must be string' ) ) ;
2016-01-25 14:08:11 +01:00
if ( 'displayName' in req . body && typeof req . body . displayName !== 'string' ) return next ( new HttpError ( 400 , 'displayName must be string' ) ) ;
2015-07-20 00:09:47 -07:00
if ( req . user . tokenType !== tokendb . TYPE _USER ) return next ( new HttpError ( 403 , 'Token type not allowed' ) ) ;
2016-01-25 14:58:02 +01:00
user . get ( req . params . userId , function ( error , result ) {
if ( error && error . reason === UserError . NOT _FOUND ) return next ( new HttpError ( 404 , 'No such user' ) ) ;
2015-07-20 00:09:47 -07:00
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
2016-01-25 14:58:02 +01:00
user . update ( req . params . userId , result . username , req . body . email || result . email , req . body . displayName || result . displayName , function ( error ) {
if ( error && error . reason === UserError . BAD _EMAIL ) return next ( new HttpError ( 400 , error . message ) ) ;
if ( error && error . reason === UserError . NOT _FOUND ) return next ( new HttpError ( 404 , 'User not found' ) ) ;
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 204 ) ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
function changePassword ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
assert . strictEqual ( typeof req . user , 'object' ) ;
if ( typeof req . body . password !== 'string' ) return next ( new HttpError ( 400 , 'API call requires the users old password.' ) ) ;
if ( typeof req . body . newPassword !== 'string' ) return next ( new HttpError ( 400 , 'API call requires the users new password.' ) ) ;
if ( req . user . tokenType !== tokendb . TYPE _USER ) return next ( new HttpError ( 403 , 'Token type not allowed' ) ) ;
user . changePassword ( req . user . username , req . body . password , req . body . newPassword , function ( error ) {
if ( error && error . reason === UserError . BAD _PASSWORD ) return next ( new HttpError ( 400 , error . message ) ) ;
if ( error && error . reason === UserError . WRONG _PASSWORD ) return next ( new HttpError ( 403 , 'Wrong password' ) ) ;
if ( error && error . reason === UserError . NOT _FOUND ) return next ( new HttpError ( 403 , 'Wrong password' ) ) ;
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 204 ) ) ;
} ) ;
}
function listUser ( req , res , next ) {
user . list ( function ( error , result ) {
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 200 , { users : result } ) ) ;
} ) ;
}
function info ( req , res , next ) {
assert . strictEqual ( typeof req . params . userId , 'string' ) ;
user . get ( req . params . userId , function ( error , result ) {
if ( error && error . reason === UserError . NOT _FOUND ) return next ( new HttpError ( 404 , 'No such user' ) ) ;
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
2016-02-09 09:37:12 -08:00
groups . isMember ( groups . ADMIN _GROUP _ID , req . params . userId , function ( error , isAdmin ) {
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 200 , {
id : result . id ,
username : result . username ,
email : result . email ,
admin : isAdmin ,
displayName : result . displayName
} ) ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
function removeUser ( req , res , next ) {
assert . strictEqual ( typeof req . params . userId , 'string' ) ;
// rules:
// - admin can remove any user
// - admin cannot remove admin
// - user cannot remove himself <- TODO should this actually work?
if ( req . user . id === req . params . userId ) return next ( new HttpError ( 403 , 'Not allowed to remove yourself.' ) ) ;
user . remove ( req . params . userId , function ( error ) {
if ( error && error . reason === UserError . NOT _FOUND ) return next ( new HttpError ( 404 , 'User not found' ) ) ;
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 204 ) ) ;
} ) ;
}
function verifyPassword ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
// developers are allowed to through without password
if ( req . user . tokenType === tokendb . TYPE _DEV ) return next ( ) ;
if ( typeof req . body . password !== 'string' ) return next ( new HttpError ( 400 , 'API call requires user password' ) ) ;
2016-02-10 14:48:54 +01:00
groups . isMember ( groups . ADMIN _GROUP _ID , req . user . id , function ( error , isAdmin ) {
2015-07-20 00:09:47 -07:00
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
2016-02-10 14:48:54 +01:00
// Only allow admins or users, operating on themselves
if ( req . params . userId && ! ( req . user . id === req . params . userId || isAdmin ) ) return next ( new HttpError ( 403 , 'Not allowed' ) ) ;
user . verify ( req . user . username , req . body . password , function ( error ) {
if ( error && error . reason === UserError . WRONG _PASSWORD ) return next ( new HttpError ( 403 , 'Password incorrect' ) ) ;
if ( error && error . reason === UserError . NOT _FOUND ) return next ( new HttpError ( 403 , 'Password incorrect' ) ) ;
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
/ *
Middleware which makes the route only accessable for the admin user .
* /
function requireAdmin ( req , res , next ) {
assert . strictEqual ( typeof req . user , 'object' ) ;
2016-02-09 09:37:12 -08:00
groups . isMember ( groups . ADMIN _GROUP _ID , req . user . id , function ( error , isAdmin ) {
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
if ( ! isAdmin ) return next ( new HttpError ( 403 , 'API call requires admin rights.' ) ) ;
req . user . admin = true ;
2015-07-20 00:09:47 -07:00
2016-02-09 09:37:12 -08:00
next ( ) ;
} ) ;
2015-07-20 00:09:47 -07:00
}
2016-01-18 15:37:03 +01:00
function sendInvite ( req , res , next ) {
assert . strictEqual ( typeof req . params . userId , 'string' ) ;
user . sendInvite ( req . params . userId , function ( error ) {
if ( error && error . reason === UserError . NOT _FOUND ) return next ( new HttpError ( 404 , 'User not found' ) ) ;
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 200 , { } ) ) ;
} ) ;
}
2016-02-09 15:47:02 -08:00
function setGroups ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
assert . strictEqual ( typeof req . params . userId , 'string' ) ;
if ( ! Array . isArray ( req . body . groupIds ) ) return next ( new HttpError ( 400 , 'API call requires a groups array.' ) ) ;
2016-02-11 11:29:04 +01:00
// this route is only allowed for admins, so req.user has to be an admin
if ( req . user . id === req . params . userId && req . body . groupIds . indexOf ( groups . ADMIN _GROUP _ID ) === - 1 ) return next ( new HttpError ( 403 , 'Admin removing itself from admins is not allowed' ) ) ;
2016-02-09 15:47:02 -08:00
user . setGroups ( req . params . userId , req . body . groupIds , function ( error ) {
if ( error && error . reason === UserError . NOT _FOUND ) return next ( new HttpError ( 404 , 'One or more groups not found' ) ) ;
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 204 ) ) ;
} ) ;
}