446 lines
13 KiB
JavaScript
Raw Normal View History

2017-11-14 11:49:19 +01:00
'use strict';
/**
* Auth.js controller
*
* @description: A set of functions called "actions" for managing `Auth`.
*/
/* eslint-disable no-useless-escape */
2017-11-16 18:00:15 +01:00
const crypto = require('crypto');
const _ = require('lodash');
const utils = require('@strapi/utils');
2021-07-08 18:15:32 +02:00
const { getService } = require('../utils');
2021-10-20 17:30:05 +02:00
const {
validateCallbackBody,
validateRegisterBody,
validateSendEmailConfirmationBody,
} = require('./validation/auth');
2019-01-18 16:08:15 +01:00
2022-03-21 11:04:54 +01:00
const { getAbsoluteAdminUrl, getAbsoluteServerUrl, sanitize } = utils;
const { ApplicationError, ValidationError } = utils.errors;
2019-01-18 16:08:15 +01:00
const emailRegExp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
2017-11-16 14:12:03 +01:00
const sanitizeUser = (user, ctx) => {
const { auth } = ctx.state;
const userSchema = strapi.getModel('plugin::users-permissions.user');
return sanitize.contentAPI.output(user, userSchema, { auth });
};
2017-11-14 11:49:19 +01:00
module.exports = {
async callback(ctx) {
2017-11-14 11:49:19 +01:00
const provider = ctx.params.provider || 'local';
const params = ctx.request.body;
2022-02-23 22:51:59 +08:00
const store = strapi.store({ type: 'plugin', name: 'users-permissions' });
2017-11-14 11:49:19 +01:00
if (provider === 'local') {
if (!_.get(await store.get({ key: 'grant' }), 'email.enabled')) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('This provider is disabled');
2018-01-18 16:01:52 +01:00
}
2021-10-20 17:30:05 +02:00
await validateCallbackBody(params);
2017-11-14 11:49:19 +01:00
const query = { provider };
2017-11-14 11:49:19 +01:00
// Check if the provided identifier is an email or not.
const isEmail = emailRegExp.test(params.identifier);
2017-11-14 11:49:19 +01:00
// Set the identifier to the appropriate query field.
if (isEmail) {
query.email = params.identifier.toLowerCase();
2017-11-14 11:49:19 +01:00
} else {
query.username = params.identifier;
}
// Check if the user exists.
2021-08-06 18:09:49 +02:00
const user = await strapi.query('plugin::users-permissions.user').findOne({ where: query });
2017-11-20 16:28:50 +01:00
if (!user) {
2021-10-20 17:30:05 +02:00
throw new ValidationError('Invalid identifier or password');
}
2017-11-20 16:28:50 +01:00
if (
_.get(await store.get({ key: 'advanced' }), 'email_confirmation') &&
user.confirmed !== true
) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('Your account email is not confirmed');
}
if (user.blocked === true) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('Your account has been blocked by an administrator');
}
// The user never authenticated with the `local` provider.
2017-11-20 16:28:50 +01:00
if (!user.password) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError(
'This user never set a local password, please login with the provider used during account creation'
);
2017-11-20 16:28:50 +01:00
}
2021-08-19 22:27:00 +02:00
const validPassword = await getService('user').validatePassword(
params.password,
user.password
);
2017-11-20 16:28:50 +01:00
if (!validPassword) {
2021-10-20 17:30:05 +02:00
throw new ValidationError('Invalid identifier or password');
2017-11-20 16:28:50 +01:00
} else {
ctx.send({
2021-08-19 22:27:00 +02:00
jwt: getService('jwt').issue({
id: user.id,
}),
user: await sanitizeUser(user, ctx),
2017-11-20 16:28:50 +01:00
});
2017-11-14 11:49:19 +01:00
}
} else {
if (!_.get(await store.get({ key: 'grant' }), [provider, 'enabled'])) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('This provider is disabled');
}
2020-01-10 07:40:17 -05:00
// Connect the user with the third-party provider.
try {
2022-02-23 22:51:59 +08:00
const user = await getService('providers').connect(provider, ctx.query);
ctx.send({
jwt: getService('jwt').issue({ id: user.id }),
user: await sanitizeUser(user, ctx),
});
} catch (error) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError(error.message);
2018-01-25 15:04:42 +01:00
}
2017-11-14 11:49:19 +01:00
}
2017-11-16 14:12:03 +01:00
},
async resetPassword(ctx) {
const params = _.assign({}, ctx.request.body, ctx.params);
2017-11-16 14:12:03 +01:00
if (
params.password &&
params.passwordConfirmation &&
params.password === params.passwordConfirmation &&
params.code
) {
const user = await strapi
2021-08-06 18:09:49 +02:00
.query('plugin::users-permissions.user')
2021-07-08 18:15:32 +02:00
.findOne({ where: { resetPasswordToken: `${params.code}` } });
2017-11-16 14:12:03 +01:00
if (!user) {
2021-10-20 17:30:05 +02:00
throw new ValidationError('Incorrect code provided');
}
2017-11-16 14:12:03 +01:00
await getService('user').edit(user.id, {
resetPasswordToken: null,
password: params.password,
});
// Update the user.
2017-12-06 11:47:39 +01:00
ctx.send({
2021-08-19 22:27:00 +02:00
jwt: getService('jwt').issue({ id: user.id }),
user: await sanitizeUser(user, ctx),
2017-12-06 11:47:39 +01:00
});
} else if (
params.password &&
params.passwordConfirmation &&
params.password !== params.passwordConfirmation
) {
2021-10-20 17:30:05 +02:00
throw new ValidationError('Passwords do not match');
} else {
2021-10-20 17:30:05 +02:00
throw new ValidationError('Incorrect params provided');
2017-12-06 11:47:39 +01:00
}
2017-11-16 18:00:15 +01:00
},
async connect(ctx, next) {
2021-09-03 11:11:37 +02:00
const grant = require('grant-koa');
const providers = await strapi
.store({ type: 'plugin', name: 'users-permissions', key: 'grant' })
.get();
const apiPrefix = strapi.config.get('api.rest.prefix');
const grantConfig = {
defaults: {
prefix: `${apiPrefix}/connect`,
},
...providers,
};
const [requestPath] = ctx.request.url.split('?');
const provider = requestPath.split('/connect/')[1].split('/')[0];
if (!_.get(grantConfig[provider], 'enabled')) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('This provider is disabled');
}
if (!strapi.config.server.url.startsWith('http')) {
strapi.log.warn(
'You are using a third party provider for login. Make sure to set an absolute url in config/server.js. More info here: https://docs.strapi.io/developer-docs/latest/plugins/users-permissions.html#setting-up-the-server-url'
);
}
// Ability to pass OAuth callback dynamically
2021-12-21 12:12:55 +01:00
grantConfig[provider].callback =
_.get(ctx, 'query.callback') ||
_.get(ctx, 'session.grant.dynamic.callback') ||
grantConfig[provider].callback;
2021-08-19 22:27:00 +02:00
grantConfig[provider].redirect_uri = getService('providers').buildRedirectUri(provider);
return grant(grantConfig)(ctx, next);
},
async forgotPassword(ctx) {
let { email } = ctx.request.body;
// Check if the provided email is valid or not.
const isEmail = emailRegExp.test(email);
if (isEmail) {
email = email.toLowerCase();
} else {
2021-10-20 17:30:05 +02:00
throw new ValidationError('Please provide a valid email address');
}
2019-10-09 17:37:16 +02:00
const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' });
2017-11-16 18:00:15 +01:00
2020-01-10 07:40:17 -05:00
// Find the user by email.
const user = await strapi
2021-08-06 18:09:49 +02:00
.query('plugin::users-permissions.user')
2021-07-08 18:15:32 +02:00
.findOne({ where: { email: email.toLowerCase() } });
2017-11-16 18:00:15 +01:00
// User not found.
if (!user) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('This email does not exist');
2017-11-16 18:00:15 +01:00
}
// User blocked
if (user.blocked) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('This user is disabled');
}
2017-11-16 18:00:15 +01:00
// Generate random token.
const resetPasswordToken = crypto.randomBytes(64).toString('hex');
const settings = await pluginStore.get({ key: 'email' }).then(storeEmail => {
try {
return storeEmail['reset_password'].options;
} catch (error) {
return {};
}
});
2019-10-09 17:37:16 +02:00
const advanced = await pluginStore.get({
key: 'advanced',
});
const userInfo = await sanitizeUser(user, ctx);
2021-07-08 18:15:32 +02:00
settings.message = await getService('users-permissions').template(settings.message, {
URL: advanced.email_reset_password,
SERVER_URL: getAbsoluteServerUrl(strapi.config),
2022-03-21 11:04:54 +01:00
ADMIN_URL: getAbsoluteAdminUrl(strapi.config),
2021-07-08 18:15:32 +02:00
USER: userInfo,
TOKEN: resetPasswordToken,
});
2017-12-04 14:11:00 +01:00
2021-07-08 18:15:32 +02:00
settings.object = await getService('users-permissions').template(settings.object, {
USER: userInfo,
});
2017-12-04 14:00:09 +01:00
try {
// Send an email to the user.
2021-08-19 22:27:00 +02:00
await strapi
.plugin('email')
.service('email')
.send({
to: user.email,
from:
settings.from.email || settings.from.name
? `${settings.from.name} <${settings.from.email}>`
: undefined,
replyTo: settings.response_email,
subject: settings.object,
text: settings.message,
html: settings.message,
});
2017-12-04 14:00:09 +01:00
} catch (err) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError(err.message);
2017-12-04 14:00:09 +01:00
}
2017-11-16 18:00:15 +01:00
// Update the user.
2021-07-08 18:15:32 +02:00
await strapi
2021-08-06 18:09:49 +02:00
.query('plugin::users-permissions.user')
2021-07-08 18:15:32 +02:00
.update({ where: { id: user.id }, data: { resetPasswordToken } });
2017-11-16 18:00:15 +01:00
ctx.send({ ok: true });
2017-11-17 11:41:23 +01:00
},
async register(ctx) {
const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' });
2018-08-23 18:28:13 +02:00
const settings = await pluginStore.get({
key: 'advanced',
2018-08-23 18:28:13 +02:00
});
2018-03-12 16:06:54 +01:00
if (!settings.allow_register) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('Register action is currently disabled');
}
const params = {
..._.omit(ctx.request.body, ['confirmed', 'confirmationToken', 'resetPasswordToken']),
provider: 'local',
};
2017-11-17 11:41:23 +01:00
2021-10-20 17:30:05 +02:00
await validateRegisterBody(params);
// Throw an error if the password selected by the user
// contains more than three times the symbol '$'.
2021-07-08 18:15:32 +02:00
if (getService('user').isHashed(params.password)) {
2021-10-20 17:30:05 +02:00
throw new ValidationError(
'Your password cannot contain more than three times the symbol `$`'
);
}
2017-11-17 11:41:23 +01:00
const role = await strapi
2021-08-06 18:09:49 +02:00
.query('plugin::users-permissions.role')
2021-07-08 18:15:32 +02:00
.findOne({ where: { type: settings.default_role } });
if (!role) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('Impossible to find the default role');
}
2017-11-17 11:41:23 +01:00
2018-07-13 20:29:41 +05:30
// Check if the provided email is valid or not.
const isEmail = emailRegExp.test(params.email);
if (isEmail) {
2018-07-13 20:29:41 +05:30
params.email = params.email.toLowerCase();
2019-11-03 10:41:21 +11:00
} else {
2021-10-20 17:30:05 +02:00
throw new ValidationError('Please provide a valid email address');
}
params.role = role.id;
2021-08-06 18:09:49 +02:00
const user = await strapi.query('plugin::users-permissions.user').findOne({
2021-07-08 18:15:32 +02:00
where: { email: params.email },
});
if (user && user.provider === params.provider) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('Email is already taken');
}
2018-09-03 14:19:51 +02:00
if (user && user.provider !== params.provider && settings.unique_email) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('Email is already taken');
}
try {
2018-08-23 18:28:13 +02:00
if (!settings.email_confirmation) {
params.confirmed = true;
}
2022-03-10 17:51:47 +01:00
const user = await getService('user').add(params);
2017-11-17 11:41:23 +01:00
const sanitizedUser = await sanitizeUser(user, ctx);
2018-08-08 17:57:02 +02:00
if (settings.email_confirmation) {
try {
await getService('user').sendConfirmationEmail(sanitizedUser);
2018-08-08 17:57:02 +02:00
} catch (err) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError(err.message);
2018-08-08 17:57:02 +02:00
}
return ctx.send({ user: sanitizedUser });
2018-08-08 17:57:02 +02:00
}
2021-08-19 22:27:00 +02:00
const jwt = getService('jwt').issue(_.pick(user, ['id']));
return ctx.send({
jwt,
user: sanitizedUser,
2017-11-20 16:28:50 +01:00
});
} catch (err) {
2021-10-20 17:30:05 +02:00
if (_.includes(err.message, 'username')) {
throw new ApplicationError('Username already taken');
2022-01-29 22:16:18 +01:00
} else if (_.includes(err.message, 'email')) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('Email already taken');
2022-01-29 22:16:18 +01:00
} else {
strapi.log.error(err);
throw new ApplicationError('An error occurred during account creation');
2021-10-20 17:30:05 +02:00
}
2017-11-17 11:41:23 +01:00
}
2018-08-08 17:57:02 +02:00
},
async emailConfirmation(ctx, next, returnUser) {
const { confirmation: confirmationToken } = ctx.query;
2018-08-08 17:57:02 +02:00
2021-08-19 22:27:00 +02:00
const userService = getService('user');
const jwtService = getService('jwt');
2018-08-08 17:57:02 +02:00
if (_.isEmpty(confirmationToken)) {
2021-10-20 17:30:05 +02:00
throw new ValidationError('token.invalid');
}
const [user] = await userService.fetchAll({ filters: { confirmationToken } });
if (!user) {
2021-10-20 17:30:05 +02:00
throw new ValidationError('token.invalid');
}
await userService.edit(user.id, { confirmed: true, confirmationToken: null });
2018-08-08 17:57:02 +02:00
if (returnUser) {
ctx.send({
jwt: jwtService.issue({ id: user.id }),
user: await sanitizeUser(user, ctx),
});
} else {
const settings = await strapi
.store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
.get();
2018-08-08 17:57:02 +02:00
ctx.redirect(settings.email_confirmation_redirection || '/');
}
},
async sendEmailConfirmation(ctx) {
const params = _.assign(ctx.request.body);
2021-10-20 17:30:05 +02:00
await validateSendEmailConfirmationBody(params);
const isEmail = emailRegExp.test(params.email);
if (isEmail) {
params.email = params.email.toLowerCase();
} else {
2021-10-20 17:30:05 +02:00
throw new ValidationError('wrong.email');
}
2021-08-06 18:09:49 +02:00
const user = await strapi.query('plugin::users-permissions.user').findOne({
2021-07-08 18:15:32 +02:00
where: { email: params.email },
});
if (!user) {
2022-03-10 17:51:47 +01:00
return ctx.send({
email: params.email,
sent: true,
2022-03-10 17:51:47 +01:00
});
}
if (user.confirmed) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('already.confirmed');
}
if (user.blocked) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError('blocked.user');
}
try {
2021-07-08 18:15:32 +02:00
await getService('user').sendConfirmationEmail(user);
ctx.send({
email: user.email,
sent: true,
});
} catch (err) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError(err.message);
}
},
2017-11-14 11:49:19 +01:00
};