2015-07-20 00:09:47 -07:00
'use strict' ;
exports = module . exports = {
2020-07-10 07:23:33 -07:00
get ,
update ,
list ,
2021-07-15 09:50:11 -07:00
add ,
2021-06-26 09:57:07 -07:00
del ,
2021-07-15 09:50:11 -07:00
setPassword ,
2020-07-10 07:23:33 -07:00
verifyPassword ,
createInvite ,
sendInvite ,
setGroups ,
2021-01-15 14:28:41 +01:00
makeOwner ,
2020-07-10 07:23:33 -07:00
2021-04-14 20:45:13 -07:00
disableTwoFactorAuthentication ,
2020-07-10 07:23:33 -07:00
load
2015-07-20 00:09:47 -07:00
} ;
2021-06-26 09:57:07 -07:00
const assert = require ( 'assert' ) ,
2019-03-25 15:07:06 -07:00
auditSource = require ( '../auditsource.js' ) ,
2019-10-24 14:40:26 -07:00
BoxError = require ( '../boxerror.js' ) ,
2021-07-15 09:50:11 -07:00
groups = require ( '../groups.js' ) ,
2015-07-20 00:09:47 -07:00
HttpError = require ( 'connect-lastmile' ) . HttpError ,
HttpSuccess = require ( 'connect-lastmile' ) . HttpSuccess ,
2021-06-26 09:57:07 -07:00
safe = require ( 'safetydance' ) ,
2019-10-24 14:40:26 -07:00
users = require ( '../users.js' ) ;
2021-07-15 09:50:11 -07:00
async function load ( req , res , next ) {
2020-02-13 20:45:00 -08:00
assert . strictEqual ( typeof req . params . userId , 'string' ) ;
2021-07-15 09:50:11 -07:00
const [ error , result ] = await safe ( users . get ( req . params . userId ) ) ;
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
if ( ! result ) return next ( new HttpError ( 404 , 'User not found' ) ) ;
req . resource = result ;
2020-02-13 20:45:00 -08:00
2021-07-15 09:50:11 -07:00
next ( ) ;
2020-02-13 20:45:00 -08:00
}
2021-07-15 09:50:11 -07:00
async function add ( req , res , next ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof req . body , 'object' ) ;
if ( typeof req . body . email !== 'string' ) return next ( new HttpError ( 400 , 'email must be string' ) ) ;
2016-04-01 16:48:50 +02:00
if ( 'username' in req . body && typeof req . body . username !== 'string' ) return next ( new HttpError ( 400 , 'username must be string' ) ) ;
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' ) ) ;
2018-04-26 13:58:21 -07:00
if ( 'password' in req . body && typeof req . body . password !== 'string' ) return next ( new HttpError ( 400 , 'password must be string' ) ) ;
2020-02-21 12:17:06 -08:00
if ( 'role' in req . body ) {
if ( typeof req . body . role !== 'string' ) return next ( new HttpError ( 400 , 'role must be string' ) ) ;
2020-03-06 13:16:47 -08:00
if ( users . compareRoles ( req . user . role , req . body . role ) < 0 ) return next ( new HttpError ( 403 , ` role ' ${ req . body . role } ' is required but you are only ' ${ req . user . role } ' ` ) ) ;
2020-02-13 22:06:54 -08:00
}
2015-07-20 00:09:47 -07:00
2021-07-15 09:50:11 -07:00
const password = req . body . password || null ;
const email = req . body . email ;
const username = 'username' in req . body ? req . body . username : null ;
const displayName = req . body . displayName || '' ;
2015-07-20 00:09:47 -07:00
2021-08-10 13:50:52 -07:00
const [ error , id ] = await safe ( users . add ( email , { username , password , displayName , invitor : req . user , role : req . body . role || users . ROLE _USER } , auditSource . fromRequest ( req ) ) ) ;
2021-07-15 09:50:11 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2021-08-10 13:50:52 -07:00
next ( new HttpSuccess ( 201 , { id } ) ) ;
2015-07-20 00:09:47 -07:00
}
2021-07-15 09:50:11 -07:00
async function update ( req , res , next ) {
2020-02-13 20:45:00 -08:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
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' ) ) ;
2018-01-21 14:25:39 +01:00
if ( 'fallbackEmail' in req . body && typeof req . body . fallbackEmail !== 'string' ) return next ( new HttpError ( 400 , 'fallbackEmail 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' ) ) ;
2016-06-02 23:53:06 -07:00
if ( 'username' in req . body && typeof req . body . username !== 'string' ) return next ( new HttpError ( 400 , 'username must be a string' ) ) ;
2015-07-20 00:09:47 -07:00
2020-02-21 12:17:06 -08:00
if ( 'role' in req . body ) {
if ( typeof req . body . role !== 'string' ) return next ( new HttpError ( 400 , 'role must be a string' ) ) ;
2021-01-15 14:16:55 +01:00
if ( req . user . id === req . resource . id ) return next ( new HttpError ( 409 , 'Cannot set role flag on self' ) ) ;
2020-03-15 15:56:06 -07:00
if ( users . compareRoles ( req . user . role , req . body . role ) < 0 ) return next ( new HttpError ( 403 , ` role ' ${ req . body . role } ' is required but you are only ' ${ req . user . role } ' ` ) ) ;
2020-02-13 22:06:54 -08:00
}
2020-03-05 21:00:59 -08:00
if ( 'active' in req . body ) {
if ( typeof req . body . active !== 'boolean' ) return next ( new HttpError ( 400 , 'active must be a boolean' ) ) ;
if ( req . user . id === req . resource . id ) return next ( new HttpError ( 409 , 'Cannot set active flag on self' ) ) ;
}
2019-08-08 07:39:32 -07:00
2020-03-15 15:56:06 -07:00
if ( users . compareRoles ( req . user . role , req . resource . role ) < 0 ) return next ( new HttpError ( 403 , ` role ' ${ req . resource . role } ' is required but you are only ' ${ req . user . role } ' ` ) ) ;
2020-03-07 13:52:57 -08:00
2021-07-15 09:50:11 -07:00
const [ error ] = await safe ( users . update ( req . resource , req . body , auditSource . fromRequest ( req ) ) ) ;
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2021-07-15 09:50:11 -07:00
next ( new HttpSuccess ( 204 ) ) ;
2015-07-20 00:09:47 -07:00
}
2021-07-15 09:50:11 -07:00
async function list ( req , res , next ) {
const page = typeof req . query . page !== 'undefined' ? parseInt ( req . query . page ) : 1 ;
2019-01-14 16:39:20 +01:00
if ( ! page || page < 0 ) return next ( new HttpError ( 400 , 'page query param has to be a postive number' ) ) ;
2021-07-15 09:50:11 -07:00
const perPage = typeof req . query . per _page !== 'undefined' ? parseInt ( req . query . per _page ) : 25 ;
2019-01-14 16:39:20 +01:00
if ( ! perPage || perPage < 0 ) return next ( new HttpError ( 400 , 'per_page query param has to be a postive number' ) ) ;
2019-01-15 17:21:40 +01:00
if ( req . query . search && typeof req . query . search !== 'string' ) return next ( new HttpError ( 400 , 'search must be a string' ) ) ;
2021-08-20 11:30:35 -07:00
let [ error , results ] = await safe ( users . listPaged ( req . query . search || null , page , perPage ) ) ;
2021-07-15 09:50:11 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2016-06-03 00:04:17 -07:00
2021-07-15 09:50:11 -07:00
results = results . map ( users . removeRestrictedFields ) ;
2016-06-03 00:04:17 -07:00
2021-07-15 09:50:11 -07:00
next ( new HttpSuccess ( 200 , { users : results } ) ) ;
2015-07-20 00:09:47 -07:00
}
2016-04-17 18:27:11 +02:00
function get ( req , res , next ) {
2020-02-13 20:45:00 -08:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2016-02-25 14:03:42 +01:00
assert . strictEqual ( typeof req . user , 'object' ) ;
2015-07-20 00:09:47 -07:00
2020-02-13 20:45:00 -08:00
next ( new HttpSuccess ( 200 , users . removePrivateFields ( req . resource ) ) ) ;
2015-07-20 00:09:47 -07:00
}
2021-06-26 09:57:07 -07:00
async function del ( req , res , next ) {
2020-02-13 20:45:00 -08:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2015-07-20 00:09:47 -07:00
2020-02-13 20:45:00 -08:00
if ( req . user . id === req . resource . id ) return next ( new HttpError ( 409 , 'Not allowed to remove yourself.' ) ) ;
2020-02-21 12:17:06 -08:00
if ( users . compareRoles ( req . user . role , req . resource . role ) < 0 ) return next ( new HttpError ( 403 , ` role ' ${ req . resource . role } ' is required but user has only ' ${ req . user . role } ' ` ) ) ;
2015-07-20 00:09:47 -07:00
2021-06-29 09:44:16 -07:00
const [ error ] = await safe ( users . del ( req . resource , auditSource . fromRequest ( req ) ) ) ;
2021-06-26 09:57:07 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2021-06-26 09:57:07 -07:00
next ( new HttpSuccess ( 204 ) ) ;
2015-07-20 00:09:47 -07:00
}
2021-07-15 09:50:11 -07:00
async function verifyPassword ( req , res , next ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof req . body , 'object' ) ;
if ( typeof req . body . password !== 'string' ) return next ( new HttpError ( 400 , 'API call requires user password' ) ) ;
2021-07-15 09:50:11 -07:00
const [ error ] = await safe ( users . verifyWithUsername ( req . user . username , req . body . password , users . AP _WEBADMIN ) ) ;
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2015-07-20 00:09:47 -07:00
2021-07-15 09:50:11 -07:00
req . body . password = '<redacted>' ; // this will prevent logs from displaying plain text password
2017-05-05 15:36:47 -07:00
2021-07-15 09:50:11 -07:00
next ( ) ;
2015-07-20 00:09:47 -07:00
}
2021-07-15 09:50:11 -07:00
async function createInvite ( req , res , next ) {
2020-02-13 20:45:00 -08:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2018-08-17 09:49:58 -07:00
2020-02-21 12:17:06 -08:00
if ( users . compareRoles ( req . user . role , req . resource . role ) < 0 ) return next ( new HttpError ( 403 , ` role ' ${ req . resource . role } ' is required but user has only ' ${ req . user . role } ' ` ) ) ;
2020-02-13 22:06:54 -08:00
2021-07-15 09:50:11 -07:00
const [ error , result ] = await safe ( users . createInvite ( req . resource , auditSource . fromRequest ( req ) ) ) ;
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2021-04-14 20:45:13 -07:00
2021-07-15 09:50:11 -07:00
next ( new HttpSuccess ( 200 , result ) ) ;
2021-04-14 20:45:13 -07:00
}
function disableTwoFactorAuthentication ( req , res , next ) {
assert . strictEqual ( typeof req . resource , 'object' ) ;
if ( users . compareRoles ( req . user . role , req . resource . role ) < 0 ) return next ( new HttpError ( 403 , ` role ' ${ req . resource . role } ' is required but user has only ' ${ req . user . role } ' ` ) ) ;
users . disableTwoFactorAuthentication ( req . resource . id , function ( error , result ) {
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2018-08-17 09:49:58 -07:00
2020-02-05 15:53:05 +01:00
next ( new HttpSuccess ( 200 , result ) ) ;
2018-08-17 09:49:58 -07:00
} ) ;
}
2021-07-15 09:50:11 -07:00
async function sendInvite ( req , res , next ) {
2020-02-13 20:45:00 -08:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2016-01-18 15:37:03 +01:00
2020-02-21 12:17:06 -08:00
if ( users . compareRoles ( req . user . role , req . resource . role ) < 0 ) return next ( new HttpError ( 403 , ` role ' ${ req . resource . role } ' is required but user has only ' ${ req . user . role } ' ` ) ) ;
2020-02-13 22:06:54 -08:00
2021-07-15 09:50:11 -07:00
const [ error ] = await safe ( users . sendInvite ( req . resource , { invitor : req . user } ) ) ;
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2016-01-18 15:37:03 +01:00
2021-07-15 09:50:11 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
2016-01-18 15:37:03 +01:00
}
2016-02-09 15:47:02 -08:00
2021-06-28 15:15:28 -07:00
async function setGroups ( req , res , next ) {
2016-02-09 15:47:02 -08:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-02-13 20:45:00 -08:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2016-02-09 15:47:02 -08:00
if ( ! Array . isArray ( req . body . groupIds ) ) return next ( new HttpError ( 400 , 'API call requires a groups array.' ) ) ;
2020-02-21 12:17:06 -08:00
if ( users . compareRoles ( req . user . role , req . resource . role ) < 0 ) return next ( new HttpError ( 403 , ` role ' ${ req . resource . role } ' is required but user has only ' ${ req . user . role } ' ` ) ) ;
2016-02-09 15:47:02 -08:00
2021-07-15 09:50:11 -07:00
const [ error ] = await safe ( groups . setMembership ( req . resource . id , req . body . groupIds ) ) ;
2021-06-28 15:15:28 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2016-02-09 15:47:02 -08:00
2021-06-28 15:15:28 -07:00
next ( new HttpSuccess ( 204 ) ) ;
2016-02-09 15:47:02 -08:00
}
2018-06-28 16:48:04 -07:00
2021-07-15 09:50:11 -07:00
async function setPassword ( req , res , next ) {
2018-08-31 14:30:00 -07:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2020-02-13 20:45:00 -08:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2018-08-31 14:30:00 -07:00
if ( typeof req . body . password !== 'string' ) return next ( new HttpError ( 400 , 'password must be a string' ) ) ;
2020-02-21 12:17:06 -08:00
if ( users . compareRoles ( req . user . role , req . resource . role ) < 0 ) return next ( new HttpError ( 403 , ` role ' ${ req . resource . role } ' is required but user has only ' ${ req . user . role } ' ` ) ) ;
2018-08-31 14:30:00 -07:00
2021-07-15 09:50:11 -07:00
const [ error ] = await safe ( users . setPassword ( req . resource , req . body . password , auditSource . fromRequest ( req ) ) ) ;
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2018-08-31 14:30:00 -07:00
2021-07-15 09:50:11 -07:00
next ( new HttpSuccess ( 204 ) ) ;
2018-08-31 14:30:00 -07:00
}
2020-07-10 07:23:33 -07:00
2021-01-15 14:28:41 +01:00
// This route transfers ownership from token user to user specified in path param
2021-07-15 09:50:11 -07:00
async function makeOwner ( req , res , next ) {
2021-01-15 14:28:41 +01:00
assert . strictEqual ( typeof req . resource , 'object' ) ;
2021-07-15 09:50:11 -07:00
// first make new one owner, then demote current one
let [ error ] = await safe ( users . update ( req . resource , { role : users . ROLE _OWNER } , auditSource . fromRequest ( req ) ) ) ;
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2021-01-15 14:28:41 +01:00
2021-07-15 09:50:11 -07:00
[ error ] = await safe ( users . update ( req . user , { role : users . ROLE _USER } , auditSource . fromRequest ( req ) ) ) ;
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2021-01-15 14:28:41 +01:00
2021-07-15 09:50:11 -07:00
next ( new HttpSuccess ( 204 ) ) ;
2021-01-15 14:28:41 +01:00
}