mirror of
https://github.com/strapi/strapi.git
synced 2025-07-12 11:31:38 +00:00
604 lines
16 KiB
JavaScript
604 lines
16 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Auth.js controller
|
|
*
|
|
* @description: A set of functions called "actions" for managing `Auth`.
|
|
*/
|
|
|
|
/* eslint-disable no-useless-escape */
|
|
const crypto = require('crypto');
|
|
const _ = require('lodash');
|
|
const grant = require('grant-koa');
|
|
const { sanitizeEntity } = require('@strapi/utils');
|
|
const { getService } = require('../utils');
|
|
|
|
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 }] },
|
|
];
|
|
|
|
module.exports = {
|
|
async callback(ctx) {
|
|
const provider = ctx.params.provider || 'local';
|
|
const params = ctx.request.body;
|
|
|
|
const store = await strapi.store({
|
|
environment: '',
|
|
type: 'plugin',
|
|
name: 'users-permissions',
|
|
});
|
|
|
|
if (provider === 'local') {
|
|
if (!_.get(await store.get({ key: 'grant' }), 'email.enabled')) {
|
|
return ctx.badRequest(null, 'This provider is disabled.');
|
|
}
|
|
|
|
// 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.',
|
|
})
|
|
);
|
|
}
|
|
|
|
// The password is required.
|
|
if (!params.password) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.form.error.password.provide',
|
|
message: 'Please provide your password.',
|
|
})
|
|
);
|
|
}
|
|
|
|
const query = { provider };
|
|
|
|
// Check if the provided identifier is an email or not.
|
|
const isEmail = emailRegExp.test(params.identifier);
|
|
|
|
// Set the identifier to the appropriate query field.
|
|
if (isEmail) {
|
|
query.email = params.identifier.toLowerCase();
|
|
} else {
|
|
query.username = params.identifier;
|
|
}
|
|
|
|
// Check if the user exists.
|
|
const user = await strapi.query('plugin::users-permissions.user').findOne({ where: query });
|
|
|
|
if (!user) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.form.error.invalid',
|
|
message: 'Identifier or password invalid.',
|
|
})
|
|
);
|
|
}
|
|
|
|
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.
|
|
if (!user.password) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.form.error.password.local',
|
|
message:
|
|
'This user never set a local password, please login with the provider used during account creation.',
|
|
})
|
|
);
|
|
}
|
|
|
|
const validPassword = await getService('user').validatePassword(
|
|
params.password,
|
|
user.password
|
|
);
|
|
|
|
if (!validPassword) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.form.error.invalid',
|
|
message: 'Identifier or password invalid.',
|
|
})
|
|
);
|
|
} else {
|
|
ctx.send({
|
|
jwt: getService('jwt').issue({
|
|
id: user.id,
|
|
}),
|
|
user: sanitizeEntity(user, {
|
|
model: strapi.getModel('plugin::users-permissions.user'),
|
|
}),
|
|
});
|
|
}
|
|
} else {
|
|
if (!_.get(await store.get({ key: 'grant' }), [provider, 'enabled'])) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'provider.disabled',
|
|
message: 'This provider is disabled.',
|
|
})
|
|
);
|
|
}
|
|
|
|
// Connect the user with the third-party provider.
|
|
let user;
|
|
let error;
|
|
try {
|
|
[user, error] = await getService('providers').connect(provider, ctx.query);
|
|
} catch ([user, error]) {
|
|
return ctx.badRequest(null, error === 'array' ? error[0] : error);
|
|
}
|
|
|
|
if (!user) {
|
|
return ctx.badRequest(null, error === 'array' ? error[0] : error);
|
|
}
|
|
|
|
ctx.send({
|
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
user: sanitizeEntity(user, {
|
|
model: strapi.getModel('plugin::users-permissions.user'),
|
|
}),
|
|
});
|
|
}
|
|
},
|
|
|
|
async resetPassword(ctx) {
|
|
const params = _.assign({}, ctx.request.body, ctx.params);
|
|
|
|
if (
|
|
params.password &&
|
|
params.passwordConfirmation &&
|
|
params.password === params.passwordConfirmation &&
|
|
params.code
|
|
) {
|
|
const user = await strapi
|
|
.query('plugin::users-permissions.user')
|
|
.findOne({ where: { resetPasswordToken: `${params.code}` } });
|
|
|
|
if (!user) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.form.error.code.provide',
|
|
message: 'Incorrect code provided.',
|
|
})
|
|
);
|
|
}
|
|
|
|
const password = await getService('user').hashPassword({ password: params.password });
|
|
|
|
// Update the user.
|
|
await strapi
|
|
.query('plugin::users-permissions.user')
|
|
.update({ where: { id: user.id }, data: { resetPasswordToken: null, password } });
|
|
|
|
ctx.send({
|
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
user: sanitizeEntity(user, {
|
|
model: strapi.getModel('plugin::users-permissions.user'),
|
|
}),
|
|
});
|
|
} 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.',
|
|
})
|
|
);
|
|
}
|
|
},
|
|
|
|
async connect(ctx, next) {
|
|
const grantConfig = await strapi
|
|
.store({
|
|
environment: '',
|
|
type: 'plugin',
|
|
name: 'users-permissions',
|
|
key: 'grant',
|
|
})
|
|
.get();
|
|
|
|
const [requestPath] = ctx.request.url.split('?');
|
|
const provider = requestPath.split('/')[2];
|
|
|
|
if (!_.get(grantConfig[provider], 'enabled')) {
|
|
return ctx.badRequest(null, '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://strapi.io/documentation/developer-docs/latest/development/plugins/users-permissions.html#setting-up-the-server-url'
|
|
);
|
|
}
|
|
|
|
// Ability to pass OAuth callback dynamically
|
|
grantConfig[provider].callback = _.get(ctx, 'query.callback') || grantConfig[provider].callback;
|
|
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 {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.form.error.email.format',
|
|
message: 'Please provide a valid email address.',
|
|
})
|
|
);
|
|
}
|
|
|
|
const pluginStore = await strapi.store({
|
|
environment: '',
|
|
type: 'plugin',
|
|
name: 'users-permissions',
|
|
});
|
|
|
|
// Find the user by email.
|
|
const user = await strapi
|
|
.query('plugin::users-permissions.user')
|
|
.findOne({ where: { email: email.toLowerCase() } });
|
|
|
|
// User not found.
|
|
if (!user) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.form.error.user.not-exist',
|
|
message: 'This email does not exist.',
|
|
})
|
|
);
|
|
}
|
|
|
|
// User blocked
|
|
if (user.blocked) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.form.error.user.blocked',
|
|
message: 'This user is disabled.',
|
|
})
|
|
);
|
|
}
|
|
|
|
// 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 {};
|
|
}
|
|
});
|
|
|
|
const advanced = await pluginStore.get({
|
|
key: 'advanced',
|
|
});
|
|
|
|
const userInfo = sanitizeEntity(user, {
|
|
model: strapi.getModel('plugin::users-permissions.user'),
|
|
});
|
|
|
|
settings.message = await getService('users-permissions').template(settings.message, {
|
|
URL: advanced.email_reset_password,
|
|
USER: userInfo,
|
|
TOKEN: resetPasswordToken,
|
|
});
|
|
|
|
settings.object = await getService('users-permissions').template(settings.object, {
|
|
USER: userInfo,
|
|
});
|
|
|
|
try {
|
|
// Send an email to the user.
|
|
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,
|
|
});
|
|
} catch (err) {
|
|
return ctx.badRequest(null, err);
|
|
}
|
|
|
|
// Update the user.
|
|
await strapi
|
|
.query('plugin::users-permissions.user')
|
|
.update({ where: { id: user.id }, data: { resetPasswordToken } });
|
|
|
|
ctx.send({ ok: true });
|
|
},
|
|
|
|
async register(ctx) {
|
|
const pluginStore = await strapi.store({
|
|
environment: '',
|
|
type: 'plugin',
|
|
name: 'users-permissions',
|
|
});
|
|
|
|
const settings = await pluginStore.get({
|
|
key: 'advanced',
|
|
});
|
|
|
|
if (!settings.allow_register) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.advanced.allow_register',
|
|
message: 'Register action is currently disabled.',
|
|
})
|
|
);
|
|
}
|
|
|
|
const params = {
|
|
..._.omit(ctx.request.body, ['confirmed', 'confirmationToken', 'resetPasswordToken']),
|
|
provider: 'local',
|
|
};
|
|
|
|
// Password is required.
|
|
if (!params.password) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.form.error.password.provide',
|
|
message: 'Please provide your password.',
|
|
})
|
|
);
|
|
}
|
|
|
|
// 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 three times the symbol '$'.
|
|
if (getService('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 `$`.',
|
|
})
|
|
);
|
|
}
|
|
|
|
const role = await strapi
|
|
.query('plugin::users-permissions.role')
|
|
.findOne({ where: { type: settings.default_role } });
|
|
|
|
if (!role) {
|
|
return ctx.badRequest(
|
|
null,
|
|
formatError({
|
|
id: 'Auth.form.error.role.notFound',
|
|
message: 'Impossible to find the default role.',
|
|
})
|
|
);
|
|
}
|
|
|
|
// Check if the provided email is valid or not.
|
|
const isEmail = emailRegExp.test(params.email);
|
|
|
|
if (isEmail) {
|
|
params.email = params.email.toLowerCase();
|
|
} 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 getService('user').hashPassword(params);
|
|
|
|
const user = await strapi.query('plugin::users-permissions.user').findOne({
|
|
where: { 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.',
|
|
})
|
|
);
|
|
}
|
|
|
|
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 {
|
|
if (!settings.email_confirmation) {
|
|
params.confirmed = true;
|
|
}
|
|
|
|
const user = await strapi.query('plugin::users-permissions.user').create({ data: params });
|
|
|
|
const sanitizedUser = sanitizeEntity(user, {
|
|
model: strapi.getModel('plugin::users-permissions.user'),
|
|
});
|
|
|
|
if (settings.email_confirmation) {
|
|
try {
|
|
await getService('user').sendConfirmationEmail(user);
|
|
} catch (err) {
|
|
return ctx.badRequest(null, err);
|
|
}
|
|
|
|
return ctx.send({ user: sanitizedUser });
|
|
}
|
|
|
|
const jwt = getService('jwt').issue(_.pick(user, ['id']));
|
|
|
|
return ctx.send({
|
|
jwt,
|
|
user: sanitizedUser,
|
|
});
|
|
} 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));
|
|
}
|
|
},
|
|
|
|
async emailConfirmation(ctx, next, returnUser) {
|
|
const { confirmation: confirmationToken } = ctx.query;
|
|
|
|
const userService = getService('user');
|
|
const jwtService = getService('jwt');
|
|
|
|
if (_.isEmpty(confirmationToken)) {
|
|
return ctx.badRequest('token.invalid');
|
|
}
|
|
|
|
const user = await userService.fetch({ confirmationToken }, []);
|
|
|
|
if (!user) {
|
|
return ctx.badRequest('token.invalid');
|
|
}
|
|
|
|
await userService.edit({ id: user.id }, { confirmed: true, confirmationToken: null });
|
|
|
|
if (returnUser) {
|
|
ctx.send({
|
|
jwt: jwtService.issue({ id: user.id }),
|
|
user: sanitizeEntity(user, {
|
|
model: strapi.getModel('plugin::users-permissions.user'),
|
|
}),
|
|
});
|
|
} else {
|
|
const settings = await strapi
|
|
.store({
|
|
environment: '',
|
|
type: 'plugin',
|
|
name: 'users-permissions',
|
|
key: 'advanced',
|
|
})
|
|
.get();
|
|
|
|
ctx.redirect(settings.email_confirmation_redirection || '/');
|
|
}
|
|
},
|
|
|
|
async sendEmailConfirmation(ctx) {
|
|
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('plugin::users-permissions.user').findOne({
|
|
where: { email: params.email },
|
|
});
|
|
|
|
if (user.confirmed) {
|
|
return ctx.badRequest('already.confirmed');
|
|
}
|
|
|
|
if (user.blocked) {
|
|
return ctx.badRequest('blocked.user');
|
|
}
|
|
|
|
try {
|
|
await getService('user').sendConfirmationEmail(user);
|
|
ctx.send({
|
|
email: user.email,
|
|
sent: true,
|
|
});
|
|
} catch (err) {
|
|
return ctx.badRequest(null, err);
|
|
}
|
|
},
|
|
};
|