698 lines
19 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 grant = require('grant-koa');
2019-09-12 10:50:52 +02:00
const { sanitizeEntity } = require('strapi-utils');
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,}))$/;
const formatError = error => [
{ messages: [{ id: error.id, message: error.message, field: error.field }] },
];
2017-11-16 14:12:03 +01:00
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;
const store = await strapi.store({
environment: '',
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')) {
2018-01-18 16:01:52 +01:00
return ctx.badRequest(null, 'This provider is disabled.');
}
2017-11-14 11:49:19 +01:00
// The identifier is required.
if (!params.identifier) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.email.provide',
message: 'Please provide your username or your e-mail.',
})
);
2017-11-14 11:49:19 +01:00
}
// The password is required.
if (!params.password) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.password.provide',
message: 'Please provide your password.',
})
);
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.
const user = await strapi.query('user', 'users-permissions').findOne(query);
2017-11-20 16:28:50 +01:00
if (!user) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.invalid',
message: 'Identifier or password invalid.',
})
);
}
2017-11-20 16:28:50 +01:00
if (
_.get(await store.get({ key: 'advanced' }), 'email_confirmation') &&
user.confirmed !== true
) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.confirmed',
message: 'Your account email is not confirmed',
})
);
}
if (user.blocked === true) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.blocked',
message: '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) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.password.local',
message:
2020-01-10 07:40:17 -05:00
'This user never set a local password, please login with the provider used during account creation.',
})
);
2017-11-20 16:28:50 +01:00
}
const validPassword = strapi.plugins['users-permissions'].services.user.validatePassword(
params.password,
user.password
);
2017-11-20 16:28:50 +01:00
if (!validPassword) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.invalid',
message: 'Identifier or password invalid.',
})
);
2017-11-20 16:28:50 +01:00
} else {
ctx.send({
jwt: strapi.plugins['users-permissions'].services.jwt.issue({
id: user.id,
}),
2019-09-12 10:50:52 +02:00
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
model: strapi.query('user', 'users-permissions').model,
}),
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'])) {
return ctx.badRequest(
null,
formatError({
id: 'provider.disabled',
message: 'This provider is disabled.',
})
);
}
2020-01-10 07:40:17 -05:00
// Connect the user with the third-party provider.
2018-01-25 15:04:42 +01:00
let user, error;
try {
[user, error] = await strapi.plugins['users-permissions'].services.providers.connect(
provider,
ctx.query
);
} catch ([user, error]) {
return ctx.badRequest(null, error === 'array' ? error[0] : error);
}
2018-01-25 15:04:42 +01:00
if (!user) {
return ctx.badRequest(null, error === 'array' ? error[0] : error);
2018-01-25 15:04:42 +01:00
}
2018-01-12 15:20:13 +01:00
ctx.send({
jwt: strapi.plugins['users-permissions'].services.jwt.issue({
id: user.id,
}),
2019-09-12 10:50:52 +02:00
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
model: strapi.query('user', 'users-permissions').model,
}),
2018-01-12 15:20:13 +01:00
});
2017-11-14 11:49:19 +01:00
}
2017-11-16 14:12:03 +01:00
},
async changePassword(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
.query('user', 'users-permissions')
.findOne({ resetPasswordToken: `${params.code}` });
2017-11-16 14:12:03 +01:00
if (!user) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.code.provide',
message: 'Incorrect code provided.',
})
);
}
2017-11-16 14:12:03 +01:00
// Delete the current code
user.resetPasswordToken = null;
2017-11-16 14:12:03 +01:00
user.password = await strapi.plugins['users-permissions'].services.user.hashPassword({
password: params.password,
});
2017-11-16 14:29:49 +01:00
// Update the user.
await strapi.query('user', 'users-permissions').update({ id: user.id }, user);
2017-12-06 11:47:39 +01:00
ctx.send({
jwt: strapi.plugins['users-permissions'].services.jwt.issue({
id: user.id,
}),
2019-09-12 10:50:52 +02:00
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
model: strapi.query('user', 'users-permissions').model,
}),
2017-12-06 11:47:39 +01:00
});
} else if (
params.password &&
params.passwordConfirmation &&
params.password !== params.passwordConfirmation
) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.password.matching',
message: 'Passwords do not match.',
})
);
} else {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.params.provide',
message: 'Incorrect params provided.',
})
);
2017-12-06 11:47:39 +01:00
}
2017-11-16 18:00:15 +01:00
},
async connect(ctx, next) {
const grantConfig = await strapi
.store({
environment: '',
type: 'plugin',
name: 'users-permissions',
key: 'grant',
})
.get();
const [protocol, host] = strapi.config.url.split('://');
_.defaultsDeep(grantConfig, { server: { protocol, host } });
const [requestPath] = ctx.request.url.split('?');
const provider =
process.platform === 'win32' ? requestPath.split('\\')[2] : requestPath.split('/')[2];
const config = grantConfig[provider];
if (!_.get(config, 'enabled')) {
return ctx.badRequest(null, 'This provider is disabled.');
}
// Ability to pass OAuth callback dynamically
2019-10-09 17:37:16 +02:00
grantConfig[provider].callback =
ctx.query && ctx.query.callback ? ctx.query.callback : grantConfig[provider].callback;
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 {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.email.format',
message: 'Please provide valid email address.',
})
);
}
2019-10-09 17:37:16 +02:00
const pluginStore = await strapi.store({
environment: '',
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.query('user', 'users-permissions').findOne({ email });
2017-11-16 18:00:15 +01:00
// User not found.
if (!user) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.user.not-exist',
message: 'This email does not exist.',
})
);
2017-11-16 18:00:15 +01:00
}
// Generate random token.
const resetPasswordToken = crypto.randomBytes(64).toString('hex');
2017-11-17 11:41:23 +01:00
// Set the property code.
2017-11-16 18:00:15 +01:00
user.resetPasswordToken = resetPasswordToken;
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',
});
settings.message = await strapi.plugins['users-permissions'].services.userspermissions.template(
settings.message,
{
URL: advanced.email_reset_password,
USER: _.omit(user.toJSON ? user.toJSON() : user, [
'password',
'resetPasswordToken',
'role',
'provider',
]),
TOKEN: resetPasswordToken,
}
);
2017-12-04 14:11:00 +01:00
settings.object = await strapi.plugins['users-permissions'].services.userspermissions.template(
settings.object,
{
USER: _.omit(user.toJSON ? user.toJSON() : user, [
'password',
'resetPasswordToken',
'role',
'provider',
]),
}
);
2017-12-04 14:00:09 +01:00
try {
// Send an email to the user.
2017-12-04 14:00:09 +01:00
await strapi.plugins['email'].services.email.send({
to: user.email,
from:
settings.from.email || settings.from.name
? `${settings.from.name} <${settings.from.email}>`
: undefined,
2018-01-19 13:34:55 +01:00
replyTo: settings.response_email,
2018-01-25 08:38:46 +01:00
subject: settings.object,
text: settings.message,
html: settings.message,
2017-12-04 14:00:09 +01:00
});
} catch (err) {
return ctx.badRequest(null, err);
}
2017-11-16 18:00:15 +01:00
// Update the user.
await strapi.query('user', 'users-permissions').update({ id: user.id }, user);
2017-11-16 18:00:15 +01:00
ctx.send({ ok: true });
2017-11-17 11:41:23 +01:00
},
async register(ctx) {
2018-08-23 18:28:13 +02:00
const pluginStore = await strapi.store({
environment: '',
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) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.advanced.allow_register',
message: 'Register action is currently disabled.',
})
);
}
const params = _.assign(ctx.request.body, {
provider: 'local',
});
2017-11-17 11:41:23 +01:00
// Password is required.
if (!params.password) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.password.provide',
message: 'Please provide your password.',
})
);
}
2017-11-17 11:41:23 +01:00
// Email is required.
if (!params.email) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.email.provide',
message: 'Please provide your email.',
})
);
}
// Throw an error if the password selected by the user
// contains more than two times the symbol '$'.
if (strapi.plugins['users-permissions'].services.user.isHashed(params.password)) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.password.format',
message: 'Your password cannot contain more than three times the symbol `$`.',
})
);
}
2017-11-17 11:41:23 +01:00
const role = await strapi
.query('role', 'users-permissions')
.findOne({ type: settings.default_role }, []);
if (!role) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.role.notFound',
message: '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 {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.email.format',
message: 'Please provide valid email address.',
})
);
}
params.role = role.id;
params.password = await strapi.plugins['users-permissions'].services.user.hashPassword(params);
const user = await strapi.query('user', 'users-permissions').findOne({
email: params.email,
});
if (user && user.provider === params.provider) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.email.taken',
message: 'Email is already taken.',
})
);
}
2018-09-03 14:19:51 +02:00
if (user && user.provider !== params.provider && settings.unique_email) {
return ctx.badRequest(
null,
formatError({
id: 'Auth.form.error.email.taken',
message: 'Email is already taken.',
})
);
}
try {
2018-08-23 18:28:13 +02:00
if (!settings.email_confirmation) {
params.confirmed = true;
}
const user = await strapi.query('user', 'users-permissions').create(params);
2017-11-17 11:41:23 +01:00
const jwt = strapi.plugins['users-permissions'].services.jwt.issue(
_.pick(user.toJSON ? user.toJSON() : user, ['id'])
);
2018-08-08 17:57:02 +02:00
if (settings.email_confirmation) {
const settings = await pluginStore.get({ key: 'email' }).then(storeEmail => {
try {
return storeEmail['email_confirmation'].options;
} catch (error) {
return {};
}
});
settings.message = await strapi.plugins[
'users-permissions'
].services.userspermissions.template(settings.message, {
URL: new URL('/auth/email-confirmation', strapi.config.url).toString(),
USER: _.omit(user.toJSON ? user.toJSON() : user, [
'password',
'resetPasswordToken',
'role',
'provider',
]),
CODE: jwt,
2018-08-08 17:57:02 +02:00
});
settings.object = await strapi.plugins[
'users-permissions'
].services.userspermissions.template(settings.object, {
USER: _.omit(user.toJSON ? user.toJSON() : user, [
'password',
'resetPasswordToken',
'role',
'provider',
]),
2018-08-08 17:57:02 +02:00
});
try {
// Send an email to the user.
await strapi.plugins['email'].services.email.send({
2019-02-06 21:57:50 +02:00
to: (user.toJSON ? user.toJSON() : user).email,
from:
settings.from.email && settings.from.name
? `${settings.from.name} <${settings.from.email}>`
: undefined,
2018-08-08 17:57:02 +02:00
replyTo: settings.response_email,
subject: settings.object,
text: settings.message,
html: settings.message,
2018-08-08 17:57:02 +02:00
});
} catch (err) {
return ctx.badRequest(null, err);
}
}
2017-11-20 16:28:50 +01:00
ctx.send({
jwt,
2019-09-12 10:50:52 +02:00
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
model: strapi.query('user', 'users-permissions').model,
}),
2017-11-20 16:28:50 +01:00
});
} catch (err) {
const adminError = _.includes(err.message, 'username')
? {
id: 'Auth.form.error.username.taken',
message: 'Username already taken',
}
: { id: 'Auth.form.error.email.taken', message: 'Email already taken' };
ctx.badRequest(null, formatError(adminError));
2017-11-17 11:41:23 +01:00
}
2018-08-08 17:57:02 +02:00
},
async emailConfirmation(ctx, returnUser) {
2018-08-08 17:57:02 +02:00
const params = ctx.query;
const decodedToken = await strapi.plugins['users-permissions'].services.jwt.verify(
params.confirmation
);
2018-08-08 17:57:02 +02:00
let user = await strapi.plugins['users-permissions'].services.user.edit(
{ id: decodedToken.id },
2019-06-08 18:50:07 +02:00
{ confirmed: true }
);
2018-08-08 17:57:02 +02:00
if(returnUser) {
ctx.send({
jwt: strapi.plugins['users-permissions'].services.jwt.issue({
id: user.id
}),
user: sanitizeEntity(user.toJSON ? user.toJSON() : user, {
model: strapi.query('user', 'users-permissions').model
})
});
} else {
const settings = await strapi
.store({
environment: '',
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 pluginStore = await strapi.store({
environment: '',
type: 'plugin',
name: 'users-permissions',
});
const params = _.assign(ctx.request.body);
if (!params.email) {
return ctx.badRequest('missing.email');
}
const isEmail = emailRegExp.test(params.email);
if (isEmail) {
params.email = params.email.toLowerCase();
} else {
return ctx.badRequest('wrong.email');
}
const user = await strapi.query('user', 'users-permissions').findOne({
email: params.email,
});
if (user.confirmed) {
return ctx.badRequest('already.confirmed');
}
if (user.blocked) {
return ctx.badRequest('blocked.user');
}
const jwt = strapi.plugins['users-permissions'].services.jwt.issue(
_.pick(user.toJSON ? user.toJSON() : user, ['id'])
);
const settings = await pluginStore.get({ key: 'email' }).then(storeEmail => {
try {
return storeEmail['email_confirmation'].options;
} catch (err) {
return {};
}
});
settings.message = await strapi.plugins['users-permissions'].services.userspermissions.template(
settings.message,
{
URL: new URL('/auth/email-confirmation', strapi.config.url).toString(),
USER: _.omit(user.toJSON ? user.toJSON() : user, [
'password',
'resetPasswordToken',
'role',
'provider',
]),
CODE: jwt,
}
);
settings.object = await strapi.plugins['users-permissions'].services.userspermissions.template(
settings.object,
{
USER: _.omit(user.toJSON ? user.toJSON() : user, [
'password',
'resetPasswordToken',
'role',
'provider',
]),
}
);
try {
await strapi.plugins['email'].services.email.send({
to: (user.toJSON ? user.toJSON() : 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,
});
ctx.send({
email: (user.toJSON ? user.toJSON() : user).email,
sent: true,
});
} catch (err) {
return ctx.badRequest(null, err);
}
},
2017-11-14 11:49:19 +01:00
};