Add ability to white list IP addresses to ignore iprestrictions

This commit is contained in:
Andrew Hancox 2017-10-03 11:05:40 +01:00
parent 5a115b42f1
commit bded162058
5 changed files with 112 additions and 7 deletions

View file

@ -57,6 +57,7 @@ class auth_plugin_userkey extends auth_plugin_base {
'mappingfield' => self::DEFAULT_MAPPING_FIELD, 'mappingfield' => self::DEFAULT_MAPPING_FIELD,
'keylifetime' => 60, 'keylifetime' => 60,
'iprestriction' => 0, 'iprestriction' => 0,
'ipwhitelist' => '',
'redirecturl' => '', 'redirecturl' => '',
'ssourl' => '', 'ssourl' => '',
'createuser' => false, 'createuser' => false,
@ -347,7 +348,7 @@ class auth_plugin_userkey extends auth_plugin_base {
$requiredfieds = ['username', 'email', 'firstname', 'lastname']; $requiredfieds = ['username', 'email', 'firstname', 'lastname'];
$missingfields = []; $missingfields = [];
foreach ($requiredfieds as $requiredfied) { foreach($requiredfieds as $requiredfied) {
if (empty($user[$requiredfied])) { if (empty($user[$requiredfied])) {
$missingfields[] = $requiredfied; $missingfields[] = $requiredfied;
} }
@ -603,14 +604,14 @@ class auth_plugin_userkey extends auth_plugin_base {
$mappingfield = $this->get_mapping_field(); $mappingfield = $this->get_mapping_field();
if ($this->should_create_user() || $this->should_update_user()) { if ($this->should_create_user() || $this->should_update_user()) {
$parameters['firstname'] = new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL); $parameters['firstname'] = new external_value(core_user::get_property_type('firstname'), 'The first name(s) of the user', VALUE_OPTIONAL);
$parameters['lastname'] = new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL); $parameters['lastname'] = new external_value(core_user::get_property_type('lastname'), 'The family name of the user', VALUE_OPTIONAL);
if ($mappingfield != 'email') { if ($mappingfield != 'email') {
$parameters['email'] = new external_value(PARAM_RAW_TRIMMED, 'A valid and unique email address', VALUE_OPTIONAL); $parameters['email'] = new external_value(core_user::get_property_type('email'), 'A valid and unique email address', VALUE_OPTIONAL);
} }
if ($mappingfield != 'username') { if ($mappingfield != 'username') {
$parameters['username'] = new external_value(PARAM_USERNAME, 'A valid and unique username', VALUE_OPTIONAL); $parameters['username'] = new external_value(core_user::get_property_type('username'), 'A valid and unique username', VALUE_OPTIONAL);
} }
} }

View file

@ -124,8 +124,22 @@ class core_userkey_manager implements userkey_manager_interface {
if ($key->iprestriction) { if ($key->iprestriction) {
$remoteaddr = getremoteaddr(null); $remoteaddr = getremoteaddr(null);
if (empty($remoteaddr) or !address_in_subnet($remoteaddr, $key->iprestriction)) { $whitelist = get_config('auth_userkey', 'ipwhitelist');
print_error('ipmismatch'); if (!empty($whitelist)) {
$ips = explode(';', $whitelist);
$whitelisted = false;
foreach ($ips as $ip) {
if (address_in_subnet($remoteaddr, $ip)) {
$whitelisted = true;
}
}
if (!$whitelisted) {
print_error('ipmismatch', 'error', '', null, "Remote address: $remoteaddr\nKey IP: $key->iprestriction");
}
} else if (empty($remoteaddr) ) {
print_error('noip', 'auth_userkey');
} else if (!address_in_subnet($remoteaddr, $key->iprestriction)) {
print_error('ipmismatch', 'error', '', null, "Remote address: $remoteaddr\nKey IP: $key->iprestriction");
} }
} }

View file

@ -29,6 +29,10 @@ $string['mappingfield_desc'] = 'This user field will be used to find relevant us
$string['iprestriction'] = 'IP restriction'; $string['iprestriction'] = 'IP restriction';
$string['iprestriction_desc'] = 'If enabled, a web call has to contain "ip" parameter when requesting login URL. $string['iprestriction_desc'] = 'If enabled, a web call has to contain "ip" parameter when requesting login URL.
A user has to have provided IP to be able to use a key to login to LMS.'; A user has to have provided IP to be able to use a key to login to LMS.';
$string['ipwhitelist'] = 'Whitelist IP ranges';
$string['ipwhitelist_desc'] = "Ignore IP restrictions if the IP address the token was issued for or the login attempt comes from falls within any of these ranges.
\nThis can happen when some users reach Moodle or the system issuing login tokens via a private network or DMZ.
\nIf the route to either the system issuing tokens or this Moodle is via a private address range then set this value to 10.0.0.0/8;172.16.0.0/12;192.168.0.0/16";
$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';

View file

@ -49,6 +49,13 @@ $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>
<tr valign="top">
<?php $field = 'ipwhitelist' ?>
<td align="right"><label for="<?php echo $field ?>"><?php print_string($field, 'auth_userkey') ?></label></td>
<td><input type="text" size="60" name="<?php echo $field ?>" value="<?php print $config->$field ?>" placeholder=""><br>
<?php if (isset($err[$field])) { echo $OUTPUT->notification($err[$field], 'notifyfailure'); } ?>
<?php print_string($field.'_desc', 'auth_userkey') ?></td>
</tr>
<tr valign="top"> <tr valign="top">
<?php $field = 'redirecturl' ?> <?php $field = 'redirecturl' ?>
<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>

View file

@ -756,6 +756,7 @@ class auth_plugin_userkey_testcase extends advanced_testcase {
$formconfig->mappingfield = 'email'; $formconfig->mappingfield = 'email';
$formconfig->keylifetime = 100; $formconfig->keylifetime = 100;
$formconfig->iprestriction = 0; $formconfig->iprestriction = 0;
$formconfig->ipwhitelist = '';
$formconfig->redirecturl = 'http://google.com/'; $formconfig->redirecturl = 'http://google.com/';
$formconfig->ssourl = 'http://google.com/'; $formconfig->ssourl = 'http://google.com/';
$formconfig->createuser = false; $formconfig->createuser = false;
@ -842,6 +843,84 @@ class auth_plugin_userkey_testcase extends advanced_testcase {
$this->auth->user_login_userkey(); $this->auth->user_login_userkey();
} }
/**
* Test that IP address mismatch exception gets thrown if incorrect IP.
*
* @expectedException moodle_exception
* @expectedExceptionMessage Unsupported redirect to http://www.example.com/moodle detected, execution terminated.
*/
public function test_ipmismatch_exception_notthrown_if_ip_is_whitelisted() {
global $DB;
set_config('ipwhitelist', '10.0.0.0/8;172.16.0.0/12;192.168.0.0/16', 'auth_userkey');
$key = new stdClass();
$key->value = 'IpmismatchKey';
$key->script = 'auth/userkey';
$key->userid = $this->user->id;
$key->instance = $this->user->id;
$key->iprestriction = '192.168.1.1';
$key->validuntil = time() + 300;
$key->timecreated = time();
$DB->insert_record('user_private_key', $key);
$_POST['key'] = 'IpmismatchKey';
$_SERVER['HTTP_CLIENT_IP'] = '192.168.1.2';
@$this->auth->user_login_userkey();
}
/**
* Test that IP address mismatch exception gets thrown if incorrect IP.
*
* @expectedException moodle_exception
* @expectedExceptionMessage Unsupported redirect to http://www.example.com/moodle detected, execution terminated.
*/
public function test_ipmismatch_exception_notthrown_if_ip_is_whitelisted_temp() {
global $DB;
set_config('ipwhitelist', '192.168.1.2', 'auth_userkey');
$key = new stdClass();
$key->value = 'IpmismatchKey';
$key->script = 'auth/userkey';
$key->userid = $this->user->id;
$key->instance = $this->user->id;
$key->iprestriction = '192.168.1.1';
$key->validuntil = time() + 300;
$key->timecreated = time();
$DB->insert_record('user_private_key', $key);
$_POST['key'] = 'IpmismatchKey';
$_SERVER['HTTP_CLIENT_IP'] = '192.168.1.2';
@$this->auth->user_login_userkey();
}
/**
* Test that IP address mismatch exception gets thrown if incorrect IP.
*
* @expectedException moodle_exception
* @expectedExceptionMessage Client IP address mismatch
*/
public function test_ipmismatch_exception_thrown_if_ip_is_outside_whitelist() {
global $DB;
set_config('ipwhitelist', '10.0.0.0/8;172.16.0.0/12;192.168.0.0/16', 'auth_userkey');
$key = new stdClass();
$key->value = 'IpmismatchKey';
$key->script = 'auth/userkey';
$key->userid = $this->user->id;
$key->instance = $this->user->id;
$key->iprestriction = '192.161.1.1';
$key->validuntil = time() + 300;
$key->timecreated = time();
$DB->insert_record('user_private_key', $key);
$_POST['key'] = 'IpmismatchKey';
$_SERVER['HTTP_CLIENT_IP'] = '192.161.1.2';
$this->auth->user_login_userkey();
}
/** /**
* Test that IP address mismatch exception gets thrown if user id is incorrect. * Test that IP address mismatch exception gets thrown if user id is incorrect.
* *