2018-04-26 15:54:53 -07:00
'use strict' ;
exports = module . exports = {
2021-01-06 21:57:23 -08:00
passwordAuth ,
tokenAuth ,
2018-08-03 17:21:24 -07:00
2021-01-06 21:57:23 -08:00
authorize ,
2021-09-21 10:11:27 -07:00
authorizeOperator ,
2018-04-26 15:54:53 -07:00
} ;
2022-09-23 12:57:13 +02:00
const apps = require ( '../apps.js' ) ,
tokens = require ( '../tokens.js' ) ,
2018-04-26 15:54:53 -07:00
assert = require ( 'assert' ) ,
2019-10-22 21:16:00 -07:00
BoxError = require ( '../boxerror.js' ) ,
2019-11-20 19:48:40 +01:00
externalLdap = require ( '../externalldap.js' ) ,
2018-04-26 15:54:53 -07:00
HttpError = require ( 'connect-lastmile' ) . HttpError ,
2021-06-04 09:28:40 -07:00
safe = require ( 'safetydance' ) ,
speakeasy = require ( 'speakeasy' ) ,
users = require ( '../users.js' ) ;
2018-06-15 17:31:28 -07:00
2021-07-15 09:50:11 -07:00
async function passwordAuth ( req , res , next ) {
2020-02-06 14:50:12 +01:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2018-06-15 17:31:28 -07:00
2020-02-06 14:50:12 +01:00
if ( ! req . body . username || typeof req . body . username !== 'string' ) return next ( new HttpError ( 400 , 'A username must be non-empty string' ) ) ;
if ( ! req . body . password || typeof req . body . password !== 'string' ) return next ( new HttpError ( 400 , 'A password must be non-empty string' ) ) ;
2020-12-20 14:41:16 -08:00
if ( 'totpToken' in req . body && typeof req . body . totpToken !== 'string' ) return next ( new HttpError ( 400 , 'totpToken must be a string' ) ) ;
2020-02-06 14:50:12 +01:00
2020-12-20 14:41:16 -08:00
const { username , password , totpToken } = req . body ;
2020-02-06 14:50:12 +01:00
2021-07-15 09:50:11 -07:00
const verifyFunc = username . indexOf ( '@' ) === - 1 ? users . verifyWithUsername : users . verifyWithEmail ;
2020-02-06 15:36:14 +01:00
2021-07-15 09:50:11 -07:00
let [ error , user ] = await safe ( verifyFunc ( username , password , users . AP _WEBADMIN ) ) ;
if ( error && error . reason === BoxError . NOT _FOUND ) {
2021-10-26 18:04:25 +02:00
[ error , user ] = await safe ( externalLdap . maybeCreateUser ( username . toLowerCase ( ) ) ) ;
2021-07-15 09:50:11 -07:00
if ( error ) return next ( new HttpError ( 401 , 'Unauthorized' ) ) ;
2021-10-26 18:04:25 +02:00
[ error ] = await safe ( externalLdap . verifyPassword ( user , password ) ) ;
2021-07-15 09:50:11 -07:00
if ( error ) return next ( new HttpError ( 401 , 'Unauthorized' ) ) ;
2020-02-06 15:36:14 +01:00
}
2021-07-15 09:50:11 -07:00
if ( error && error . reason === BoxError . INVALID _CREDENTIALS ) return next ( new HttpError ( 401 , 'Unauthorized' ) ) ;
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
if ( ! user ) return next ( new HttpError ( 401 , 'Unauthorized' ) ) ;
2020-02-06 15:36:14 +01:00
2021-07-15 09:50:11 -07:00
if ( ! user . ghost && ! user . appPassword && user . twoFactorAuthenticationEnabled ) {
if ( ! totpToken ) return next ( new HttpError ( 401 , 'A totpToken must be provided' ) ) ;
2018-06-15 17:31:28 -07:00
2022-08-02 14:02:35 +02:00
if ( user . source === 'ldap' ) {
const [ error ] = await safe ( externalLdap . verifyPasswordAndTotpToken ( user , password , totpToken ) ) ;
if ( error ) return next ( new HttpError ( 401 , 'Invalid totpToken' ) ) ;
} else {
const verified = speakeasy . totp . verify ( { secret : user . twoFactorAuthenticationSecret , encoding : 'base32' , token : totpToken , window : 2 } ) ;
if ( ! verified ) return next ( new HttpError ( 401 , 'Invalid totpToken' ) ) ;
}
2020-02-06 14:50:12 +01:00
}
2018-06-15 17:31:28 -07:00
2021-07-15 09:50:11 -07:00
req . user = user ;
next ( ) ;
2018-06-15 17:31:28 -07:00
}
2021-06-04 09:28:40 -07:00
async function tokenAuth ( req , res , next ) {
2022-09-23 12:57:13 +02:00
let accessToken ;
2020-02-06 14:50:12 +01:00
// this determines the priority
2022-09-23 12:57:13 +02:00
if ( req . body && req . body . access _token ) accessToken = req . body . access _token ;
if ( req . query && req . query . access _token ) accessToken = req . query . access _token ;
2020-02-06 14:50:12 +01:00
if ( req . headers && req . headers . authorization ) {
2021-06-04 09:28:40 -07:00
const parts = req . headers . authorization . split ( ' ' ) ;
2020-02-06 14:50:12 +01:00
if ( parts . length == 2 ) {
2021-06-04 09:28:40 -07:00
const [ scheme , credentials ] = parts ;
2018-06-15 17:31:28 -07:00
2022-09-23 12:57:13 +02:00
if ( /^Bearer$/i . test ( scheme ) ) accessToken = credentials ;
2020-02-06 14:50:12 +01:00
}
}
2022-09-23 12:57:13 +02:00
if ( ! accessToken ) return next ( new HttpError ( 401 , 'Token required' ) ) ;
2020-02-06 14:50:12 +01:00
2022-09-23 12:57:13 +02:00
const token = await tokens . getByAccessToken ( accessToken ) ;
if ( ! token ) return next ( new HttpError ( 401 , 'No such token' ) ) ;
2020-02-06 14:50:12 +01:00
2022-09-23 12:57:13 +02:00
const user = await users . get ( token . identifier ) ;
if ( ! user ) return next ( new HttpError ( 401 , 'User not found' ) ) ;
if ( ! user . active ) return next ( new HttpError ( 401 , 'User not active' ) ) ;
await safe ( tokens . update ( token . id , { lastUsedTime : new Date ( ) } ) ) ; // ignore any error
req . token = token ;
2021-06-04 09:28:40 -07:00
req . user = user ;
2020-02-06 14:50:12 +01:00
2021-06-04 09:28:40 -07:00
next ( ) ;
2018-06-15 17:31:28 -07:00
}
2020-02-21 12:17:06 -08:00
function authorize ( requiredRole ) {
assert . strictEqual ( typeof requiredRole , 'string' ) ;
2018-04-26 15:54:53 -07:00
2020-02-06 14:50:12 +01:00
return function ( req , res , next ) {
2020-02-06 17:29:45 +01:00
assert . strictEqual ( typeof req . user , 'object' ) ;
2022-09-24 21:27:43 +02:00
assert . strictEqual ( typeof req . token , 'object' ) ;
2018-06-17 15:25:41 -07:00
2020-02-21 12:17:06 -08:00
if ( users . compareRoles ( req . user . role , requiredRole ) < 0 ) return next ( new HttpError ( 403 , ` role ' ${ requiredRole } ' is required but user has only ' ${ req . user . role } ' ` ) ) ;
2022-09-23 12:57:13 +02:00
if ( ! tokens . hasScope ( req . token , req . method , req . path ) ) return next ( new HttpError ( 403 , 'access token does not have this scope' ) ) ;
2018-04-26 15:54:53 -07:00
2020-02-06 14:50:12 +01:00
next ( ) ;
} ;
2018-04-26 15:54:53 -07:00
}
2021-09-21 10:11:27 -07:00
async function authorizeOperator ( req , res , next ) {
assert . strictEqual ( typeof req . params . id , 'string' ) ;
assert . strictEqual ( typeof req . user , 'object' ) ;
assert . strictEqual ( typeof req . app , 'object' ) ;
2022-09-24 21:27:43 +02:00
assert . strictEqual ( typeof req . token , 'object' ) ;
2021-09-21 10:11:27 -07:00
2022-09-23 12:57:13 +02:00
if ( ! tokens . hasScope ( req . token , req . method , req . path ) ) return next ( new HttpError ( 403 , 'access token does not have this scope' ) ) ;
2021-09-21 10:11:27 -07:00
if ( apps . isOperator ( req . app , req . user ) ) return next ( ) ;
return next ( new HttpError ( 403 , 'user is not an operator' ) ) ;
}