mirror of
https://github.com/strapi/strapi.git
synced 2025-11-01 18:33:55 +00:00
Init admin reset
Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com>
This commit is contained in:
parent
5cc40a350f
commit
c81af9a1d0
@ -20,7 +20,7 @@
|
||||
"index.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"commander": "^2.20.0",
|
||||
"commander": "6.1.0",
|
||||
"strapi-generate-new": "3.2.3"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@ -744,4 +744,90 @@ describe('User', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resetPasswordByEmail', () => {
|
||||
test('Throws on missing user', async () => {
|
||||
const email = 'email@email.fr';
|
||||
const password = 'invalidpass';
|
||||
|
||||
const findOne = jest.fn(() => {
|
||||
return null;
|
||||
});
|
||||
|
||||
global.strapi = {
|
||||
query() {
|
||||
return {
|
||||
findOne,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
expect.hasAssertions();
|
||||
|
||||
await userService.resetPasswordByEmail(email, password).catch(error => {
|
||||
expect(findOne).toHaveBeenCalledWith({ email }, undefined);
|
||||
expect(error).toEqual(new Error(`User not found for email: ${email}`));
|
||||
});
|
||||
});
|
||||
|
||||
test.each(['abc', 'Abcd', 'Abcdefgh', 'Abcd123'])(
|
||||
'Throws on invalid password',
|
||||
async password => {
|
||||
const email = 'email@email.fr';
|
||||
|
||||
const findOne = jest.fn(() => ({ id: 1 }));
|
||||
|
||||
global.strapi = {
|
||||
query() {
|
||||
return {
|
||||
findOne,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
expect.hasAssertions();
|
||||
|
||||
await userService.resetPasswordByEmail(email, password).catch(error => {
|
||||
expect(findOne).toHaveBeenCalledWith({ email }, undefined);
|
||||
expect(error).toEqual(
|
||||
new Error(
|
||||
'Invalid password. Expected a minimum of 8 characters with at least one number and one uppercase letter'
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('Call the update function with the expected params', async () => {
|
||||
const email = 'email@email.fr';
|
||||
const password = 'Testing1234';
|
||||
const hash = 'hash';
|
||||
const userId = 1;
|
||||
|
||||
const findOne = jest.fn(() => ({ id: userId }));
|
||||
const update = jest.fn();
|
||||
const hashPassword = jest.fn(() => hash);
|
||||
|
||||
global.strapi = {
|
||||
query() {
|
||||
return {
|
||||
findOne,
|
||||
update,
|
||||
};
|
||||
},
|
||||
admin: {
|
||||
services: {
|
||||
auth: {
|
||||
hashPassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await userService.resetPasswordByEmail(email, password);
|
||||
expect(findOne).toHaveBeenCalledWith({ email }, undefined);
|
||||
expect(update).toHaveBeenCalledWith({ id: userId }, { password: hash });
|
||||
expect(hashPassword).toHaveBeenCalledWith(password);
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ const _ = require('lodash');
|
||||
const { stringIncludes } = require('strapi-utils');
|
||||
const { createUser, hasSuperAdminRole } = require('../domain/user');
|
||||
const { SUPER_ADMIN_CODE } = require('./constants');
|
||||
const { password: passwordValidator } = require('../validation/common-validators');
|
||||
|
||||
const sanitizeUserRoles = role => _.pick(role, ['id', 'name', 'description', 'code']);
|
||||
|
||||
@ -43,7 +44,7 @@ const create = async attributes => {
|
||||
|
||||
/**
|
||||
* Update a user in database
|
||||
* @param params query params to find the user to update
|
||||
* @param id query params to find the user to update
|
||||
* @param attributes A partial user object
|
||||
* @returns {Promise<user>}
|
||||
*/
|
||||
@ -89,6 +90,31 @@ const updateById = async (id, attributes) => {
|
||||
return strapi.query('user', 'admin').update({ id }, attributes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset a user password by email. (Used in admin:reset CLI)
|
||||
* @param {string} email - user email
|
||||
* @param {string} password - new password
|
||||
*/
|
||||
const resetPasswordByEmail = async (email, password) => {
|
||||
const user = await findOne({ email });
|
||||
|
||||
if (!user) {
|
||||
throw new Error(`User not found for email: ${email}`);
|
||||
}
|
||||
|
||||
try {
|
||||
await passwordValidator.validate(password);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
'Invalid password. Expected a minimum of 8 characters with at least one number and one uppercase letter'
|
||||
);
|
||||
}
|
||||
|
||||
const { id: userId } = user;
|
||||
|
||||
await updateById(userId, { password });
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a user is the last super admin
|
||||
* @param {int|string} userId user's id to look for
|
||||
@ -320,4 +346,5 @@ module.exports = {
|
||||
assignARoleToAll,
|
||||
displayWarningIfUsersDontHaveRole,
|
||||
migrateUsers,
|
||||
resetPasswordByEmail,
|
||||
};
|
||||
|
||||
@ -4,17 +4,12 @@
|
||||
const _ = require('lodash');
|
||||
const resolveCwd = require('resolve-cwd');
|
||||
const { yellow } = require('chalk');
|
||||
const program = require('commander');
|
||||
const { Command } = require('commander');
|
||||
const program = new Command();
|
||||
|
||||
const packageJSON = require('../package.json');
|
||||
|
||||
// Allow us to display `help()`, but omit the wildcard (`*`) command.
|
||||
program.Command.prototype.usageMinusWildcard = program.usageMinusWildcard = () => {
|
||||
program.commands = _.reject(program.commands, {
|
||||
_name: '*',
|
||||
});
|
||||
program.help();
|
||||
};
|
||||
program.storeOptionsAsProperties(false).passCommandToAction(false);
|
||||
|
||||
const checkCwdIsStrapiApp = name => {
|
||||
let logErrorAndExit = () => {
|
||||
@ -72,20 +67,15 @@ const getLocalScript = name => (...args) => {
|
||||
|
||||
program.allowUnknownOption(true);
|
||||
|
||||
// Expose version.
|
||||
program.version(packageJSON.version, '-v, --version');
|
||||
|
||||
// Make `-v` option case-insensitive.
|
||||
process.argv = _.map(process.argv, arg => {
|
||||
return arg === '-V' ? '-v' : arg;
|
||||
});
|
||||
program.option('-v, --version', 'output the version number');
|
||||
|
||||
// `$ strapi version` (--version synonym)
|
||||
program
|
||||
.command('version')
|
||||
.description('output your version of Strapi')
|
||||
.action(() => {
|
||||
console.log(packageJSON.version);
|
||||
process.stdout.write(packageJSON.version + '\n');
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// `$ strapi console`
|
||||
@ -126,7 +116,7 @@ program
|
||||
.command('develop')
|
||||
.alias('dev')
|
||||
.option('--no-build', 'Disable build', false)
|
||||
.option('--watch-admin', 'Enable watch', true)
|
||||
.option('--watch-admin', 'Enable watch', false)
|
||||
.option('--browser <name>', 'Open the browser', true)
|
||||
.description('Start your Strapi application in development mode')
|
||||
.action(getLocalScript('develop'));
|
||||
@ -221,38 +211,25 @@ program
|
||||
program
|
||||
.command('configuration:dump')
|
||||
.alias('config:dump')
|
||||
.description('Dump configurations of your application')
|
||||
.option('-f, --file <file>', 'Output file, default output is stdout')
|
||||
.action(getLocalScript('configurationDump'));
|
||||
|
||||
program
|
||||
.command('configuration:restore')
|
||||
.alias('config:restore')
|
||||
.description('Restore configurations of your application')
|
||||
.option('-f, --file <file>', 'Input file, default input is stdin')
|
||||
.option('-s, --strategy <strategy>', 'Strategy name, one of: "replace", "merge", "keep"')
|
||||
.action(getLocalScript('configurationRestore'));
|
||||
|
||||
/**
|
||||
* Normalize help argument
|
||||
*/
|
||||
|
||||
// `$ strapi help` (--help synonym)
|
||||
// Admin
|
||||
program
|
||||
.command('help')
|
||||
.description('output the help')
|
||||
.action(program.usageMinusWildcard);
|
||||
.command('admin:reset-password')
|
||||
.alias('admin:reset')
|
||||
.description('Reset an admin user password')
|
||||
.option('-e, --email <email>', 'The user email')
|
||||
.option('-p, --password <password>', 'New password for the user')
|
||||
.action(getLocalScript('admin-reset'));
|
||||
|
||||
// `$ strapi <unrecognized_cmd>`
|
||||
// Mask the '*' in `help`.
|
||||
program.command('*').action(program.usageMinusWildcard);
|
||||
|
||||
// Don't balk at unknown options.
|
||||
|
||||
/**
|
||||
* `$ strapi`
|
||||
*/
|
||||
|
||||
program.parse(process.argv);
|
||||
const NO_COMMAND_SPECIFIED = program.args.length === 0;
|
||||
if (NO_COMMAND_SPECIFIED) {
|
||||
program.usageMinusWildcard();
|
||||
}
|
||||
program.parseAsync(process.argv);
|
||||
|
||||
51
packages/strapi/lib/commands/admin-reset.js
Normal file
51
packages/strapi/lib/commands/admin-reset.js
Normal file
@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const inquirer = require('inquirer');
|
||||
const strapi = require('../index');
|
||||
|
||||
const promptQuestions = [
|
||||
{ type: 'input', name: 'email', message: 'User email?' },
|
||||
{ type: 'password', name: 'password', message: 'New password?' },
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: "Do you really want to reset this user's password?",
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Reset user's password
|
||||
* @param {Object} cmdOptions - command options
|
||||
* @param {string} cmdOptions.email - user's email
|
||||
* @param {string} cmdOptions.password - user's new password
|
||||
*/
|
||||
module.exports = async function(cmdOptions) {
|
||||
const { email, password } = cmdOptions;
|
||||
|
||||
if (_.isEmpty(email) && _.isEmpty(password) && process.stdin.isTTY) {
|
||||
const inquiry = await inquirer.prompt(promptQuestions);
|
||||
|
||||
if (!inquiry.confirm) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
return changePassword(inquiry);
|
||||
}
|
||||
|
||||
if (_.isEmpty(email) || _.isEmpty(password)) {
|
||||
console.error('Missing required options `email` or `password`');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return changePassword({ email, password });
|
||||
};
|
||||
|
||||
async function changePassword({ email, password }) {
|
||||
const app = await strapi().load();
|
||||
|
||||
await app.admin.services.user.resetPasswordByEmail(email, password);
|
||||
|
||||
console.log(`Successfully reset user's password`);
|
||||
process.exit(0);
|
||||
}
|
||||
@ -19,7 +19,7 @@
|
||||
"chokidar": "3.3.1",
|
||||
"ci-info": "2.0.0",
|
||||
"cli-table3": "^0.6.0",
|
||||
"commander": "^2.20.0",
|
||||
"commander": "6.1.0",
|
||||
"cross-spawn": "^6.0.5",
|
||||
"debug": "^4.1.1",
|
||||
"delegates": "^1.0.0",
|
||||
|
||||
@ -5651,6 +5651,11 @@ commander@2.3.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873"
|
||||
integrity sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=
|
||||
|
||||
commander@6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.1.0.tgz#f8d722b78103141006b66f4c7ba1e97315ba75bc"
|
||||
integrity sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==
|
||||
|
||||
commander@^2.19.0, commander@^2.20.0, commander@^2.20.3:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user