2016-08-26 18:50:37 +03:00
'use strict' ;
2020-10-01 10:52:05 -04:00
const winston = require ( 'winston' ) ;
const passport = require ( 'passport' ) ;
2020-10-01 16:22:19 -04:00
const util = require ( 'util' ) ;
2016-11-15 12:45:00 +03:00
2019-12-16 08:44:55 -05:00
const user = require ( '../user' ) ;
const privileges = require ( '../privileges' ) ;
const plugins = require ( '../plugins' ) ;
2020-06-03 20:18:42 -04:00
const helpers = require ( './helpers' ) ;
2019-12-16 08:44:55 -05:00
const auth = require ( '../routes/authentication' ) ;
2021-04-02 10:40:48 -04:00
const writeRouter = require ( '../routes/write' ) ;
2018-09-04 15:43:33 +02:00
2019-12-16 08:44:55 -05:00
const controllers = {
2017-02-17 19:31:21 -07:00
helpers : require ( '../controllers/helpers' ) ,
2020-10-01 10:52:05 -04:00
authentication : require ( '../controllers/authentication' ) ,
2016-08-26 18:50:37 +03:00
} ;
2020-10-01 13:30:00 -04:00
const passportAuthenticateAsync = function ( req , res ) {
return new Promise ( ( resolve , reject ) => {
2021-04-02 10:40:48 -04:00
passport . authenticate ( 'core.api' , ( err , user ) => {
2020-10-01 13:30:00 -04:00
if ( err ) {
reject ( err ) ;
} else {
resolve ( user ) ;
2021-04-02 10:40:48 -04:00
res . on ( 'finish' , writeRouter . cleanup . bind ( null , req ) ) ;
2020-10-01 13:30:00 -04:00
}
} ) ( req , res ) ;
} ) ;
} ;
2016-10-13 11:43:39 +02:00
module . exports = function ( middleware ) {
2020-06-03 20:18:42 -04:00
async function authenticate ( req , res ) {
2020-10-01 16:22:19 -04:00
const loginAsync = util . promisify ( req . login ) . bind ( req ) ;
2018-01-31 15:20:17 -05:00
if ( req . loggedIn ) {
2020-12-07 15:44:34 -05:00
// If authenticated via cookie (express-session), protect routes with CSRF checking
2020-10-13 16:58:44 -04:00
if ( res . locals . isAPI ) {
await middleware . applyCSRFasync ( req , res ) ;
}
2020-06-03 20:18:42 -04:00
return true ;
2020-10-01 10:52:05 -04:00
} else if ( req . headers . hasOwnProperty ( 'authorization' ) ) {
2020-10-01 13:30:00 -04:00
const user = await passportAuthenticateAsync ( req , res ) ;
if ( ! user ) { return true ; }
// If the token received was a master token, a _uid must also be present for all calls
if ( user . hasOwnProperty ( 'uid' ) ) {
2020-10-01 16:22:19 -04:00
await loginAsync ( user ) ;
await controllers . authentication . onSuccessfulLogin ( req , user . uid ) ;
req . uid = user . uid ;
req . loggedIn = req . uid > 0 ;
return true ;
2020-10-01 13:30:00 -04:00
} else if ( user . hasOwnProperty ( 'master' ) && user . master === true ) {
if ( req . body . hasOwnProperty ( '_uid' ) || req . query . hasOwnProperty ( '_uid' ) ) {
user . uid = req . body . _uid || req . query . _uid ;
delete user . master ;
2020-10-01 10:52:05 -04:00
2020-10-01 16:22:19 -04:00
await loginAsync ( user ) ;
await controllers . authentication . onSuccessfulLogin ( req , user . uid ) ;
req . uid = user . uid ;
req . loggedIn = req . uid > 0 ;
return true ;
2020-10-01 10:52:05 -04:00
}
2020-10-01 16:22:19 -04:00
2021-07-09 11:40:05 -04:00
throw new Error ( '[[error:api.master-token-no-uid]]' ) ;
2020-10-01 13:30:00 -04:00
} else {
winston . warn ( '[api/authenticate] Unable to find user after verifying token' ) ;
return true ;
}
2017-05-12 17:53:23 -04:00
}
2020-04-24 11:49:56 -04:00
2020-11-20 16:06:26 -05:00
await plugins . hooks . fire ( 'response:middleware.authenticate' , {
2020-04-24 11:49:56 -04:00
req : req ,
res : res ,
next : function ( ) { } , // no-op for backwards compatibility
} ) ;
if ( ! res . headersSent ) {
2020-06-03 20:18:42 -04:00
auth . setAuthVars ( req ) ;
2017-05-12 17:53:23 -04:00
}
2020-06-03 20:18:42 -04:00
return ! res . headersSent ;
2018-12-07 13:31:31 -05:00
}
2021-09-03 12:04:16 -04:00
// TODO: Remove in v1.19.0
2021-02-04 00:01:39 -07:00
middleware . authenticate = helpers . try ( async ( req , res , next ) => {
2021-03-08 14:03:22 -05:00
winston . warn ( ` [middleware] middleware.authenticate has been deprecated, page and API routes are now automatically authenticated via setup(Page|API)Route. Use middleware.authenticateRequest (if not using route helper) and middleware.ensureLoggedIn instead. (request path: ${ req . path } ) ` ) ;
2020-06-03 20:18:42 -04:00
if ( ! await authenticate ( req , res ) ) {
return ;
}
if ( ! req . loggedIn ) {
return controllers . helpers . notAllowed ( req , res ) ;
}
next ( ) ;
} ) ;
2019-12-16 08:44:55 -05:00
2021-03-08 14:03:22 -05:00
middleware . authenticateRequest = helpers . try ( async ( req , res , next ) => {
2020-06-03 20:18:42 -04:00
if ( ! await authenticate ( req , res ) ) {
return ;
}
next ( ) ;
} ) ;
2017-05-12 17:53:23 -04:00
2021-09-03 12:04:16 -04:00
// TODO: Remove in v1.19.0
2021-03-08 14:52:49 -05:00
middleware . authenticateOrGuest = ( req , res , next ) => {
winston . warn ( ` [middleware] middleware.authenticateOrGuest has been renamed, use middleware.authenticateRequest instead. (request path: ${ req . path } ) ` ) ;
middleware . authenticateRequest ( req , res , next ) ;
} ;
2021-02-04 00:01:39 -07:00
middleware . ensureSelfOrGlobalPrivilege = helpers . try ( async ( req , res , next ) => {
2020-06-03 20:18:42 -04:00
await ensureSelfOrMethod ( user . isAdminOrGlobalMod , req , res , next ) ;
} ) ;
2017-05-12 17:53:23 -04:00
2021-02-04 00:01:39 -07:00
middleware . ensureSelfOrPrivileged = helpers . try ( async ( req , res , next ) => {
2020-06-03 20:18:42 -04:00
await ensureSelfOrMethod ( user . isPrivileged , req , res , next ) ;
} ) ;
2017-05-12 17:53:23 -04:00
2019-12-16 08:44:55 -05:00
async function ensureSelfOrMethod ( method , req , res , next ) {
2017-05-12 17:53:23 -04:00
/ *
The "self" part of this middleware hinges on you having used
middleware . exposeUid prior to invoking this middleware .
* /
2019-12-16 08:44:55 -05:00
if ( ! req . loggedIn ) {
return controllers . helpers . notAllowed ( req , res ) ;
}
if ( req . uid === parseInt ( res . locals . uid , 10 ) ) {
2020-06-03 20:18:42 -04:00
return next ( ) ;
2019-12-16 08:44:55 -05:00
}
const allowed = await method ( req . uid ) ;
if ( ! allowed ) {
return controllers . helpers . notAllowed ( req , res ) ;
}
2020-02-26 12:26:52 -05:00
return next ( ) ;
2017-05-12 17:53:23 -04:00
}
2021-02-04 00:01:39 -07:00
middleware . canViewUsers = helpers . try ( async ( req , res , next ) => {
2019-03-26 12:24:28 -04:00
if ( parseInt ( res . locals . uid , 10 ) === req . uid ) {
return next ( ) ;
}
2019-12-16 08:44:55 -05:00
const canView = await privileges . global . can ( 'view:users' , req . uid ) ;
if ( canView ) {
return next ( ) ;
}
controllers . helpers . notAllowed ( req , res ) ;
2020-06-03 20:18:42 -04:00
} ) ;
2016-08-26 18:50:37 +03:00
2021-02-04 00:01:39 -07:00
middleware . canViewGroups = helpers . try ( async ( req , res , next ) => {
2019-12-16 08:44:55 -05:00
const canView = await privileges . global . can ( 'view:groups' , req . uid ) ;
if ( canView ) {
return next ( ) ;
}
controllers . helpers . notAllowed ( req , res ) ;
2020-06-03 20:18:42 -04:00
} ) ;
2016-08-26 18:50:37 +03:00
2021-02-04 00:01:39 -07:00
middleware . checkAccountPermissions = helpers . try ( async ( req , res , next ) => {
2016-08-26 18:50:37 +03:00
// This middleware ensures that only the requested user and admins can pass
2021-03-08 14:47:33 -05:00
// This check if left behind for legacy purposes. Older plugins may call this middleware without ensureLoggedIn
2020-06-03 20:18:42 -04:00
if ( ! req . loggedIn ) {
return controllers . helpers . notAllowed ( req , res ) ;
}
2021-03-08 14:47:33 -05:00
2019-12-16 08:44:55 -05:00
const uid = await user . getUidByUserslug ( req . params . userslug ) ;
let allowed = await privileges . users . canEdit ( req . uid , uid ) ;
if ( allowed ) {
return next ( ) ;
}
if ( /user\/.+\/info$/ . test ( req . path ) ) {
allowed = await privileges . global . can ( 'view:users:info' , req . uid ) ;
}
if ( allowed ) {
return next ( ) ;
}
controllers . helpers . notAllowed ( req , res ) ;
2020-06-03 20:18:42 -04:00
} ) ;
2016-08-26 18:50:37 +03:00
2021-02-04 00:01:39 -07:00
middleware . redirectToAccountIfLoggedIn = helpers . try ( async ( req , res , next ) => {
2018-12-17 16:03:01 -05:00
if ( req . session . forceLogin || req . uid <= 0 ) {
2016-08-26 18:50:37 +03:00
return next ( ) ;
}
2019-12-16 08:44:55 -05:00
const userslug = await user . getUserField ( req . uid , 'userslug' ) ;
2021-02-03 23:59:08 -07:00
controllers . helpers . redirect ( res , ` /user/ ${ userslug } ` ) ;
2020-06-03 20:18:42 -04:00
} ) ;
2016-08-26 18:50:37 +03:00
2021-02-04 00:01:39 -07:00
middleware . redirectUidToUserslug = helpers . try ( async ( req , res , next ) => {
2019-12-16 08:44:55 -05:00
const uid = parseInt ( req . params . uid , 10 ) ;
2018-11-17 22:31:39 -05:00
if ( uid <= 0 ) {
2016-08-26 18:50:37 +03:00
return next ( ) ;
}
2019-12-16 08:44:55 -05:00
const userslug = await user . getUserField ( uid , 'userslug' ) ;
if ( ! userslug ) {
return next ( ) ;
}
2021-04-15 12:43:28 -04:00
const path = req . url . replace ( /^\/api/ , '' )
. replace ( ` /uid/ ${ uid } ` , ( ) => ` /user/ ${ userslug } ` ) ;
2019-12-16 08:44:55 -05:00
controllers . helpers . redirect ( res , path ) ;
2020-06-03 20:18:42 -04:00
} ) ;
2016-08-26 18:50:37 +03:00
2021-02-04 00:01:39 -07:00
middleware . redirectMeToUserslug = helpers . try ( async ( req , res ) => {
2019-12-16 08:44:55 -05:00
const userslug = await user . getUserField ( req . uid , 'userslug' ) ;
if ( ! userslug ) {
return controllers . helpers . notAllowed ( req , res ) ;
}
2021-02-03 23:59:08 -07:00
const path = req . path . replace ( /^(\/api)?\/me/ , ` /user/ ${ userslug } ` ) ;
2019-12-16 08:44:55 -05:00
controllers . helpers . redirect ( res , path ) ;
2020-06-03 20:18:42 -04:00
} ) ;
2017-11-16 15:38:26 -07:00
2016-10-13 11:43:39 +02:00
middleware . requireUser = function ( req , res , next ) {
2018-01-31 15:20:17 -05:00
if ( req . loggedIn ) {
2016-08-26 18:50:37 +03:00
return next ( ) ;
}
2017-02-18 12:30:49 -07:00
res . status ( 403 ) . render ( '403' , { title : '[[global:403.title]]' } ) ;
2016-08-26 18:50:37 +03:00
} ;
2021-01-25 16:01:10 -05:00
middleware . registrationComplete = async function registrationComplete ( req , res , next ) {
2016-08-26 18:50:37 +03:00
// If the user's session contains registration data, redirect the user to complete registration
if ( ! req . session . hasOwnProperty ( 'registration' ) ) {
2018-11-12 00:20:44 -05:00
return setImmediate ( next ) ;
2017-02-18 14:27:26 -07:00
}
2021-01-25 16:01:10 -05:00
const path = req . path . startsWith ( '/api/' ) ? req . path . replace ( '/api' , '' ) : req . path ;
const { allowed } = await plugins . hooks . fire ( 'filter:middleware.registrationComplete' , {
allowed : [ '/register/complete' ] ,
} ) ;
if ( ! allowed . includes ( path ) ) {
2018-05-02 13:02:07 -04:00
// Append user data if present
2021-07-05 16:55:12 -04:00
req . session . registration . uid = req . session . registration . uid || req . uid ;
2018-05-02 13:02:07 -04:00
2017-02-18 14:27:26 -07:00
controllers . helpers . redirect ( res , '/register/complete' ) ;
2016-08-26 18:50:37 +03:00
} else {
2018-12-17 16:03:01 -05:00
setImmediate ( next ) ;
2016-08-26 18:50:37 +03:00
}
} ;
} ;