Commit 432c892f authored by Aaron Wells's avatar Aaron Wells
Browse files

Command-line utility for resetting user passwords

Bug 1396564

Change-Id: Iac269d93d37add7053c7993dfcbb412c01ddf6d3
parent 73249481
<?php
/**
*
* @package mahara
* @subpackage core
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*
*/
define('INTERNAL', 1);
define('CLI', 1);
require(dirname(dirname(dirname(__FILE__))) . '/init.php');
require_once(get_config('docroot') . 'auth/lib.php');
require(get_config('libroot') . 'cli.php');
$cli = get_cli();
$options = array();
$options['username'] = (object) array(
'shortoptions' => array('u'),
'description' => get_string('username'),
'required' => true,
'examplevalue' => 'user1',
);
$options['password'] = (object) array(
'shortoptions' => array('p'),
'description' => get_string('cli_pwreset_password', 'admin'),
'required' => false,
'defaultvalue' => false,
'examplevalue' => 'Eepha8eeBa',
);
$options['makeinternal'] = (object) array(
'shortoptions' => array('i'),
'description' => get_string('cli_pwreset_makeinternal', 'admin'),
'required' => false,
'defaultvalue' => false,
);
define('CLI_PWRESET_FORCEPASSWORDCHANGE_DEFAULT', -1);
$options['forcepasswordchange'] = (object) array(
'shortoptions' => array('f'),
'description' => get_string('cli_pwreset_forcepasswordchange', 'admin'),
'required' => false,
'defaultvalue' => CLI_PWRESET_FORCEPASSWORDCHANGE_DEFAULT,
);
$settings = (object) array(
'info' => get_string('cli_pwreset_info', 'admin'),
'options' => $options,
);
$cli->setup($settings);
// Retrieve & validate the username
$username = $cli->get_cli_param('username');
$user = get_record('usr', 'username', $username);
if (!$user) {
$cli->cli_exit(get_string('cli_pwreset_nosuchuser', 'admin', $username), true);
}
// Retrieve or prompt for password
// (No validation. This is an admin tool!)
$password = $cli->get_cli_param('password');
if (!$password) {
$password1 = $cli->cli_prompt(get_string('cli_pwreset_prompt1', 'admin'), true);
$password2 = $cli->cli_prompt(get_string('cli_pwreset_prompt2', 'admin'), true);
if ($password1 === $password2) {
$password = $password1;
}
else {
$cli->cli_exit(get_string('cli_pwreset_typo', 'admin'));
}
}
$user->password = $password;
$makeinternal = $cli->get_cli_param_boolean('makeinternal');
if ($makeinternal) {
// Change them to the "internal" auth method for "No institution".
// This one should be permanent because it's the auth method for the "root" user.
$internalauth = get_field('auth_instance', 'id', 'institution', 'mahara', 'authname', 'internal');
if (!$internalauth) {
// If there is no such auth for some reason, then quit.
$cli->cli_exit(get_string('cli_pwreset_nointernalauth'), true);
}
set_field('usr', 'authinstance', $internalauth, 'id', $user->id);
$user->authinstance = $internalauth;
$cli->cli_print(get_string('cli_pwreset_authupdated', 'admin'));
}
// Determine whether or not to reset the user's password.
if ($cli->get_cli_param('forcepasswordchange') === CLI_PWRESET_FORCEPASSWORDCHANGE_DEFAULT) {
// The default behavior, is that we force a reset if they provided the password via the --password flag
$forcepasswordchange = ($cli->get_cli_param('password') !== false);
}
else {
// If they specified a forcepasswordchange param, we respect that
$forcepasswordchange = $cli->get_cli_param_boolean('forcepasswordchange');
}
// Attempt to reset the password.
$success = reset_password($user);
if ($success) {
$exitstring = get_string('cli_pwreset_success', 'admin', $username);
if ($forcepasswordchange) {
set_field('usr', 'passwordchange', 1, 'username', $username);
$exitstring .= "\n" . get_string('cli_pwreset_success_forcepasswordchange', 'admin');
}
$cli->cli_exit($exitstring);
}
else {
// If it failed because their auth instance doesn't allow password resets,
// then suggest the -i option.
$userobj = new User();
$userobj->find_by_id($user->id);
$authobj = AuthFactory::create($user->authinstance);
if (!method_exists($authobj, 'change_password')) {
$cli->cli_exit(get_string('cli_pwreset_notsupported', 'admin', $username), true);
}
else {
$cli->cli_exit(get_string('cli_pwreset_failure', 'admin', $username), true);
}
}
......@@ -2036,18 +2036,29 @@ function auth_get_random_salt() {
return substr(md5(rand(1000000, 9999999)), 2, 8);
}
// Add salt and encrypt the pw for a user, if their auth instance allows for it
//
/**
* Add salt and encrypt the pw for a user, if their auth instance allows for it
*
* @param object $user A user record.
* @param string $resetpasswordchange (default true) Whether to force the user to reset their password on their next login
* @param string $quickhash (default false) Whether to use a quick hashing method instead of the standard
* @return boolean Whether or not we were able to reset the password
*/
function reset_password($user, $resetpasswordchange=true, $quickhash=false) {
$userobj = new User();
$userobj->find_by_id($user->id);
$authobj = AuthFactory::create($user->authinstance);
if (isset($user->password) && $user->password != '' && method_exists($authobj, 'change_password')) {
$authobj->change_password($userobj, $user->password, $resetpasswordchange, $quickhash);
return (boolean) $authobj->change_password($userobj, $user->password, $resetpasswordchange, $quickhash);
}
else {
// They're not using an auth method that allows us to reset the password.
// Just update the password placeholder in the database.
$userobj->password = '';
$userobj->salt = auth_get_random_salt();
$userobj->commit();
return false;
}
}
......
......@@ -1182,3 +1182,19 @@ $string['exporttoqueuedescription2'] = 'Let the export queue handle the exportin
$string['validating'] = 'Validating data...';
$string['checkingupdates'] = 'Checking updated data...';
$string['committingchanges'] = 'Saving changes...';
// Password reset script
$string['cli_pwreset_authupdated'] = 'Auth method updated to "internal".';
$string['cli_pwreset_failure'] = 'ERROR: Unable to successfully reset the password for "%s".';
$string['cli_pwreset_forcepasswordchange'] = 'Force password change on next login. (Default "true" if you use the "--password" option; "false" otherwise.)';
$string['cli_pwreset_info'] = 'This command-line PHP script allows you to reset a user\'s password. This will only work for users whose auth method allows password resets (e.g. "internal").' ;
$string['cli_pwreset_makeinternal'] = 'Change the user\'s auth method to "internal". (Default "false")';
$string['cli_pwreset_nointernalauth'] = 'ERROR: Couldn\'t find default "internal" auth method.';
$string['cli_pwreset_nosuchuser'] = 'ERROR: There is no user with username "%s" in the database.';
$string['cli_pwreset_notsupported'] = 'ERROR: User "%s" has an auth method that doesn\'t support password resets. Use the "-i=true" option if you want to change them to "internal" auth.';
$string['cli_pwreset_password'] = 'The new password. (If this parameter is not supplied, the script will prompt you for a password.)';
$string['cli_pwreset_prompt1'] = 'Enter new password';
$string['cli_pwreset_prompt2'] = 'Retype new password';
$string['cli_pwreset_success'] = 'Successfully reset password for user "%s".';
$string['cli_pwreset_success_forcepasswordchange'] = 'The user will be forced to reset their password at their next login.';
$string['cli_pwreset_typo'] = 'Sorry, passwords do not match';
......@@ -1184,3 +1184,6 @@ $string['vspace'] = 'Vertical space';
$string['hspace'] = 'Horizontal space';
$string['border'] = 'Border width';
$string['alignment'] = 'Alignment';
// Miscellaneous (please keep these alphabetized)
$string['cli_incorrect_value'] = 'Incorrect value, please retry.';
......@@ -5,8 +5,10 @@
* @package mahara
* @subpackage lib
* @author Andrew Nicols
* @author Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
* @copyright 2009 Petr Skoda (http://skodak.org)
*/
/**
......@@ -307,6 +309,24 @@ class cli {
return $value;
}
/**
* Retrieve the value of a boolean parameter. Essentially the same as get_cli_param(),
* except that the string "false" will be interpreted as boolean false. @todo This is
* basically a workaround until we can implement a proper type system on CLI params.
*
* @param string $name Name of the param
* @return boolean
*/
public function get_cli_param_boolean($name) {
$value = $this->get_cli_param($name);
if (strtolower($value) == 'false') {
return false;
}
else {
return (boolean) $value;
}
}
/**
* Retrieve all data supplied on the command line which was not
* specified as an argument
......@@ -455,6 +475,47 @@ class cli {
$this->cli_exit(null, $exitcode);
}
/**
* Get input from user
* (This method adapted from Moodle 2.8's "cli_input()" method)
*
* @param string $prompt text prompt, should include possible options
* @param bool $silent Whether to attempt to prevent the user's input from echoing on the CLI
* @param string $default default value when enter pressed
* @param array $options list of allowed options, empty means any text
* @param bool $casesensitive true if options are case sensitive
* @return string entered text
*/
function cli_prompt($prompt, $silent = false, $default='', array $options=null, $casesensitiveoptions=false) {
print($prompt . ': ');
if ($silent) {
// This won't work on Windows, and it will cause some display issues if the user
// terminates the program before entering something. But it's the best we can
// do with PHP.
@exec('stty -echo');
}
$input = fread(STDIN, 2048);
if ($silent) {
@exec('stty echo');
}
print("\n");
$input = trim($input);
if ($input === '') {
$input = $default;
}
if ($options) {
if (!$casesensitiveoptions) {
$input = strtolower($input);
}
if (!in_array($input, $options)) {
$this->cli_print(get_string('cli_incorrect_value') . "\n");
return $this->cli_prompt($prompt, $default, $options, $casesensitiveoptions);
}
}
return $input;
}
}
/**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment