Kumi
9a87e9a496
Some checks failed
ci / ci (push) Failing after 0s
Rebranded the auth_userkey plugin to auth_jwt, including changes to namespace, class names, and configuration settings to reflect the new jwt-based authentication mechanism. Updated copyright notices and contact information accordingly. This refactoring also includes the introduction of JWT token management using the Firebase JWT library, replacing the user quick keys.
1111 lines
38 KiB
PHP
1111 lines
38 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
namespace auth_jwt;
|
|
|
|
use advanced_testcase;
|
|
use auth_plugin_jwt;
|
|
use stdClass;
|
|
use invalid_parameter_exception;
|
|
use moodle_exception;
|
|
use external_value;
|
|
|
|
/**
|
|
* Tests for auth_plugin_jwt class.
|
|
*
|
|
* @covers \auth_plugin_jwt
|
|
*
|
|
* @package auth_jwt
|
|
* @copyright 2016 Dmitrii Metelkin (dmitriim@catalyst-au.net), 2024 Kumi Systems e.U.
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class auth_plugin_test extends advanced_testcase {
|
|
/**
|
|
* An instance of auth_plugin_jwt class.
|
|
* @var auth_plugin_jwt
|
|
*/
|
|
protected $auth;
|
|
|
|
/**
|
|
* User object.
|
|
* @var
|
|
*/
|
|
protected $user;
|
|
|
|
/**
|
|
* Path used for the redirection.
|
|
* @var string
|
|
*/
|
|
const REDIRECTION_PATH = "/redirection";
|
|
|
|
/**
|
|
* Initial set up.
|
|
*/
|
|
public function setUp(): void {
|
|
global $CFG;
|
|
|
|
require_once($CFG->libdir . "/externallib.php");
|
|
require_once($CFG->dirroot . '/auth/jwt/tests/fake_jwt_manager.php');
|
|
require_once($CFG->dirroot . '/auth/jwt/auth.php');
|
|
require_once($CFG->dirroot . '/user/lib.php');
|
|
|
|
parent::setUp();
|
|
|
|
$this->resetAfterTest();
|
|
$CFG->getremoteaddrconf = GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR;
|
|
$this->auth = new auth_plugin_jwt();
|
|
$this->user = self::getDataGenerator()->create_user();
|
|
}
|
|
|
|
/**
|
|
* A helper function to create TestKey.
|
|
*
|
|
* @param array $record Key record.
|
|
*/
|
|
protected function create_user_private_key(array $record = []) {
|
|
global $DB;
|
|
|
|
$record = (object)$record;
|
|
|
|
if (!isset($record->value)) {
|
|
$record->value = 'TestKey';
|
|
}
|
|
|
|
if (!isset($record->userid)) {
|
|
$record->userid = $this->user->id;
|
|
}
|
|
|
|
if (!isset($record->userid)) {
|
|
$record->instance = $this->user->id;
|
|
}
|
|
|
|
if (!isset($record->iprestriction)) {
|
|
$record->iprestriction = null;
|
|
}
|
|
if (!isset($record->validuntil)) {
|
|
$record->validuntil = time() + 300;
|
|
}
|
|
if (!isset($record->timecreated)) {
|
|
$record->timecreated = time();
|
|
}
|
|
|
|
$record->script = 'auth/jwt';
|
|
|
|
$DB->insert_record('user_private_key', $record);
|
|
}
|
|
|
|
/**
|
|
* Test that users can't login using login form.
|
|
*/
|
|
public function test_users_can_not_login_using_login_form() {
|
|
$user = new stdClass();
|
|
$user->auth = 'jwt';
|
|
$user->username = 'username';
|
|
$user->password = 'correctpassword';
|
|
|
|
self::getDataGenerator()->create_user($user);
|
|
|
|
$this->assertFalse($this->auth->user_login('username', 'correctpassword'));
|
|
$this->assertFalse($this->auth->user_login('username', 'incorrectpassword'));
|
|
}
|
|
|
|
/**
|
|
* Test that the plugin doesn't allow to store users passwords.
|
|
*/
|
|
public function test_auth_plugin_does_not_allow_to_store_passwords() {
|
|
$this->assertTrue($this->auth->prevent_local_passwords());
|
|
}
|
|
|
|
/**
|
|
* Test that the plugin is external.
|
|
*/
|
|
public function test_auth_plugin_is_external() {
|
|
$this->assertFalse($this->auth->is_internal());
|
|
}
|
|
|
|
/**
|
|
* Test that the plugin doesn't allow users to change the passwords.
|
|
*/
|
|
public function test_auth_plugin_does_not_allow_to_change_passwords() {
|
|
$this->assertFalse($this->auth->can_change_password());
|
|
}
|
|
|
|
/**
|
|
* Test that default mapping field gets returned correctly.
|
|
*/
|
|
public function test_get_default_mapping_field() {
|
|
$expected = 'email';
|
|
$actual = $this->auth->get_mapping_field();
|
|
|
|
$this->assertEquals($expected, $actual);
|
|
}
|
|
|
|
/**
|
|
* Test that logout page hook sets global redirect variable correctly.
|
|
*/
|
|
public function test_logoutpage_hook_sets_global_redirect_correctly() {
|
|
global $redirect, $SESSION;
|
|
|
|
$this->auth->logoutpage_hook();
|
|
$this->assertEquals('', $redirect);
|
|
|
|
$SESSION->jwt = true;
|
|
$this->auth = new auth_plugin_jwt();
|
|
$this->auth->logoutpage_hook();
|
|
$this->assertEquals('', $redirect);
|
|
|
|
unset($SESSION->jwt);
|
|
set_config('redirecturl', 'http://example.com', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
$this->auth->logoutpage_hook();
|
|
$this->assertEquals('', $redirect);
|
|
|
|
$SESSION->jwt = true;
|
|
set_config('redirecturl', 'http://example.com', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
$this->auth->logoutpage_hook();
|
|
$this->assertEquals('http://example.com', $redirect);
|
|
}
|
|
|
|
/**
|
|
* Test that configured mapping field gets returned correctly.
|
|
*/
|
|
public function test_get_mapping_field() {
|
|
set_config('mappingfield', 'username', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$expected = 'username';
|
|
$actual = $this->auth->get_mapping_field();
|
|
|
|
$this->assertEquals($expected, $actual);
|
|
}
|
|
|
|
/**
|
|
* Test that auth plugin throws correct exception if default mapping field is not provided.
|
|
*/
|
|
public function test_throwing_exception_if_default_mapping_field_is_not_provided() {
|
|
$user = array();
|
|
$this->expectException(invalid_parameter_exception::class);
|
|
$this->expectExceptionMessage('Invalid parameter value detected (Required field "email" is not set or empty.)');
|
|
|
|
$actual = $this->auth->get_login_url($user);
|
|
}
|
|
|
|
/**
|
|
* Test that auth plugin throws correct exception if username mapping field is not provided, but set in configs.
|
|
*/
|
|
public function test_throwing_exception_if_mapping_field_username_is_not_provided() {
|
|
$user = array();
|
|
set_config('mappingfield', 'username', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$this->expectException(invalid_parameter_exception::class);
|
|
$this->expectExceptionMessage('Invalid parameter value detected (Required field "username" is not set or empty.)');
|
|
|
|
$actual = $this->auth->get_login_url($user);
|
|
}
|
|
|
|
/**
|
|
* Test that auth plugin throws correct exception if idnumber mapping field is not provided, but set in configs.
|
|
*/
|
|
public function test_throwing_exception_if_mapping_field_idnumber_is_not_provided() {
|
|
$user = array();
|
|
set_config('mappingfield', 'idnumber', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$this->expectException(invalid_parameter_exception::class);
|
|
$this->expectExceptionMessage('Invalid parameter value detected (Required field "idnumber" is not set or empty.)');
|
|
|
|
$actual = $this->auth->get_login_url($user);
|
|
}
|
|
|
|
/**
|
|
* Test that auth plugin throws correct exception if we trying to request not existing user.
|
|
*/
|
|
public function test_throwing_exception_if_user_is_not_exist() {
|
|
$user = array();
|
|
$user['email'] = 'notexists@test.com';
|
|
|
|
$this->expectException(invalid_parameter_exception::class);
|
|
$this->expectExceptionMessage('Invalid parameter value detected (User is not exist)');
|
|
$actual = $this->auth->get_login_url($user);
|
|
}
|
|
|
|
/**
|
|
* Test that auth plugin throws correct exception if we trying to request user,
|
|
* but ip field is not set and iprestriction is enabled.
|
|
*/
|
|
public function test_throwing_exception_if_iprestriction_is_enabled_but_ip_is_missing_in_data() {
|
|
$user = array();
|
|
$user['email'] = 'exists@test.com';
|
|
set_config('iprestriction', true, 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$this->expectException(invalid_parameter_exception::class);
|
|
$this->expectExceptionMessage('Invalid parameter value detected (Required parameter "ip" is not set.)');
|
|
|
|
$actual = $this->auth->get_login_url($user);
|
|
}
|
|
|
|
/**
|
|
* Test that we can request a user provided user data as an array.
|
|
*/
|
|
public function test_return_correct_login_url_if_user_is_array() {
|
|
global $CFG;
|
|
|
|
$user = array();
|
|
$user['username'] = 'username';
|
|
$user['email'] = 'exists@test.com';
|
|
|
|
self::getDataGenerator()->create_user($user);
|
|
|
|
$jwtmanager = new fake_jwt_manager();
|
|
$this->auth->set_jwt_manager($jwtmanager);
|
|
|
|
$expected = $CFG->wwwroot . '/auth/jwt/login.php?key=FaKeKeyFoRtEsTiNg';
|
|
$actual = $this->auth->get_login_url($user);
|
|
|
|
$this->assertEquals($expected, $actual);
|
|
}
|
|
|
|
/**
|
|
* Test that we can request a user provided user data as an object.
|
|
*/
|
|
public function test_return_correct_login_url_if_user_is_object() {
|
|
global $CFG;
|
|
|
|
$user = new stdClass();
|
|
$user->username = 'username';
|
|
$user->email = 'exists@test.com';
|
|
|
|
self::getDataGenerator()->create_user($user);
|
|
|
|
$jwtmanager = new fake_jwt_manager();
|
|
$this->auth->set_jwt_manager($jwtmanager);
|
|
|
|
$expected = $CFG->wwwroot . '/auth/jwt/login.php?key=FaKeKeyFoRtEsTiNg';
|
|
$actual = $this->auth->get_login_url($user);
|
|
|
|
$this->assertEquals($expected, $actual);
|
|
}
|
|
|
|
/**
|
|
* Test that we can request a user provided user data as an object.
|
|
*/
|
|
public function test_return_correct_login_url_if_iprestriction_is_enabled_and_data_is_correct() {
|
|
global $CFG;
|
|
|
|
$user = new stdClass();
|
|
$user->username = 'username';
|
|
$user->email = 'exists@test.com';
|
|
$user->ip = '192.168.1.1';
|
|
|
|
self::getDataGenerator()->create_user($user);
|
|
|
|
$jwtmanager = new fake_jwt_manager();
|
|
$this->auth->set_jwt_manager($jwtmanager);
|
|
|
|
$expected = $CFG->wwwroot . '/auth/jwt/login.php?key=FaKeKeyFoRtEsTiNg';
|
|
$actual = $this->auth->get_login_url($user);
|
|
|
|
$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_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$jwtmanager = new fake_jwt_manager();
|
|
$this->auth->set_jwt_manager($jwtmanager);
|
|
|
|
$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/jwt/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(1, $userrecord->confirmed);
|
|
$this->assertEquals('jwt', $userrecord->auth);
|
|
}
|
|
|
|
/**
|
|
* Test that we can request a key for a new user.
|
|
*/
|
|
public function test_missing_data_to_create_user() {
|
|
global $CFG, $DB;
|
|
|
|
set_config('createuser', true, 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$jwtmanager = new fake_jwt_manager();
|
|
$this->auth->set_jwt_manager($jwtmanager);
|
|
|
|
$user = new stdClass();
|
|
$user->email = 'username@test.com';
|
|
$user->ip = '192.168.1.1';
|
|
|
|
$this->expectException(invalid_parameter_exception::class);
|
|
$this->expectExceptionMessage('Unable to create user, missing value(s): username,firstname,lastname');
|
|
|
|
$this->auth->get_login_url($user);
|
|
}
|
|
|
|
/**
|
|
* 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_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$jwtmanager = new fake_jwt_manager();
|
|
$this->auth->set_jwt_manager($jwtmanager);
|
|
|
|
$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->expectException(invalid_parameter_exception::class);
|
|
$this->expectExceptionMessage('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_jwt');
|
|
set_config('mappingfield', 'username', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$jwtmanager = new fake_jwt_manager();
|
|
$this->auth->set_jwt_manager($jwtmanager);
|
|
|
|
$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->expectException(invalid_parameter_exception::class);
|
|
$this->expectExceptionMessage('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_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$jwtmanager = new fake_jwt_manager();
|
|
$this->auth->set_jwt_manager($jwtmanager);
|
|
|
|
$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/jwt/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('jwt', $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_jwt');
|
|
set_config('mappingfield', 'username', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$jwtmanager = new fake_jwt_manager();
|
|
$this->auth->set_jwt_manager($jwtmanager);
|
|
|
|
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->expectException(invalid_parameter_exception::class);
|
|
$this->expectExceptionMessage('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_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$jwtmanager = new fake_jwt_manager();
|
|
$this->auth->set_jwt_manager($jwtmanager);
|
|
|
|
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->expectException(invalid_parameter_exception::class);
|
|
$this->expectExceptionMessage('Username already exists: trytoduplicate');
|
|
|
|
$this->auth->get_login_url($originaluser);
|
|
}
|
|
|
|
/**
|
|
* Test that we can get login url if we do not use fake keymanager.
|
|
*/
|
|
public function test_return_correct_login_url_if_user_is_object_using_default_keymanager() {
|
|
global $DB, $CFG;
|
|
|
|
$user = array();
|
|
$user['username'] = 'username';
|
|
$user['email'] = 'exists@test.com';
|
|
|
|
$user = self::getDataGenerator()->create_user($user);
|
|
|
|
create_user_key('auth/jwt', $user->id);
|
|
create_user_key('auth/jwt', $user->id);
|
|
create_user_key('auth/jwt', $user->id);
|
|
$keys = $DB->get_records('user_private_key', array('userid' => $user->id));
|
|
|
|
$this->assertEquals(3, count($keys));
|
|
|
|
$actual = $this->auth->get_login_url($user);
|
|
|
|
$keys = $DB->get_records('user_private_key', array('userid' => $user->id));
|
|
$this->assertEquals(1, count($keys));
|
|
|
|
$actualkey = $DB->get_record('user_private_key', array('userid' => $user->id));
|
|
|
|
$expected = $CFG->wwwroot . '/auth/jwt/login.php?key=' . $actualkey->value;
|
|
|
|
$this->assertEquals($expected, $actual);
|
|
}
|
|
|
|
/**
|
|
* Test that we can return correct allowed mapping fields.
|
|
*/
|
|
public function test_get_allowed_mapping_fields_list() {
|
|
$expected = array(
|
|
'username' => 'Username',
|
|
'email' => 'Email address',
|
|
'idnumber' => 'ID number',
|
|
);
|
|
|
|
$actual = $this->auth->get_allowed_mapping_fields();
|
|
|
|
$this->assertEquals($expected, $actual);
|
|
}
|
|
|
|
/**
|
|
* Test that we can get correct request parameters based on the plugin configuration.
|
|
*/
|
|
public function test_get_request_login_url_user_parameters_based_on_plugin_config() {
|
|
// Check email as it should be set by default.
|
|
$expected = array(
|
|
'email' => new external_value(
|
|
PARAM_EMAIL,
|
|
'A valid email address'
|
|
),
|
|
);
|
|
|
|
$actual = $this->auth->get_request_login_url_user_parameters();
|
|
$this->assertEquals($expected, $actual);
|
|
|
|
// Check username.
|
|
set_config('mappingfield', 'username', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$expected = array(
|
|
'username' => new external_value(
|
|
PARAM_USERNAME,
|
|
'Username'
|
|
),
|
|
);
|
|
|
|
$actual = $this->auth->get_request_login_url_user_parameters();
|
|
$this->assertEquals($expected, $actual);
|
|
|
|
// Check idnumber.
|
|
set_config('mappingfield', 'idnumber', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$expected = array(
|
|
'idnumber' => new external_value(
|
|
PARAM_RAW,
|
|
'An arbitrary ID code number perhaps from the institution'
|
|
),
|
|
);
|
|
|
|
$actual = $this->auth->get_request_login_url_user_parameters();
|
|
$this->assertEquals($expected, $actual);
|
|
|
|
// Check some junk field name.
|
|
set_config('mappingfield', 'junkfield', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$expected = array();
|
|
|
|
$actual = $this->auth->get_request_login_url_user_parameters();
|
|
$this->assertEquals($expected, $actual);
|
|
|
|
// Check IP if iprestriction disabled.
|
|
set_config('iprestriction', false, 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
$expected = array();
|
|
$actual = $this->auth->get_request_login_url_user_parameters();
|
|
$this->assertEquals($expected, $actual);
|
|
|
|
// Check IP if iprestriction enabled.
|
|
set_config('iprestriction', true, 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
$expected = array(
|
|
'ip' => new external_value(
|
|
PARAM_HOST,
|
|
'User IP address'
|
|
),
|
|
);
|
|
$actual = $this->auth->get_request_login_url_user_parameters();
|
|
$this->assertEquals($expected, $actual);
|
|
|
|
// Check IP if createuser enabled.
|
|
set_config('createuser', true, 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
$expected = array(
|
|
'ip' => new external_value(PARAM_HOST, 'User IP address'),
|
|
'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
|
|
'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
|
|
'email' => new external_value(PARAM_RAW_TRIMMED, 'A valid and unique email address', VALUE_OPTIONAL),
|
|
'username' => new external_value(PARAM_USERNAME, 'A valid and unique username', VALUE_OPTIONAL),
|
|
);
|
|
$actual = $this->auth->get_request_login_url_user_parameters();
|
|
$this->assertEquals($expected, $actual);
|
|
set_config('createuser', false, 'auth_jwt');
|
|
|
|
// Check IP if updateuser enabled.
|
|
set_config('updateuser', true, 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
$expected = array(
|
|
'ip' => new external_value(PARAM_HOST, 'User IP address'),
|
|
'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
|
|
'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
|
|
'email' => new external_value(PARAM_RAW_TRIMMED, 'A valid and unique email address', VALUE_OPTIONAL),
|
|
'username' => new external_value(PARAM_USERNAME, 'A valid and unique username', VALUE_OPTIONAL),
|
|
);
|
|
$actual = $this->auth->get_request_login_url_user_parameters();
|
|
$this->assertEquals($expected, $actual);
|
|
set_config('updateuser', false, 'auth_jwt');
|
|
}
|
|
|
|
/**
|
|
* Data provider for testing URL validation functions.
|
|
*
|
|
* @return array First element URL, the second URL is error message. Empty error massage means no errors.
|
|
*/
|
|
public function url_data_provider() {
|
|
return array(
|
|
array('', ''),
|
|
array('http://google.com/', ''),
|
|
array('https://google.com', ''),
|
|
array('http://some.very.long.and.silly.domain/with/a/path/', ''),
|
|
array('http://0.255.1.1/numericip.php', ''),
|
|
array('http://0.255.1.1/numericip.php?test=1&id=2', ''),
|
|
array('/just/a/path', 'You should provide valid URL'),
|
|
array('random string', 'You should provide valid URL'),
|
|
array(123456, 'You should provide valid URL'),
|
|
array('php://google.com', 'You should provide valid URL'),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test required parameter exception gets thrown id try to login, but key is not set.
|
|
*/
|
|
public function test_required_parameter_exception_thrown_if_key_not_set() {
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('A required parameter (key) was missing');
|
|
|
|
$this->auth->user_login_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test that incorrect key exception gets thrown if a key is incorrect.
|
|
*/
|
|
public function test_invalid_key_exception_thrown_if_invalid_key() {
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('Incorrect key');
|
|
|
|
$_POST['key'] = 'InvalidKey';
|
|
$this->auth->user_login_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test that expired key exception gets thrown if a key is expired.
|
|
*/
|
|
public function test_expired_key_exception_thrown_if_expired_key() {
|
|
$this->create_user_private_key(['validuntil' => time() - 3000]);
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('Expired key');
|
|
|
|
$_POST['key'] = 'TestKey';
|
|
$this->auth->user_login_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test that IP address mismatch exception gets thrown if incorrect IP.
|
|
*/
|
|
public function test_ipmismatch_exception_thrown_if_ip_is_incorrect() {
|
|
$this->create_user_private_key(['iprestriction' => '192.168.1.1']);
|
|
|
|
$_POST['key'] = 'TestKey';
|
|
$_SERVER['HTTP_CLIENT_IP'] = '192.168.1.2';
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('Client IP address mismatch');
|
|
|
|
$this->auth->user_login_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test that IP address mismatch exception gets thrown if incorrect IP and outside whitelist.
|
|
*/
|
|
public function test_ipmismatch_exception_thrown_if_ip_is_outside_whitelist() {
|
|
set_config('ipwhitelist', '10.0.0.0/8;172.16.0.0/12;192.168.0.0/16', 'auth_jwt');
|
|
$this->create_user_private_key(['iprestriction' => '192.161.1.1']);
|
|
|
|
$_POST['key'] = 'TestKey';
|
|
$_SERVER['HTTP_CLIENT_IP'] = '192.161.1.2';
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('Client IP address mismatch');
|
|
|
|
$this->auth->user_login_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test that IP address mismatch exception gets thrown if user id is incorrect.
|
|
*/
|
|
public function test_invalid_user_exception_thrown_if_user_is_invalid() {
|
|
$this->create_user_private_key([
|
|
'userid' => 777,
|
|
'instance' => 777,
|
|
'iprestriction' => '192.168.1.1',
|
|
]);
|
|
|
|
$_POST['key'] = 'TestKey';
|
|
$_SERVER['HTTP_CLIENT_IP'] = '192.168.1.1';
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('Invalid user');
|
|
|
|
$this->auth->user_login_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test that key gets removed after a user logged in.
|
|
*/
|
|
public function test_that_key_gets_removed_after_user_logged_in() {
|
|
global $DB;
|
|
|
|
$this->create_user_private_key([
|
|
'value' => 'RemoveKey',
|
|
'iprestriction' => '192.168.1.1',
|
|
]);
|
|
|
|
$_POST['key'] = 'RemoveKey';
|
|
$_SERVER['HTTP_CLIENT_IP'] = '192.168.1.1';
|
|
|
|
try {
|
|
// Using @ is the only way to test this. Thanks moodle!
|
|
@$this->auth->user_login_jwt();
|
|
} catch (moodle_exception $e) {
|
|
$keyexists = $DB->record_exists('user_private_key', array('value' => 'RemoveKey'));
|
|
$this->assertFalse($keyexists);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test that a user logs in and gets redirected correctly.
|
|
*/
|
|
public function test_that_user_logged_in_and_redirected() {
|
|
global $CFG;
|
|
|
|
$this->create_user_private_key();
|
|
$CFG->wwwroot = 'http://www.example.com/moodle';
|
|
$_POST['key'] = 'TestKey';
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('Unsupported redirect to http://www.example.com/moodle detected, execution terminated');
|
|
|
|
@$this->auth->user_login_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test that a user logs in correctly.
|
|
*/
|
|
public function test_that_user_logged_in_correctly() {
|
|
global $USER, $SESSION;
|
|
|
|
$this->create_user_private_key();
|
|
|
|
$_POST['key'] = 'TestKey';
|
|
|
|
try {
|
|
// Using @ is the only way to test this. Thanks moodle!
|
|
@$this->auth->user_login_jwt();
|
|
} catch (moodle_exception $e) {
|
|
$this->assertEquals($this->user->id, $USER->id);
|
|
$this->assertSame(sesskey(), $USER->sesskey);
|
|
$this->assertObjectHasAttribute('jwt', $SESSION);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test that a user gets redirected to internal wantsurl URL successful log in.
|
|
*/
|
|
public function test_that_user_gets_redirected_to_internal_wantsurl() {
|
|
$this->create_user_private_key();
|
|
$_POST['key'] = 'TestKey';
|
|
$_POST['wantsurl'] = '/course/index.php?id=12&key=134';
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('Unsupported redirect to /course/index.php?id=12&key=134 detected, execution terminated');
|
|
|
|
// Using @ is the only way to test this. Thanks moodle!
|
|
@$this->auth->user_login_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test that a user gets redirected to external wantsurl URL successful log in.
|
|
*/
|
|
public function test_that_user_gets_redirected_to_external_wantsurl() {
|
|
$this->create_user_private_key();
|
|
|
|
$_POST['key'] = 'TestKey';
|
|
$_POST['wantsurl'] = 'http://test.com/course/index.php?id=12&key=134';
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('Unsupported redirect to http://test.com/course/index.php?id=12&key=134 detected, execution terminated');
|
|
|
|
// Using @ is the only way to test this. Thanks moodle!
|
|
@$this->auth->user_login_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test that login hook redirects a user if skipsso not set and ssourl is set.
|
|
*/
|
|
public function test_loginpage_hook_redirects_if_skipsso_not_set_and_ssourl_set() {
|
|
global $SESSION;
|
|
|
|
$SESSION->enrolkey_skipsso = 0;
|
|
set_config('ssourl', 'http://google.com', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('Unsupported redirect to http://google.com detected, execution terminated.');
|
|
|
|
$this->auth->loginpage_hook();
|
|
}
|
|
|
|
/**
|
|
* Test that login hook does not redirect a user if skipsso not set and ssourl is not set.
|
|
*/
|
|
public function test_loginpage_hook_does_not_redirect_if_skipsso_not_set_and_ssourl_not_set() {
|
|
global $SESSION;
|
|
|
|
$SESSION->enrolkey_skipsso = 0;
|
|
set_config('ssourl', '', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$this->assertTrue($this->auth->loginpage_hook());
|
|
}
|
|
|
|
/**
|
|
* Test that login hook does not redirect a user if skipsso is set and ssourl is not set.
|
|
*/
|
|
public function test_loginpage_hook_does_not_redirect_if_skipsso_set_and_ssourl_not_set() {
|
|
global $SESSION;
|
|
|
|
$SESSION->enrolkey_skipsso = 1;
|
|
set_config('ssourl', '', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$this->assertTrue($this->auth->loginpage_hook());
|
|
}
|
|
|
|
/**
|
|
* Test that pre login hook redirects a user if skipsso not set and ssourl is set.
|
|
*/
|
|
public function test_pre_loginpage_hook_redirects_if_skipsso_not_set_and_ssourl_set() {
|
|
global $SESSION;
|
|
|
|
$SESSION->enrolkey_skipsso = 0;
|
|
set_config('ssourl', 'http://google.com', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('Unsupported redirect to http://google.com detected, execution terminated.');
|
|
|
|
$this->auth->pre_loginpage_hook();
|
|
}
|
|
|
|
/**
|
|
* Test that pre login hook does not redirect a user if skipsso is not set and ssourl is not set.
|
|
*/
|
|
public function test_pre_loginpage_hook_does_not_redirect_if_skipsso_not_set_and_ssourl_not_set() {
|
|
global $SESSION;
|
|
|
|
$SESSION->enrolkey_skipsso = 0;
|
|
set_config('ssourl', '', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$this->assertTrue($this->auth->pre_loginpage_hook());
|
|
}
|
|
|
|
/**
|
|
* Test that login page hook does not redirect a user if skipsso is set and ssourl is not set.
|
|
*/
|
|
public function test_pre_loginpage_hook_does_not_redirect_if_skipsso_set_and_ssourl_not_set() {
|
|
global $SESSION;
|
|
|
|
$SESSION->enrolkey_skipsso = 1;
|
|
set_config('ssourl', '', 'auth_jwt');
|
|
$this->auth = new auth_plugin_jwt();
|
|
|
|
$this->assertTrue($this->auth->pre_loginpage_hook());
|
|
}
|
|
|
|
/**
|
|
* Test that if one user logged, he will be logged out before a new one is authorised.
|
|
*/
|
|
public function test_that_different_authorised_user_is_logged_out_and_new_one_logged_in() {
|
|
global $USER, $SESSION;
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$this->setUser($user);
|
|
$this->assertEquals($USER->id, $user->id);
|
|
|
|
$this->create_user_private_key();
|
|
|
|
$_POST['key'] = 'TestKey';
|
|
|
|
try {
|
|
// Using @ is the only way to test this. Thanks moodle!
|
|
@$this->auth->user_login_jwt();
|
|
} catch (moodle_exception $e) {
|
|
$this->assertEquals($this->user->id, $USER->id);
|
|
$this->assertSame(sesskey(), $USER->sesskey);
|
|
$this->assertObjectHasAttribute('jwt', $SESSION);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test that authorised user gets logged out when trying to logged in with invalid key.
|
|
*/
|
|
public function test_if_invalid_key_authorised_user_gets_logged_out() {
|
|
global $USER, $SESSION;
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$this->setUser($user);
|
|
$this->assertEquals($USER->id, $user->id);
|
|
|
|
$this->create_user_private_key();
|
|
|
|
$_POST['key'] = 'Incorrect Key';
|
|
|
|
try {
|
|
// Using @ is the only way to test this. Thanks moodle!
|
|
@$this->auth->user_login_jwt();
|
|
} catch (moodle_exception $e) {
|
|
$this->assertEquals('Incorrect key', $e->getMessage());
|
|
$this->assertEmpty($USER->id);
|
|
$this->assertEquals(new stdClass(), $SESSION);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test if a user is logged in and tries to log in again it stays logged in.
|
|
*/
|
|
public function test_that_already_logged_in_user_stays_logged_in() {
|
|
global $DB, $USER, $SESSION;
|
|
|
|
$this->setUser($this->user);
|
|
$this->assertEquals($USER->id, $this->user->id);
|
|
|
|
$this->create_user_private_key();
|
|
|
|
$_POST['key'] = 'TestKey';
|
|
|
|
try {
|
|
// Using @ is the only way to test this. Thanks moodle!
|
|
@$this->auth->user_login_jwt();
|
|
} catch (moodle_exception $e) {
|
|
$this->assertEquals($this->user->id, $USER->id);
|
|
$this->assertSame(sesskey(), $USER->sesskey);
|
|
$this->assertObjectNotHasAttribute('jwt', $SESSION);
|
|
$keyexists = $DB->record_exists('user_private_key', array('value' => 'TestKey'));
|
|
$this->assertFalse($keyexists);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test when try to logout, but required return is not set.
|
|
*/
|
|
public function test_user_logout_jwt_when_required_return_not_set() {
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('A required parameter (return) was missing');
|
|
|
|
$this->auth->user_logout_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test when try to logout, but user is not logged in.
|
|
*/
|
|
public function test_user_logout_jwt_when_user_is_not_logged_in() {
|
|
$_POST['return'] = self::REDIRECTION_PATH;
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage(
|
|
sprintf("Unsupported redirect to %s detected, execution terminated.", self::REDIRECTION_PATH)
|
|
);
|
|
|
|
$this->auth->user_logout_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test when try to logout, but user logged in with different auth type.
|
|
*/
|
|
public function test_user_logout_jwt_when_user_logged_in_with_different_auth() {
|
|
global $USER;
|
|
|
|
$_POST['return'] = self::REDIRECTION_PATH;
|
|
|
|
$this->setUser($this->user);
|
|
try {
|
|
$this->auth->user_logout_jwt();
|
|
} catch (moodle_exception $e) {
|
|
$this->assertTrue(isloggedin());
|
|
$this->assertEquals($USER->id, $this->user->id);
|
|
$this->assertEquals(
|
|
'Incorrect logout request',
|
|
$e->getMessage()
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test when try to logout, but user logged in with different auth type.
|
|
*/
|
|
public function test_user_logout_jwt_when_user_logged_in_but_return_not_set() {
|
|
$this->setUser($this->user);
|
|
|
|
$this->expectException(moodle_exception::class);
|
|
$this->expectExceptionMessage('A required parameter (return) was missing');
|
|
|
|
$this->auth->user_logout_jwt();
|
|
}
|
|
|
|
/**
|
|
* Test successful logout.
|
|
*/
|
|
public function test_user_logout_jwt_logging_out() {
|
|
global $USER;
|
|
|
|
$this->setUser($this->user);
|
|
$USER->auth = 'jwt';
|
|
$_POST['return'] = self::REDIRECTION_PATH;
|
|
|
|
try {
|
|
$this->auth->user_logout_jwt();
|
|
} catch (moodle_exception $e) {
|
|
$this->assertFalse(isloggedin());
|
|
$this->assertEquals(
|
|
sprintf('Unsupported redirect to %s detected, execution terminated.', self::REDIRECTION_PATH),
|
|
$e->getMessage()
|
|
);
|
|
}
|
|
}
|
|
}
|