Add user provisioning.

This commit is contained in:
Andrew Hancox 2017-03-31 11:41:33 +01:00
parent 1632d8830a
commit b08d113e0c
6 changed files with 330 additions and 19 deletions

View file

@ -97,6 +97,5 @@ E.g. http://yourmoodle.com/login/index.php?enrolkey_skipsso=1
TODO: TODO:
----- -----
1. Add users provisioning. 1. Implement logout webservice to be able to call it from external application.
2. Implement logout webservice to be able to call it from external application. 2. Add a test client code to README.
3. Add a test client code to README.

107
auth.php
View file

@ -29,6 +29,7 @@ use auth_userkey\userkey_manager_interface;
require_once($CFG->libdir . "/externallib.php"); require_once($CFG->libdir . "/externallib.php");
require_once($CFG->libdir.'/authlib.php'); require_once($CFG->libdir.'/authlib.php');
require_once($CFG->dirroot . '/user/lib.php');
/** /**
* User key authentication plugin. * User key authentication plugin.
@ -58,7 +59,8 @@ class auth_plugin_userkey extends auth_plugin_base {
'iprestriction' => 0, 'iprestriction' => 0,
'redirecturl' => '', 'redirecturl' => '',
'ssourl' => '', 'ssourl' => '',
// TODO: use this field when implementing user creation. 'createuser' => 0. 'createuser' => false,
'updateuser' => false,
); );
/** /**
@ -302,6 +304,19 @@ class auth_plugin_userkey extends auth_plugin_base {
return false; return false;
} }
/**
* Check if we need to update users.
*
* @return bool
*/
protected function should_update_user() {
if (isset($this->config->updateuser) && $this->config->updateuser == true) {
return true;
}
return false;
}
/** /**
* Check if restriction by IP is enabled. * Check if restriction by IP is enabled.
* *
@ -323,10 +338,77 @@ class auth_plugin_userkey extends auth_plugin_base {
* @return object User object. * @return object User object.
*/ */
protected function create_user(array $data) { protected function create_user(array $data) {
// TODO: global $DB, $CFG;
// 1. Validate user
// 2. Create user. $user = $data;
// 3. Throw exception if something went wrong. unset($user['ip']);
$user['auth'] = 'userkey';
$user['mnethostid'] = $CFG->mnet_localhost_id;
if ($DB->record_exists('user', array('username' => $user['username'], 'mnethostid' => $CFG->mnet_localhost_id))) {
throw new invalid_parameter_exception('Username already exists: '.$user['username']);
}
if (!validate_email($user['email'])) {
throw new invalid_parameter_exception('Email address is invalid: '.$user['email']);
} else if (empty($CFG->allowaccountssameemail) &&
$DB->record_exists('user', array('email' => $user['email'], 'mnethostid' => $user['mnethostid']))) {
throw new invalid_parameter_exception('Email address already exists: '.$user['email']);
}
$userid = user_create_user($user);
return $DB->get_record('user', ['id' => $userid]);
}
/**
* Update an existing user.
*
* @param stdClass $user Existing user record.
* @param array $data Validated user data from web service.
*
* @return object User object.
*/
protected function update_user(\stdClass $user, array $data) {
global $DB, $CFG;
$userdata = $data;
unset($userdata['ip']);
$userdata['auth'] = 'userkey';
$changed = false;
foreach ($userdata as $key => $value) {
if ($user->$key != $value) {
$changed = true;
break;
}
}
if (!$changed) {
return $user;
}
if (
$user->username != $userdata['username']
&&
$DB->record_exists('user', array('username' => $userdata['username'], 'mnethostid' => $CFG->mnet_localhost_id))
) {
throw new invalid_parameter_exception('Username already exists: '.$userdata['username']);
}
if (!validate_email($userdata['email'])) {
throw new invalid_parameter_exception('Email address is invalid: '.$userdata['email']);
} else if (
empty($CFG->allowaccountssameemail)
&&
$user->email != $userdata['email']
&&
$DB->record_exists('user', array('email' => $userdata['email'], 'mnethostid' => $CFG->mnet_localhost_id))
) {
throw new invalid_parameter_exception('Email address already exists: '.$userdata['email']);
}
$userdata['id'] = $user->id;
$userdata = (object) $userdata;
user_update_user($userdata, false);
return $DB->get_record('user', ['id' => $user->id]);
} }
/** /**
@ -381,6 +463,8 @@ class auth_plugin_userkey extends auth_plugin_base {
} else { } else {
throw new invalid_parameter_exception('User is not exist'); throw new invalid_parameter_exception('User is not exist');
} }
} else if ($this->should_update_user()) {
$user = $this->update_user($user, $data);
} }
return $user; return $user;
@ -506,7 +590,18 @@ class auth_plugin_userkey extends auth_plugin_base {
); );
} }
// TODO: add more fields here when we implement user creation. $mappingfield = $this->get_mapping_field();
if ($this->should_create_user() || $this->should_update_user()) {
$parameters['firstname'] = new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user');
$parameters['lastname'] = new external_value(core_user::get_property_type('lastname'), 'The family name of the user');
if ($mappingfield != 'email') {
$parameters['email'] = new external_value(core_user::get_property_type('email'), 'A valid and unique email address');
}
if ($mappingfield != 'username') {
$parameters['username'] = new external_value(core_user::get_property_type('username'), 'A valid and unique username');
}
}
return $parameters; return $parameters;
} }

View file

@ -32,8 +32,10 @@ A user has to have provided IP to be able to use a key to login to LMS.';
$string['keylifetime'] = 'User key life time'; $string['keylifetime'] = 'User key life time';
$string['keylifetime_desc'] = 'Life time in seconds of the each user login key.'; $string['keylifetime_desc'] = 'Life time in seconds of the each user login key.';
$string['incorrectkeylifetime'] = 'User key life time should be a number'; $string['incorrectkeylifetime'] = 'User key life time should be a number';
$string['createuser'] = 'Crete user?'; $string['createuser'] = 'Create user?';
$string['createuser_desc'] = 'If enabled, a new user will be created if fail to find one in LMS.'; $string['createuser_desc'] = 'If enabled, a new user will be created if fail to find one in LMS.';
$string['updateuser'] = 'Update user?';
$string['updateuser_desc'] = 'If enabled, users will be updated with the properties supplied when the webservice is called.';
$string['redirecturl'] = 'Logout redirect URL'; $string['redirecturl'] = 'Logout redirect URL';
$string['redirecturl_desc'] = 'Optionally you can redirect users to this URL after they logged out from LMS.'; $string['redirecturl_desc'] = 'Optionally you can redirect users to this URL after they logged out from LMS.';
$string['incorrectredirecturl'] = 'You should provide valid URL'; $string['incorrectredirecturl'] = 'You should provide valid URL';
@ -43,3 +45,4 @@ $string['pluginisdisabled'] = 'The userkey authentication plugin is disabled.';
$string['ssourl'] = 'URL of SSO host'; $string['ssourl'] = 'URL of SSO host';
$string['ssourl_desc'] = 'URL of the SSO host to redirect users to. If defined users will be redirected here on login instead of the Moodle Login page'; $string['ssourl_desc'] = 'URL of the SSO host to redirect users to. If defined users will be redirected here on login instead of the Moodle Login page';
$string['redirecterrordetected'] = 'Unsupported redirect to {$a} detected, execution terminated.'; $string['redirecterrordetected'] = 'Unsupported redirect to {$a} detected, execution terminated.';
$string['noip'] = 'Unable to fetch IP address of client.';

View file

@ -63,13 +63,19 @@ $fields = get_auth_plugin('userkey')->get_allowed_mapping_fields();
<?php if (isset($err[$field])) { echo $OUTPUT->notification($err[$field], 'notifyfailure'); } ?> <?php if (isset($err[$field])) { echo $OUTPUT->notification($err[$field], 'notifyfailure'); } ?>
<?php print_string($field.'_desc', 'auth_userkey') ?></td> <?php print_string($field.'_desc', 'auth_userkey') ?></td>
</tr> </tr>
<!--UNCOMMENT FOLLOWING WHEN IMPLEMENT USER CREATION.--> <tr valign="top">
<!--<tr valign="top">--> <?php $field = 'createuser' ?>
<!--<?php $field = 'createuser' ?>--> <td align="right"><label for="<?php echo $field ?>"><?php print_string($field, 'auth_userkey') ?></label></td>
<!--<td align="right"><label for="<?php echo $field ?>"><?php print_string($field, 'auth_userkey') ?></label></td>--> <td><?php echo html_writer::select($yesno, $field, $config->$field, false) ?>
<!--<td><?php echo html_writer::select($yesno, $field, $config->$field, false) ?>--> <?php if (isset($err[$field])) { echo $OUTPUT->notification($err[$field], 'notifyfailure'); } ?>
<!--<?php if (isset($err[$field])) { echo $OUTPUT->notification($err[$field], 'notifyfailure'); } ?>--> <?php print_string($field.'_desc', 'auth_userkey')?></td>
<!--<?php print_string($field.'_desc', 'auth_userkey')?></td>--> </tr>
<!--</tr>--> <tr valign="top">
<?php $field = 'updateuser' ?>
<td align="right"><label for="<?php echo $field ?>"><?php print_string($field, 'auth_userkey') ?></label></td>
<td><?php echo html_writer::select($yesno, $field, $config->$field, false) ?>
<?php if (isset($err[$field])) { echo $OUTPUT->notification($err[$field], 'notifyfailure'); } ?>
<?php print_string($field.'_desc', 'auth_userkey')?></td>
</tr>
</table> </table>

View file

@ -277,6 +277,185 @@ class auth_plugin_userkey_testcase extends advanced_testcase {
$this->assertEquals($expected, $actual); $this->assertEquals($expected, $actual);
} }
/**
* Test that we can request a key for a new user.
*/
public function test_return_correct_login_url_and_create_new_user() {
global $CFG, $DB;
set_config('createuser', true, 'auth_userkey');
$this->auth = new auth_plugin_userkey();
$userkeymanager = new \auth_userkey\fake_userkey_manager();
$this->auth->set_userkey_manager($userkeymanager);
$user = new stdClass();
$user->username = 'username';
$user->email = 'username@test.com';
$user->firstname = 'user';
$user->lastname = 'name';
$user->ip = '192.168.1.1';
$expected = $CFG->wwwroot . '/auth/userkey/login.php?key=FaKeKeyFoRtEsTiNg';
$actual = $this->auth->get_login_url($user);
$this->assertEquals($expected, $actual);
$userrecord = $DB->get_record('user', ['username' => 'username']);
$this->assertEquals($user->email, $userrecord->email);
$this->assertEquals($user->firstname, $userrecord->firstname);
$this->assertEquals($user->lastname, $userrecord->lastname);
$this->assertEquals('userkey', $userrecord->auth);
}
/**
* Test that when we attempt to create a new user duplicate usernames are caught.
*/
public function test_create_refuse_duplicate_username() {
set_config('createuser', true, 'auth_userkey');
$this->auth = new auth_plugin_userkey();
$userkeymanager = new \auth_userkey\fake_userkey_manager();
$this->auth->set_userkey_manager($userkeymanager);
$originaluser = new stdClass();
$originaluser->username = 'username';
$originaluser->email = 'username@test.com';
$originaluser->firstname = 'user';
$originaluser->lastname = 'name';
$originaluser->city = 'brighton';
$originaluser->ip = '192.168.1.1';
self::getDataGenerator()->create_user($originaluser);
$duplicateuser = clone($originaluser);
$duplicateuser->email = 'duplicateuser@test.com';
$this->setExpectedException('invalid_parameter_exception', 'Username already exists: username');
$this->auth->get_login_url($duplicateuser);
}
/**
* Test that when we attempt to create a new user duplicate emails are caught.
*/
public function test_create_refuse_duplicate_email() {
set_config('createuser', true, 'auth_userkey');
set_config('mappingfield', 'username', 'auth_userkey');
$this->auth = new auth_plugin_userkey();
$userkeymanager = new \auth_userkey\fake_userkey_manager();
$this->auth->set_userkey_manager($userkeymanager);
$originaluser = new stdClass();
$originaluser->username = 'username';
$originaluser->email = 'username@test.com';
$originaluser->firstname = 'user';
$originaluser->lastname = 'name';
$originaluser->city = 'brighton';
$originaluser->ip = '192.168.1.1';
self::getDataGenerator()->create_user($originaluser);
$duplicateuser = clone($originaluser);
$duplicateuser->username = 'duplicateuser';
$this->setExpectedException('invalid_parameter_exception', 'Email address already exists: username@test.com');
$this->auth->get_login_url($duplicateuser);
}
/**
* Test that we can request a key for an existing user and update their details.
*/
public function test_return_correct_login_url_and_update_user() {
global $CFG, $DB;
set_config('updateuser', true, 'auth_userkey');
$this->auth = new auth_plugin_userkey();
$userkeymanager = new \auth_userkey\fake_userkey_manager();
$this->auth->set_userkey_manager($userkeymanager);
$originaluser = new stdClass();
$originaluser->username = 'username';
$originaluser->email = 'username@test.com';
$originaluser->firstname = 'user';
$originaluser->lastname = 'name';
$originaluser->city = 'brighton';
$originaluser->ip = '192.168.1.1';
self::getDataGenerator()->create_user($originaluser);
$user = new stdClass();
$user->username = 'usernamechanged';
$user->email = 'username@test.com';
$user->firstname = 'userchanged';
$user->lastname = 'namechanged';
$user->ip = '192.168.1.1';
$expected = $CFG->wwwroot . '/auth/userkey/login.php?key=FaKeKeyFoRtEsTiNg';
$actual = $this->auth->get_login_url($user);
$this->assertEquals($expected, $actual);
$userrecord = $DB->get_record('user', ['email' => $user->email]);
$this->assertEquals($user->username, $userrecord->username);
$this->assertEquals($user->firstname, $userrecord->firstname);
$this->assertEquals($user->lastname, $userrecord->lastname);
$this->assertEquals($originaluser->city, $userrecord->city);
$this->assertEquals('userkey', $userrecord->auth);
}
/**
* Test that when we attempt to update a user duplicate emails are caught.
*/
public function test_update_refuse_duplicate_email() {
set_config('updateuser', true, 'auth_userkey');
set_config('mappingfield', 'username', 'auth_userkey');
$this->auth = new auth_plugin_userkey();
$userkeymanager = new \auth_userkey\fake_userkey_manager();
$this->auth->set_userkey_manager($userkeymanager);
self::getDataGenerator()->create_user(['email' => 'trytoduplicate@test.com']);
self::getDataGenerator()->create_user(['username' => 'username']);
$originaluser = new stdClass();
$originaluser->username = 'username';
$originaluser->email = 'trytoduplicate@test.com';
$originaluser->firstname = 'user';
$originaluser->lastname = 'name';
$originaluser->city = 'brighton';
$originaluser->ip = '192.168.1.1';
$this->setExpectedException('invalid_parameter_exception', 'Email address already exists: trytoduplicate@test.com');
$this->auth->get_login_url($originaluser);
}
/**
* Test that when we attempt to update a user duplicate usernames are caught.
*/
public function test_update_refuse_duplicate_username() {
set_config('updateuser', true, 'auth_userkey');
$this->auth = new auth_plugin_userkey();
$userkeymanager = new \auth_userkey\fake_userkey_manager();
$this->auth->set_userkey_manager($userkeymanager);
self::getDataGenerator()->create_user(['username' => 'trytoduplicate']);
self::getDataGenerator()->create_user(['email' => 'username@test.com']);
$originaluser = new stdClass();
$originaluser->username = 'trytoduplicate';
$originaluser->email = 'username@test.com';
$originaluser->firstname = 'user';
$originaluser->lastname = 'name';
$originaluser->city = 'brighton';
$originaluser->ip = '192.168.1.1';
$this->setExpectedException('invalid_parameter_exception', 'Username already exists: trytoduplicate');
$this->auth->get_login_url($originaluser);
}
/** /**
* Test that we can get login url if we do not use fake keymanager. * Test that we can get login url if we do not use fake keymanager.
*/ */
@ -394,6 +573,33 @@ class auth_plugin_userkey_testcase extends advanced_testcase {
$actual = $this->auth->get_request_login_url_user_parameters(); $actual = $this->auth->get_request_login_url_user_parameters();
$this->assertEquals($expected, $actual); $this->assertEquals($expected, $actual);
// Check IP if createuser enabled.
set_config('createuser', true, 'auth_userkey');
$this->auth = new auth_plugin_userkey();
$expected = array(
'ip' => new external_value(PARAM_HOST, 'User IP address'),
'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user'),
'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user'),
'email' => new external_value(core_user::get_property_type('email'), 'A valid and unique email address'),
'username' => new external_value(core_user::get_property_type('username'), 'A valid and unique username'),
);
$actual = $this->auth->get_request_login_url_user_parameters();
$this->assertEquals($expected, $actual);
set_config('createuser', false, 'auth_userkey');
// Check IP if updateuser enabled.
set_config('updateuser', true, 'auth_userkey');
$this->auth = new auth_plugin_userkey();
$expected = array(
'ip' => new external_value(PARAM_HOST, 'User IP address'),
'firstname' => new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user'),
'lastname' => new external_value(core_user::get_property_type('lastname'), 'The family name of the user'),
'email' => new external_value(core_user::get_property_type('email'), 'A valid and unique email address'),
'username' => new external_value(core_user::get_property_type('username'), 'A valid and unique username'),
);
$actual = $this->auth->get_request_login_url_user_parameters();
$this->assertEquals($expected, $actual);
set_config('updateuser', false, 'auth_userkey');
} }
/** /**
@ -531,6 +737,8 @@ class auth_plugin_userkey_testcase extends advanced_testcase {
$formconfig->iprestriction = 0; $formconfig->iprestriction = 0;
$formconfig->redirecturl = 'http://google.com/'; $formconfig->redirecturl = 'http://google.com/';
$formconfig->ssourl = 'http://google.com/'; $formconfig->ssourl = 'http://google.com/';
$formconfig->createuser = false;
$formconfig->updateuser = false;
$this->auth->process_config($formconfig); $this->auth->process_config($formconfig);

View file

@ -24,7 +24,7 @@
defined('MOODLE_INTERNAL') || die; defined('MOODLE_INTERNAL') || die;
$plugin->version = 2016092600; // The current plugin version (Date: YYYYMMDDXX) $plugin->version = 2016092601; // The current plugin version (Date: YYYYMMDDXX)
$plugin->release = 2016092600; // Match release exactly to version. $plugin->release = 2016092600; // Match release exactly to version.
$plugin->requires = 2015051100; // Requires this Moodle version. $plugin->requires = 2015051100; // Requires this Moodle version.
$plugin->component = 'auth_userkey'; // Full name of the plugin (used for diagnostics). $plugin->component = 'auth_userkey'; // Full name of the plugin (used for diagnostics).