UNPKG

@websolutespa/payload-plugin-bowl

Version:

Bowl PayloadCms plugin of the BOM Repository

301 lines (300 loc) 10.7 kB
import { deepMerge, eachDataField, HttpStatus, withCollectionHook } from '@websolutespa/payload-utils'; import { ResponseError, ResponseSuccess } from '@websolutespa/payload-utils/server'; import { addDataAndFileToRequest, AuthenticationError } from 'payload'; import { toField } from '../../mapper'; import { options } from '../../options'; import { hasRole, isAdmin, isAdminOrSelf } from '../access'; import { sendEmail } from '../api/email.service'; import { decrypt, encrypt } from '../encryption'; import { translateCollection } from '../translations'; import { mergeFields } from '../utils'; export const EndUserDefault = { access: { create: ()=>false, read: isAdminOrSelf, update: isAdminOrSelf, delete: ()=>false, unlock: isAdmin }, admin: { useAsTitle: 'email', group: options.group.users, defaultColumns: [ 'email' ] }, auth: { useAPIKey: false, lockTime: 10 * 60 * 1000, maxLoginAttempts: 5, tokenExpiration: 2 * 60 * 60, verify: false } }; /** * End User before login hook. * * @param req - Full express request. * @param user - User being logged in. * @returns The logged in user. * @throws {AuthenticationError} If the endUser does not have the role 'user' associated (this ensure endUser cannot authenticate before optin). */ const beforeLoginHook = async ({ req, user })=>{ if (!hasRole(user, options.roles.User)) { throw new AuthenticationError(); } return user; }; export const endUserForgotPost = (slug)=>({ path: '/forgot', method: 'post', handler: async (req)=>{ try { const { payload, user } = req; if (!hasRole(user, options.roles.Guest)) { throw new AuthenticationError(); } await addDataAndFileToRequest(req); const { email } = req.data; if (!email) { throw { status: 400, message: 'Bad Request: email is missing' }; } const token = await payload.forgotPassword({ collection: slug, data: { email }, disableEmail: true }); // console.log('token', token); if (!token) { // !!! failing silently, we don't want to let know if email exhist. return ResponseSuccess({ code: 'SENT' }); } const actionId = encrypt(token); const actionSlug = slug; await sendEmail(req, 'forgot', (options)=>{ const html = typeof options.html === 'string' ? options.html : ''; options.html = html.replace(/(\{(actionId|actionSlug)\})/gm, (m, g1, g2)=>g2 === 'actionId' ? actionId : actionSlug); return options; }); return ResponseSuccess({ code: 'SENT', actionId }); } catch (error) { console.error('withEndUser.endUserForgotPost.error', error); return ResponseError(error); } } }); export const endUserResetPost = (slug)=>({ path: '/reset', method: 'post', handler: async (req)=>{ try { const { payload, user } = req; if (!hasRole(user, options.roles.Guest)) { throw new AuthenticationError(); } await addDataAndFileToRequest(req); const { actionId } = req.data; if (!actionId) { throw { status: 400, message: 'Bad Request: actionId is missing' }; } const { password } = req.data; if (!password) { throw { status: 400, message: 'Bad Request: password is missing' }; } const token = decrypt(actionId); const result = await payload.resetPassword({ collection: slug, data: { password, token }, overrideAccess: true }); return ResponseSuccess(result); } catch (error) { console.error('withEndUser.endUserResetPost.error', error); return ResponseError(error); } } }); export const endUserPasswordPost = (slug)=>({ path: '/password', method: 'post', handler: async (req)=>{ try { const { payload, user } = req; if (!hasRole(user, options.roles.User)) { throw new AuthenticationError(); } await addDataAndFileToRequest(req); const { oldPassword } = req.data; if (!oldPassword) { throw { status: 400, message: 'Bad Request: oldPassword is missing' }; } const { newPassword } = req.data; if (!newPassword) { throw { status: 400, message: 'Bad Request: newPassword is missing' }; } if (!user.email) { throw { status: HttpStatus.UNPROCESSABLE_ENTITY, message: 'Unprocessable Entity: email is missing' }; } const oldPasswordResult = await payload.login({ collection: slug, data: { email: user.email, password: oldPassword } }); if (!oldPasswordResult.user || !oldPasswordResult.token) { throw { status: 401, message: 'Unauthorized' }; } const token = await payload.forgotPassword({ collection: slug, data: { email: oldPasswordResult.user.email }, disableEmail: true }); if (!token) { throw { status: 500, message: 'Cannot create reset token' }; } const result = await payload.resetPassword({ collection: slug, data: { password: newPassword, token }, overrideAccess: true }); return ResponseSuccess(result); } catch (error) { console.error('withEndUser.endUserForgotPost.error', error); return ResponseError(error); } } }); export const endUserExistPost = (slug)=>({ path: '/exist', method: 'post', handler: async (req)=>{ try { const { payload } = req; await addDataAndFileToRequest(req); const { email } = req.data; if (!email) { throw { status: 400, message: 'Bad Request: email is missing' }; } const existingEndUsers = await payload.find({ collection: slug, where: { email: { equals: email.toLowerCase() } }, overrideAccess: true }); const existingEndUser = existingEndUsers.totalDocs > 0 ? existingEndUsers.docs[0] : undefined; const exist = existingEndUser && hasRole(existingEndUser, options.roles.User); return ResponseSuccess({ exist }); } catch (error) { console.error('withEndUser.endUserExistPost.error', error); return ResponseError(error); } } }); export const withEndUser = (config)=>{ const endUserConfig = deepMerge(EndUserDefault, config); const endUserFields = endUserConfig.fields; /** * ATTENTION ! * Automatically setting defaultValue for DataField */ eachDataField(endUserFields, (field)=>{ if (field.type !== 'group' && field.required) { switch(field.type){ case 'number': field.defaultValue = 0; break; default: field.defaultValue = '-'; } } }); const defaultFields = [ toField({ type: 'withRoles', roles: options.rolesEndUser, defaultValue: options.roles.Guest }), { name: 'consentPreferences', type: 'array', label: 'Consent Preferences', fields: [ { name: 'consentPreference', label: 'Consent Preference', type: 'relationship', relationTo: options.slug.consentPreference }, { name: 'date', type: 'date', label: 'Date' } ] }, { type: 'checkbox', name: 'emailVerified' } ]; endUserConfig.fields = mergeFields(defaultFields, endUserConfig.fields, true); // !!! todo check if if (!endUserConfig.endpoints) { endUserConfig.endpoints = [ endUserForgotPost(endUserConfig.slug), endUserResetPost(endUserConfig.slug), endUserPasswordPost(endUserConfig.slug), endUserExistPost(endUserConfig.slug) ]; } withCollectionHook(endUserConfig, 'beforeLogin', beforeLoginHook); translateCollection(endUserConfig); return endUserConfig; }; //# sourceMappingURL=withEndUser.js.map