From 6969d2834bc1162a9ebc8af85024551a977042ba Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 28 Jun 2022 12:18:58 +0200 Subject: [PATCH] MDL-74862 tool_mobile: Allow to disable QR login IP checks --- admin/tool/mobile/classes/api.php | 2 +- admin/tool/mobile/lang/en/tool_mobile.php | 2 + admin/tool/mobile/settings.php | 5 ++ admin/tool/mobile/tests/externallib_test.php | 71 ++++++++++++++++++++++++++++ admin/tool/mobile/version.php | 2 +- 5 files changed, 80 insertions(+), 2 deletions(-) diff --git a/admin/tool/mobile/classes/api.php b/admin/tool/mobile/classes/api.php index 72e1151ab9c..2e450449bfe 100644 --- a/admin/tool/mobile/classes/api.php +++ b/admin/tool/mobile/classes/api.php @@ -402,7 +402,7 @@ class api { delete_user_key('tool_mobile', $USER->id); // Create a new key. - $iprestriction = getremoteaddr(null); + $iprestriction = !empty($mobilesettings->qrsameipcheck) ? getremoteaddr(null) : null; $qrkeyttl = !empty($mobilesettings->qrkeyttl) ? $mobilesettings->qrkeyttl : self::LOGIN_QR_KEY_TTL; $validuntil = time() + $qrkeyttl; return create_user_key('tool_mobile', $USER->id, null, $iprestriction, $validuntil); diff --git a/admin/tool/mobile/lang/en/tool_mobile.php b/admin/tool/mobile/lang/en/tool_mobile.php index 578d848435d..caba6915cc7 100644 --- a/admin/tool/mobile/lang/en/tool_mobile.php +++ b/admin/tool/mobile/lang/en/tool_mobile.php @@ -125,6 +125,8 @@ $string['qrcodetypeurl'] = 'QR code with site URL'; $string['qrcodetypelogin'] = 'QR code with automatic login'; $string['qrkeyttl'] = 'QR authentication key duration'; $string['qrkeyttl_desc'] = 'The length of time for which a QR code for automatic login is valid.'; +$string['qrsameipcheck'] = 'QR authentication same IP check'; +$string['qrsameipcheck_desc'] = 'This setting forces users to use the same network for both generating and scanning a QR code for login. Only disable it in case your users are reporting issues with QR login.'; $string['readingthisemailgettheapp'] = 'Reading this in an email? Download the mobile app and receive notifications on your mobile device.'; $string['remoteaddons'] = 'Remote add-ons'; $string['scanqrcode'] = 'Scan QR code'; diff --git a/admin/tool/mobile/settings.php b/admin/tool/mobile/settings.php index e9db68c89ba..b2e3630e2a1 100644 --- a/admin/tool/mobile/settings.php +++ b/admin/tool/mobile/settings.php @@ -122,6 +122,11 @@ if ($hassiteconfig) { new lang_string('qrkeyttl_desc', 'tool_mobile'), tool_mobile\api::LOGIN_QR_KEY_TTL, MINSECS)); $temp->hide_if('tool_mobile/qrkeyttl', 'tool_mobile/qrcodetype', 'neq', tool_mobile\api::QR_CODE_LOGIN); + $temp->add(new admin_setting_configcheckbox('tool_mobile/qrsameipcheck', + new lang_string('qrsameipcheck', 'tool_mobile'), + new lang_string('qrsameipcheck_desc', 'tool_mobile'), 1)); + $temp->hide_if('tool_mobile/qrsameipcheck', 'tool_mobile/qrcodetype', 'neq', tool_mobile\api::QR_CODE_LOGIN); + $temp->add(new admin_setting_configtext('tool_mobile/forcedurlscheme', new lang_string('forcedurlscheme_key', 'tool_mobile'), new lang_string('forcedurlscheme', 'tool_mobile'), 'moodlemobile', PARAM_NOTAGS)); diff --git a/admin/tool/mobile/tests/externallib_test.php b/admin/tool/mobile/tests/externallib_test.php index a3af5eec152..c518df5f554 100644 --- a/admin/tool/mobile/tests/externallib_test.php +++ b/admin/tool/mobile/tests/externallib_test.php @@ -628,6 +628,7 @@ class externallib_test extends externallib_advanced_testcase { $this->setUser($user); $mobilesettings = get_config('tool_mobile'); + $mobilesettings->qrsameipcheck = 1; $qrloginkey = api::get_qrlogin_key($mobilesettings); // Generate new tokens, the ones we expect to receive. @@ -651,6 +652,76 @@ class externallib_test extends externallib_advanced_testcase { $result = external::get_tokens_for_qr_login(random_string('64'), $user->id); } + /* + * Test get_tokens_for_qr_login ignore ip check. + */ + public function test_get_tokens_for_qr_login_ignore_ip_check() { + global $DB, $CFG, $USER; + + $this->resetAfterTest(true); + + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + + $mobilesettings = get_config('tool_mobile'); + $mobilesettings->qrsameipcheck = 0; + $qrloginkey = api::get_qrlogin_key($mobilesettings); + + $key = $DB->get_record('user_private_key', ['value' => $qrloginkey]); + $this->assertNull($key->iprestriction); + + // Generate new tokens, the ones we expect to receive. + $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); + $token = external_generate_token_for_current_user($service); + + // Fake the app. + \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . + 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); + + $result = external::get_tokens_for_qr_login($qrloginkey, $USER->id); + $result = \external_api::clean_returnvalue(external::get_tokens_for_qr_login_returns(), $result); + + $this->assertEmpty($result['warnings']); + $this->assertEquals($token->token, $result['token']); + $this->assertEquals($token->privatetoken, $result['privatetoken']); + + // Now, try with an invalid key. + $this->expectException('moodle_exception'); + $this->expectExceptionMessage(get_string('invalidkey', 'error')); + $result = external::get_tokens_for_qr_login(random_string('64'), $user->id); + } + + /* + * Test get_tokens_for_qr_login ip check fails. + */ + public function test_get_tokens_for_qr_login_ip_check_mismatch() { + global $DB, $CFG, $USER; + + $this->resetAfterTest(true); + + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + + $mobilesettings = get_config('tool_mobile'); + $mobilesettings->qrsameipcheck = 1; + $qrloginkey = api::get_qrlogin_key($mobilesettings); + + // Alter expected ip. + $DB->set_field('user_private_key', 'iprestriction', '6.6.6.6', ['value' => $qrloginkey]); + + // Generate new tokens, the ones we expect to receive. + $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); + $token = external_generate_token_for_current_user($service); + + // Fake the app. + \core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . + 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); + + $this->expectException('moodle_exception'); + $this->expectExceptionMessage(get_string('ipmismatch', 'error')); + $result = external::get_tokens_for_qr_login($qrloginkey, $USER->id); + } + /** * Test get_tokens_for_qr_login missing QR code enabled. */ diff --git a/admin/tool/mobile/version.php b/admin/tool/mobile/version.php index 16fcd6afccc..2ad143b924c 100644 --- a/admin/tool/mobile/version.php +++ b/admin/tool/mobile/version.php @@ -23,7 +23,7 @@ */ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2022041900; // The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2022041901; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2022041200; // Requires this Moodle version. $plugin->component = 'tool_mobile'; // Full name of the plugin (used for diagnostics). $plugin->dependencies = array( -- 2.11.4.GIT