2017-02-18 01:56:23 -07:00
'use strict' ;
2014-03-03 13:17:10 -05:00
2016-08-26 18:50:37 +03:00
var winston = require ( 'winston' ) ;
2018-03-20 06:32:17 -06:00
var jsesc = require ( 'jsesc' ) ;
var nconf = require ( 'nconf' ) ;
var semver = require ( 'semver' ) ;
2016-08-26 18:50:37 +03:00
var user = require ( '../user' ) ;
var meta = require ( '../meta' ) ;
var plugins = require ( '../plugins' ) ;
2020-06-05 15:26:51 -04:00
var privileges = require ( '../privileges' ) ;
2020-06-03 19:07:08 -04:00
var utils = require ( '../../public/src/utils' ) ;
2018-03-20 06:32:17 -06:00
var versions = require ( '../admin/versions' ) ;
2020-06-03 20:18:42 -04:00
var helpers = require ( './helpers' ) ;
2016-08-26 18:50:37 +03:00
var controllers = {
api : require ( '../controllers/api' ) ,
2017-02-17 19:31:21 -07:00
helpers : require ( '../controllers/helpers' ) ,
2014-12-01 23:07:47 -05:00
} ;
2020-08-21 15:29:40 -04:00
const middleware = module . exports ;
middleware . buildHeader = helpers . try ( async function ( req , res , next ) {
2020-08-21 15:11:54 -04:00
res . locals . renderAdminHeader = true ;
res . locals . config = await controllers . api . loadConfig ( req ) ;
next ( ) ;
} ) ;
2020-08-21 15:29:40 -04:00
middleware . renderHeader = async ( req , res , data ) => {
2020-08-21 15:11:54 -04:00
var custom _header = {
plugins : [ ] ,
authentication : [ ] ,
} ;
res . locals . config = res . locals . config || { } ;
const results = await utils . promiseParallel ( {
userData : user . getUserFields ( req . uid , [ 'username' , 'userslug' , 'email' , 'picture' , 'email:confirmed' ] ) ,
scripts : getAdminScripts ( ) ,
2020-11-20 16:06:26 -05:00
custom _header : plugins . hooks . fire ( 'filter:admin.header.build' , custom _header ) ,
2020-08-21 15:11:54 -04:00
configs : meta . configs . list ( ) ,
latestVersion : getLatestVersion ( ) ,
privileges : privileges . admin . get ( req . uid ) ,
2020-06-03 20:18:42 -04:00
} ) ;
2015-03-06 19:38:10 -05:00
2020-08-21 15:11:54 -04:00
var userData = results . userData ;
userData . uid = req . uid ;
userData [ 'email:confirmed' ] = userData [ 'email:confirmed' ] === 1 ;
userData . privileges = results . privileges ;
var acpPath = req . path . slice ( 1 ) . split ( '/' ) ;
acpPath . forEach ( function ( path , i ) {
acpPath [ i ] = path . charAt ( 0 ) . toUpperCase ( ) + path . slice ( 1 ) ;
} ) ;
acpPath = acpPath . join ( ' > ' ) ;
var version = nconf . get ( 'version' ) ;
res . locals . config . userLang = res . locals . config . acpLang || res . locals . config . userLang ;
var templateValues = {
config : res . locals . config ,
configJSON : jsesc ( JSON . stringify ( res . locals . config ) , { isScriptContext : true } ) ,
relative _path : res . locals . config . relative _path ,
adminConfigJSON : encodeURIComponent ( JSON . stringify ( results . configs ) ) ,
user : userData ,
userJSON : jsesc ( JSON . stringify ( userData ) , { isScriptContext : true } ) ,
plugins : results . custom _header . plugins ,
authentication : results . custom _header . authentication ,
scripts : results . scripts ,
'cache-buster' : meta . config [ 'cache-buster' ] || '' ,
env : ! ! process . env . NODE _ENV ,
title : ( acpPath || 'Dashboard' ) + ' | NodeBB Admin Control Panel' ,
bodyClass : data . bodyClass ,
version : version ,
latestVersion : results . latestVersion ,
upgradeAvailable : results . latestVersion && semver . gt ( results . latestVersion , version ) ,
2020-08-21 15:42:04 -04:00
showManageMenu : results . privileges . superadmin || [ 'categories' , 'privileges' , 'users' , 'settings' ] . some ( priv => results . privileges [ ` admin: ${ priv } ` ] ) ,
2016-08-26 18:50:37 +03:00
} ;
2016-10-05 15:22:35 +03:00
2020-08-21 15:11:54 -04:00
templateValues . template = { name : res . locals . template } ;
templateValues . template [ res . locals . template ] = true ;
2020-12-21 09:59:12 -05:00
// Normally this should hook be automatically added by middleware.processRender(), but it seems to only be fired for page hooks, and not when called internally.
( { templateValues } = await plugins . hooks . fire ( 'filter:admin/header.build' , { req , res , templateData : templateValues } ) ) ;
2020-08-21 15:11:54 -04:00
return await req . app . renderAsync ( 'admin/header' , templateValues ) ;
} ;
async function getAdminScripts ( ) {
2020-11-20 16:06:26 -05:00
const scripts = await plugins . hooks . fire ( 'filter:admin.scripts.get' , [ ] ) ;
2020-08-21 15:11:54 -04:00
return scripts . map ( function ( script ) {
return { src : script } ;
} ) ;
}
async function getLatestVersion ( ) {
try {
const result = await versions . getLatestVersion ( ) ;
return result ;
} catch ( err ) {
winston . error ( '[acp] Failed to fetch latest version' + err . stack ) ;
2017-05-24 15:07:03 -04:00
}
2020-08-21 15:11:54 -04:00
return null ;
}
2016-10-05 15:22:35 +03:00
2020-08-21 15:29:40 -04:00
middleware . renderFooter = async function ( req , res , data ) {
2020-08-21 15:11:54 -04:00
return await req . app . renderAsync ( 'admin/footer' , data ) ;
} ;
2020-10-30 12:30:58 -04:00
middleware . checkPrivileges = helpers . try ( async ( req , res , next ) => {
2020-08-21 15:11:54 -04:00
// Kick out guests, obviously
2020-10-30 12:30:58 -04:00
if ( req . uid <= 0 ) {
2020-08-21 15:11:54 -04:00
return controllers . helpers . notAllowed ( req , res ) ;
2020-06-03 20:18:42 -04:00
}
2020-08-21 15:11:54 -04:00
// Otherwise, check for privilege based on page (if not in mapping, deny access)
const path = req . path . replace ( /^(\/api)?\/admin\/?/g , '' ) ;
if ( path ) {
const privilege = privileges . admin . resolve ( path ) ;
2020-10-30 17:16:40 -04:00
if ( ! await privileges . admin . can ( privilege , req . uid ) ) {
2020-06-05 15:26:51 -04:00
return controllers . helpers . notAllowed ( req , res ) ;
}
2020-08-21 15:11:54 -04:00
} else {
// If accessing /admin, check for any valid admin privs
const privilegeSet = await privileges . admin . get ( req . uid ) ;
if ( ! Object . values ( privilegeSet ) . some ( Boolean ) ) {
return controllers . helpers . notAllowed ( req , res ) ;
2020-06-05 15:26:51 -04:00
}
2020-08-21 15:11:54 -04:00
}
2020-06-05 15:26:51 -04:00
2020-12-05 09:50:45 -05:00
// If user does not have password
const hasPassword = await user . hasPassword ( req . uid ) ;
if ( ! hasPassword ) {
return next ( ) ;
}
2020-10-30 12:30:58 -04:00
// Reject if they need to re-login (due to ACP timeout), otherwise extend logout timer
const loginTime = req . session . meta ? req . session . meta . datetime : 0 ;
const adminReloginDuration = meta . config . adminReloginDuration * 60000 ;
const disabled = meta . config . adminReloginDuration === 0 ;
if ( disabled || ( loginTime && parseInt ( loginTime , 10 ) > Date . now ( ) - adminReloginDuration ) ) {
const timeLeft = parseInt ( loginTime , 10 ) - ( Date . now ( ) - adminReloginDuration ) ;
if ( req . session . meta && timeLeft < Math . min ( 300000 , adminReloginDuration ) ) {
req . session . meta . datetime += Math . min ( 300000 , adminReloginDuration ) ;
}
return next ( ) ;
}
let returnTo = req . path ;
if ( nconf . get ( 'relative_path' ) ) {
returnTo = req . path . replace ( new RegExp ( '^' + nconf . get ( 'relative_path' ) ) , '' ) ;
}
returnTo = returnTo . replace ( /^\/api/ , '' ) ;
req . session . returnTo = returnTo ;
req . session . forceLogin = 1 ;
2020-12-11 11:50:18 -05:00
await plugins . hooks . fire ( 'response:auth.relogin' , { req , res } ) ;
if ( res . headersSent ) {
return ;
}
2020-10-30 12:30:58 -04:00
if ( res . locals . isAPI ) {
2020-10-30 14:07:47 -04:00
res . status ( 401 ) . json ( { } ) ;
2020-10-30 12:30:58 -04:00
} else {
res . redirect ( nconf . get ( 'relative_path' ) + '/login?local=1' ) ;
}
} ) ;