diff --git a/src/tests/globalSetup.js b/src/tests/globalSetup.js index 65b279c11..de89d207d 100644 --- a/src/tests/globalSetup.js +++ b/src/tests/globalSetup.js @@ -6,7 +6,7 @@ const url = config.serverURL; const usernameField = config.user.auth.useAsUsername; const globalSetup = async () => { - global.PAYLOAD_SERVER = await server.start(); + global.PAYLOAD_SERVER = server.start(); const response = await fetch(`${url}/api/first-register`, { body: JSON.stringify({ @@ -20,6 +20,11 @@ const globalSetup = async () => { }); const data = await response.json(); + + if (!data.token) { + throw new Error('Failed to register first user'); + } + global.AUTH_TOKEN = data.token; }; diff --git a/src/users/operations/checkIfInitialized.js b/src/users/operations/checkIfInitialized.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/users/operations/index.js b/src/users/operations/index.js index 473f28cc5..a1765d390 100644 --- a/src/users/operations/index.js +++ b/src/users/operations/index.js @@ -1,17 +1,17 @@ -const checkIfInitialized = require('./checkIfInitialized'); const login = require('./login'); const refresh = require('./refresh'); const register = require('./register'); const init = require('./init'); const forgotPassword = require('./forgotPassword'); const resetPassword = require('./resetPassword'); +const registerFirstUser = require('./registerFirstUser'); module.exports = { - checkIfInitialized, login, refresh, init, register, forgotPassword, resetPassword, + registerFirstUser, }; diff --git a/src/users/operations/login.js b/src/users/operations/login.js index 0a744d350..dedc93c4e 100644 --- a/src/users/operations/login.js +++ b/src/users/operations/login.js @@ -16,7 +16,7 @@ const login = async (args) => { // 1. Execute before login hook // ///////////////////////////////////// - const beforeLoginHook = args.config && args.config.hooks && args.config.hooks.beforeLogin; + const beforeLoginHook = args.config.hooks && args.config.hooks.beforeLogin; if (typeof beforeLoginHook === 'function') { options = await beforeLoginHook(options); @@ -77,8 +77,8 @@ const login = async (args) => { // ///////////////////////////////////// return token; - } catch (err) { - throw err; + } catch (error) { + throw error; } }; diff --git a/src/users/operations/refresh.js b/src/users/operations/refresh.js index 307ccd77c..4559fba86 100644 --- a/src/users/operations/refresh.js +++ b/src/users/operations/refresh.js @@ -14,7 +14,7 @@ const refresh = async (args) => { // 1. Execute before refresh hook // ///////////////////////////////////// - const beforeRefreshHook = args.config.user && args.config.user.hooks && args.config.user.hooks.beforeRefresh; + const beforeRefreshHook = args.config.hooks && args.config.hooks.beforeRefresh; if (typeof beforeRefreshHook === 'function') { options = await beforeRefreshHook(options); @@ -24,9 +24,9 @@ const refresh = async (args) => { // 2. Perform refresh // ///////////////////////////////////// - const secret = options.config.user.auth.secretKey; + const secret = options.config.auth.secretKey; const opts = {}; - opts.expiresIn = options.config.user.auth.tokenExpiration; + opts.expiresIn = options.config.auth.tokenExpiration; const token = options.authorization.replace('JWT ', ''); jwt.verify(token, secret, {}); @@ -36,7 +36,7 @@ const refresh = async (args) => { // 3. Execute after login hook // ///////////////////////////////////// - const afterRefreshHook = args.config.user && args.config.user.hooks && args.config.user.hooks.afterRefresh; + const afterRefreshHook = args.config.hooks && args.config.hooks.afterRefresh; if (typeof afterRefreshHook === 'function') { await afterRefreshHook(options, refreshedToken); diff --git a/src/users/operations/register.js b/src/users/operations/register.js index e69de29bb..a55dc850f 100644 --- a/src/users/operations/register.js +++ b/src/users/operations/register.js @@ -0,0 +1,63 @@ + +const passport = require('passport'); + +const register = async (args) => { + try { + // Await validation here + + let options = { + Model: args.Model, + config: args.config, + api: args.api, + data: args.data, + }; + + // ///////////////////////////////////// + // 1. Execute before register hook + // ///////////////////////////////////// + + const beforeRegisterHook = args.config.hooks && args.config.hooks.beforeRegister; + + if (typeof beforeRegisterHook === 'function') { + options = await beforeRegisterHook(options); + } + + // ///////////////////////////////////// + // 2. Perform register + // ///////////////////////////////////// + + const { + Model, + config, + data, + } = options; + + const usernameField = config.auth.useAsUsername; + + let result = await Model.register(new Model({ + [usernameField]: data[usernameField], + }), data.password); + + await passport.authenticate('local'); + + // ///////////////////////////////////// + // 3. Execute after register hook + // ///////////////////////////////////// + + const afterRegisterHook = args.config.hooks && args.config.hooks.afterRegister; + + if (typeof afterRegisterHook === 'function') { + result = await afterRegisterHook(options, result); + } + + // ///////////////////////////////////// + // 4. Return user + // ///////////////////////////////////// + + return result; + } catch (error) { + throw error; + } +}; + +module.exports = register; diff --git a/src/users/operations/registerFirstUser.js b/src/users/operations/registerFirstUser.js new file mode 100644 index 000000000..9f5cc1cfc --- /dev/null +++ b/src/users/operations/registerFirstUser.js @@ -0,0 +1,63 @@ +const register = require('./register'); +const login = require('./login'); +const { Forbidden } = require('../../errors'); + +const registerFirstUser = async (args) => { + try { + const count = await args.Model.countDocuments({}); + + if (count >= 1) throw new Forbidden(); + + // Await validation here + + let options = { + Model: args.Model, + config: args.config, + api: args.api, + data: args.data, + }; + + // ///////////////////////////////////// + // 1. Execute before register first user hook + // ///////////////////////////////////// + + const beforeRegisterHook = args.config.hooks && args.config.hooks.beforeFirstRegister; + + if (typeof beforeRegisterHook === 'function') { + options = await beforeRegisterHook(options); + } + + // ///////////////////////////////////// + // 2. Perform register first user + // ///////////////////////////////////// + + let result = await register(options); + + // ///////////////////////////////////// + // 3. Execute after register hook + // ///////////////////////////////////// + + const afterRegisterHook = args.config.hooks && args.config.hooks.afterFirstRegister; + + if (typeof afterRegisterHook === 'function') { + result = await afterRegisterHook(options, result); + } + + // ///////////////////////////////////// + // 4. Return user + // ///////////////////////////////////// + + const token = await login(options); + + const results = { + ...result, + token, + }; + + return results; + } catch (error) { + throw error; + } +}; + +module.exports = registerFirstUser; diff --git a/src/users/requestHandlers/checkIfInitialized.js b/src/users/requestHandlers/checkIfInitialized.js deleted file mode 100644 index 3ae9ca52a..000000000 --- a/src/users/requestHandlers/checkIfInitialized.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Middleware to check if there are any users present in the database. - * @param req - * @param res - * @param next - * @returns {*} - */ -const checkIfInitialized = User => (req, res, next) => { - User.countDocuments({}, (err, count) => { - if (err) res.status(500).json({ error: err }); - if (count >= 1) return res.status(403).json({ initialized: true }); - return next(); - }); -}; - -module.exports = checkIfInitialized; diff --git a/src/users/requestHandlers/index.js b/src/users/requestHandlers/index.js index 9a9c5a100..4871e92ed 100644 --- a/src/users/requestHandlers/index.js +++ b/src/users/requestHandlers/index.js @@ -1,4 +1,3 @@ -const checkIfInitialized = require('./checkIfInitialized'); const login = require('./login'); const me = require('./me'); const refresh = require('./refresh'); @@ -6,14 +5,15 @@ const register = require('./register'); const init = require('./init'); const forgotPassword = require('./forgotPassword'); const resetPassword = require('./resetPassword'); +const registerFirstUser = require('./registerFirstUser'); module.exports = { - checkIfInitialized, login, me, refresh, init, register, forgotPassword, + registerFirstUser, resetPassword, }; diff --git a/src/users/requestHandlers/login.js b/src/users/requestHandlers/login.js index ceb1fecd0..13430b205 100644 --- a/src/users/requestHandlers/login.js +++ b/src/users/requestHandlers/login.js @@ -6,7 +6,7 @@ const loginHandler = (User, config) => async (req, res) => { try { const token = await login({ Model: User, - config: config.user, + config, data: req.body, api: 'REST', }); diff --git a/src/users/requestHandlers/refresh.js b/src/users/requestHandlers/refresh.js index f35591b72..36215967b 100644 --- a/src/users/requestHandlers/refresh.js +++ b/src/users/requestHandlers/refresh.js @@ -10,7 +10,7 @@ const refreshHandler = config => async (req, res) => { authorization: req.headers.authorization, }); - res.status(200).json({ + return res.status(200).json({ message: 'Token refresh successful', refreshedToken, }); diff --git a/src/users/requestHandlers/register.js b/src/users/requestHandlers/register.js index 38d73b9bf..3e6aa7956 100644 --- a/src/users/requestHandlers/register.js +++ b/src/users/requestHandlers/register.js @@ -1,32 +1,20 @@ -const passport = require('passport'); const httpStatus = require('http-status'); const formatErrorResponse = require('../../express/responses/formatError'); +const { register } = require('../operations'); -/** - * Returns User when succesfully registered - * @param req - * @param res - * @param next - * @returns {*} - */ -const register = (User, config) => async (req, res) => { +const registerHandler = (User, config) => async (req, res) => { try { - const usernameField = config.user.auth.useAsUsername; - - const user = await User.register(new User({ - [usernameField]: req.body[usernameField], - }), req.body.password); - - await passport.authenticate('local'); - - return res.status(httpStatus.CREATED).json({ - [usernameField]: user[usernameField], - role: user.role, - createdAt: user.createdAt, + const user = await register({ + data: req.body, + Model: User, + config, + api: 'REST', }); + + return res.status(201).json(user); } catch (error) { return res.status(httpStatus.UNAUTHORIZED).json(formatErrorResponse(error)); } }; -module.exports = register; +module.exports = registerHandler; diff --git a/src/users/requestHandlers/registerFirstUser.js b/src/users/requestHandlers/registerFirstUser.js new file mode 100644 index 000000000..a3fb832d1 --- /dev/null +++ b/src/users/requestHandlers/registerFirstUser.js @@ -0,0 +1,20 @@ +const httpStatus = require('http-status'); +const formatErrorResponse = require('../../express/responses/formatError'); +const { registerFirstUser } = require('../operations'); + +const registerFirstUserHandler = (User, config) => async (req, res) => { + try { + const firstUser = await registerFirstUser({ + Model: User, + config, + api: 'REST', + data: req.body, + }); + + return res.status(201).json(firstUser); + } catch (error) { + return res.status(error.status || httpStatus.INTERNAL_SERVER_ERROR).json(formatErrorResponse(error)); + } +}; + +module.exports = registerFirstUserHandler; diff --git a/src/users/routes.js b/src/users/routes.js index d36d362f3..a4e1126b7 100644 --- a/src/users/routes.js +++ b/src/users/routes.js @@ -6,7 +6,7 @@ const { refresh, me, register, - checkIfInitialized, + registerFirstUser, forgotPassword, resetPassword, } = require('./requestHandlers'); @@ -14,19 +14,17 @@ const { const router = express.Router(); const authRoutes = (User, config, email) => { - const registerHandler = register(User, config); - router .route('/init') .get(init(User)); router .route('/login') - .post(login(User, config)); + .post(login(User, config.user)); router .route('/refresh') - .post(refresh(config)); + .post(refresh(config.user)); router .route('/me') @@ -34,15 +32,15 @@ const authRoutes = (User, config, email) => { router .route(`/${config.user.slug}/register`) - .post(registerHandler); + .post(register(User, config.user)); router .route('/first-register') - .post(checkIfInitialized(User), registerHandler); + .post(registerFirstUser(User, config.user)); router .route('/forgot') - .post(forgotPassword(User, config)); + .post(forgotPassword(User, config, email)); router .route('/reset')