mirror of
https://github.com/moodle/moodle.git
synced 2025-04-13 12:32:08 +02:00
Merge branch 'MDL-73703-master' of https://github.com/davewoloszyn/moodle
This commit is contained in:
commit
fa91b1e12a
@ -668,16 +668,29 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncronizes user fron external LDAP server to moodle user table
|
||||
* Synchronise users from the external LDAP server to Moodle's user table.
|
||||
*
|
||||
* Calls sync_users_update_callback() with default callback if appropriate.
|
||||
*
|
||||
* @param bool $doupdates will do pull in data updates from LDAP if relevant
|
||||
* @return bool success
|
||||
*/
|
||||
public function sync_users($doupdates = true) {
|
||||
return $this->sync_users_update_callback($doupdates ? [$this, 'update_users'] : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronise users from the external LDAP server to Moodle's user table (callback).
|
||||
*
|
||||
* Sync is now using username attribute.
|
||||
*
|
||||
* Syncing users removes or suspends users that dont exists anymore in external LDAP.
|
||||
* Creates new users and updates coursecreator status of users.
|
||||
*
|
||||
* @param bool $do_updates will do pull in data updates from LDAP if relevant
|
||||
* @param callable|null $updatecallback will do pull in data updates from LDAP if relevant
|
||||
* @return bool success
|
||||
*/
|
||||
function sync_users($do_updates=true) {
|
||||
public function sync_users_update_callback(?callable $updatecallback = null): bool {
|
||||
global $CFG, $DB;
|
||||
|
||||
require_once($CFG->dirroot . '/user/profile/lib.php');
|
||||
@ -861,40 +874,24 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
unset($revive_users);
|
||||
}
|
||||
|
||||
|
||||
/// User Updates - time-consuming (optional)
|
||||
if ($do_updates) {
|
||||
// Narrow down what fields we need to update
|
||||
$updatekeys = $this->get_profile_keys();
|
||||
|
||||
} else {
|
||||
print_string('noupdatestobedone', 'auth_ldap');
|
||||
}
|
||||
if ($do_updates and !empty($updatekeys)) { // run updates only if relevant
|
||||
if ($updatecallback && $updatekeys = $this->get_profile_keys()) { // Run updates only if relevant.
|
||||
$users = $DB->get_records_sql('SELECT u.username, u.id
|
||||
FROM {user} u
|
||||
WHERE u.deleted = 0 AND u.auth = ? AND u.mnethostid = ?',
|
||||
array($this->authtype, $CFG->mnet_localhost_id));
|
||||
if (!empty($users)) {
|
||||
print_string('userentriestoupdate', 'auth_ldap', count($users));
|
||||
|
||||
foreach ($users as $user) {
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
echo "\t"; print_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id));
|
||||
$userinfo = $this->get_userinfo($user->username);
|
||||
if (!$this->update_user_record($user->username, $updatekeys, true,
|
||||
$this->is_user_suspended((object) $userinfo))) {
|
||||
echo ' - '.get_string('skipped');
|
||||
// Update users in chunks as specified in sync_updateuserchunk.
|
||||
if (!empty($this->config->sync_updateuserchunk)) {
|
||||
foreach (array_chunk($users, $this->config->sync_updateuserchunk) as $chunk) {
|
||||
call_user_func($updatecallback, $chunk, $updatekeys);
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// Update system roles, if needed.
|
||||
$this->sync_roles($user);
|
||||
$transaction->allow_commit();
|
||||
} else {
|
||||
call_user_func($updatecallback, $users, $updatekeys);
|
||||
}
|
||||
unset($users); // free mem
|
||||
unset($users); // Free mem.
|
||||
}
|
||||
} else { // end do updates
|
||||
} else {
|
||||
print_string('noupdatestobedone', 'auth_ldap');
|
||||
}
|
||||
|
||||
@ -974,6 +971,36 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update users from the external LDAP server into Moodle's user table.
|
||||
*
|
||||
* Sync helper
|
||||
*
|
||||
* @param array $users chunk of users to update
|
||||
* @param array $updatekeys fields to update
|
||||
*/
|
||||
public function update_users(array $users, array $updatekeys): void {
|
||||
global $DB;
|
||||
|
||||
print_string('userentriestoupdate', 'auth_ldap', count($users));
|
||||
|
||||
foreach ($users as $user) {
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
echo "\t";
|
||||
print_string('auth_dbupdatinguser', 'auth_db', ['name' => $user->username, 'id' => $user->id]);
|
||||
$userinfo = $this->get_userinfo($user->username);
|
||||
if (!$this->update_user_record($user->username, $updatekeys, true,
|
||||
$this->is_user_suspended((object) $userinfo))) {
|
||||
echo ' - '.get_string('skipped');
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// Update system roles, if needed.
|
||||
$this->sync_roles($user);
|
||||
$transaction->allow_commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk insert in SQL's temp table
|
||||
*/
|
||||
|
61
auth/ldap/classes/task/asynchronous_sync_task.php
Normal file
61
auth/ldap/classes/task/asynchronous_sync_task.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Adhoc task for LDAP user sync.
|
||||
*
|
||||
* @package auth_ldap
|
||||
* @copyright Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace auth_ldap\task;
|
||||
|
||||
use core\task\adhoc_task;
|
||||
|
||||
/**
|
||||
* Adhoc task class for LDAP user sync.
|
||||
*
|
||||
* @package auth_ldap
|
||||
* @copyright Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class asynchronous_sync_task extends adhoc_task {
|
||||
|
||||
/** @var string Message prefix for mtrace */
|
||||
protected const MTRACE_MSG = 'Synced ldap users';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->set_blocking(false);
|
||||
$this->set_component('auth_ldap');
|
||||
}
|
||||
|
||||
/**
|
||||
* Run users sync.
|
||||
*/
|
||||
public function execute() {
|
||||
$data = $this->get_custom_data();
|
||||
|
||||
/** @var auth_plugin_ldap $auth */
|
||||
$auth = get_auth_plugin('ldap');
|
||||
$auth->update_users($data->users, $data->updatekeys);
|
||||
|
||||
mtrace(sprintf(" %s (%d)", self::MTRACE_MSG, count($data->users)));
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
* @copyright 2015 Vadim Dvorovenko <Vadimon@mail.ru>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace auth_ldap\task;
|
||||
|
||||
/**
|
||||
@ -31,6 +32,9 @@ namespace auth_ldap\task;
|
||||
*/
|
||||
class sync_task extends \core\task\scheduled_task {
|
||||
|
||||
/** @var string Message prefix for mtrace */
|
||||
protected const MTRACE_MSG = 'Synced ldap users';
|
||||
|
||||
/**
|
||||
* Get a descriptive name for this task (shown to admins).
|
||||
*
|
||||
@ -44,11 +48,22 @@ class sync_task extends \core\task\scheduled_task {
|
||||
* Run users sync.
|
||||
*/
|
||||
public function execute() {
|
||||
global $CFG;
|
||||
if (is_enabled_auth('ldap')) {
|
||||
/** @var auth_plugin_ldap $auth */
|
||||
$auth = get_auth_plugin('ldap');
|
||||
$auth->sync_users(true);
|
||||
$count = 0;
|
||||
$auth->sync_users_update_callback(function ($users, $updatekeys) use (&$count) {
|
||||
$asynctask = new asynchronous_sync_task();
|
||||
$asynctask->set_custom_data([
|
||||
'users' => $users,
|
||||
'updatekeys' => $updatekeys,
|
||||
]);
|
||||
\core\task\manager::queue_adhoc_task($asynctask);
|
||||
|
||||
$count++;
|
||||
mtrace(sprintf(" %s (%d)", self::MTRACE_MSG, $count));
|
||||
sleep(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -142,6 +142,8 @@ $string['renamingnotallowed'] = 'User renaming not allowed in LDAP';
|
||||
$string['rootdseerror'] = 'Error querying rootDSE for Active Directory';
|
||||
$string['syncroles'] = 'Synchronise system roles from LDAP';
|
||||
$string['synctask'] = 'LDAP users sync job';
|
||||
$string['sync_updateuserchunk'] = 'Set this value to the number of users you want updated per transaction. Setting this to 0 will update all users in one transaction.';
|
||||
$string['sync_updateuserchunk_key'] = 'Sync update users chunk size';
|
||||
$string['systemrolemapping'] = 'System role mapping';
|
||||
$string['start_tls'] = 'Use regular LDAP service (port 389) with TLS encryption';
|
||||
$string['start_tls_key'] = 'Use TLS';
|
||||
|
@ -289,6 +289,11 @@ if ($ADMIN->fulltree) {
|
||||
new lang_string('auth_sync_suspended_key', 'auth'),
|
||||
new lang_string('auth_sync_suspended', 'auth'), 0 , $yesno));
|
||||
|
||||
// Sync update users chunk size.
|
||||
$settings->add(new admin_setting_configtext('auth_ldap/sync_updateuserchunk',
|
||||
new lang_string('sync_updateuserchunk_key', 'auth_ldap'),
|
||||
new lang_string('sync_updateuserchunk', 'auth_ldap'), 1000, PARAM_INT));
|
||||
|
||||
// NTLM SSO Header.
|
||||
$settings->add(new admin_setting_heading('auth_ldap/ntlm',
|
||||
new lang_string('auth_ntlmsso', 'auth_ldap'), ''));
|
||||
|
@ -29,11 +29,32 @@ namespace auth_ldap;
|
||||
* define('TEST_AUTH_LDAP_DOMAIN', 'dc=example,dc=local');
|
||||
*
|
||||
* @package auth_ldap
|
||||
* @category phpunit
|
||||
* @copyright 2013 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class plugin_test extends \advanced_testcase {
|
||||
|
||||
use auth_plugin_ldap;
|
||||
use auth_ldap\task\{
|
||||
sync_task,
|
||||
asynchronous_sync_task,
|
||||
};
|
||||
|
||||
/**
|
||||
* LDAP authentication plugin tests.
|
||||
*
|
||||
* @package auth_ldap
|
||||
* @copyright 2013 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class auth_ldap_test extends \advanced_testcase {
|
||||
|
||||
public static function setUpBeforeClass(): void {
|
||||
global $CFG;
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
require_once($CFG->dirroot . '/auth/ldap/auth.php');
|
||||
require_once($CFG->libdir . '/ldaplib.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for auth_ldap tests
|
||||
@ -65,7 +86,7 @@ class plugin_test extends \advanced_testcase {
|
||||
* @param int $subcontext Value to be configured in settings controlling searching in subcontexts.
|
||||
*/
|
||||
public function test_auth_ldap(int $pagesize, int $subcontext) {
|
||||
global $CFG, $DB;
|
||||
global $DB;
|
||||
|
||||
if (!extension_loaded('ldap')) {
|
||||
$this->markTestSkipped('LDAP extension is not loaded.');
|
||||
@ -73,9 +94,6 @@ class plugin_test extends \advanced_testcase {
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
require_once($CFG->dirroot.'/auth/ldap/auth.php');
|
||||
require_once($CFG->libdir.'/ldaplib.php');
|
||||
|
||||
if (!defined('TEST_AUTH_LDAP_HOST_URL') or !defined('TEST_AUTH_LDAP_BIND_DN') or !defined('TEST_AUTH_LDAP_BIND_PW') or !defined('TEST_AUTH_LDAP_DOMAIN')) {
|
||||
$this->markTestSkipped('External LDAP test server not configured.');
|
||||
}
|
||||
@ -336,6 +354,41 @@ class plugin_test extends \advanced_testcase {
|
||||
$this->assertEquals(2, $DB->count_records('role_assignments'));
|
||||
$this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
|
||||
|
||||
// Let's test syncing users in chunks of '1'.
|
||||
set_config('field_updatelocal_email', 'onlogin', 'auth_ldap');
|
||||
set_config('sync_updateuserchunk', 1, 'auth_ldap');
|
||||
|
||||
/** @var auth_plugin_ldap $auth */
|
||||
$auth = get_auth_plugin('ldap');
|
||||
|
||||
$count = 0;
|
||||
ob_start();
|
||||
$auth->sync_users_update_callback(function ($users, $updatekeys) use (&$count) {
|
||||
$count++;
|
||||
});
|
||||
ob_end_clean();
|
||||
// After updating in chunks of '1', we should have counted more than one update.
|
||||
$this->assertGreaterThan(1, $count);
|
||||
|
||||
ob_start();
|
||||
\core\cron::setup_user();
|
||||
$cron = new sync_task();
|
||||
$cron->execute();
|
||||
|
||||
$this->runAdhocTasks('\auth_ldap\task\asynchronous_sync_task');
|
||||
$output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// Use Reflection to make protected constants available.
|
||||
$rp = new \ReflectionClassConstant(sync_task::class, 'MTRACE_MSG');
|
||||
$synctaskmsg = $rp->getValue();
|
||||
$rp = new \ReflectionClassConstant(asynchronous_sync_task::class, 'MTRACE_MSG');
|
||||
$asynctaskmsg = $rp->getValue();
|
||||
|
||||
$this->assertMatchesRegularExpression(
|
||||
sprintf('/%s.*%s/s', $synctaskmsg, $asynctaskmsg),
|
||||
$output
|
||||
);
|
||||
|
||||
$this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
|
||||
ldap_close($connection);
|
||||
@ -347,8 +400,6 @@ class plugin_test extends \advanced_testcase {
|
||||
public function test_ldap_user_loggedin_event() {
|
||||
global $CFG, $DB, $USER;
|
||||
|
||||
require_once($CFG->dirroot . '/auth/ldap/auth.php');
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$this->assertFalse(isloggedin());
|
||||
@ -412,9 +463,6 @@ class plugin_test extends \advanced_testcase {
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
require_once($CFG->dirroot.'/auth/ldap/auth.php');
|
||||
require_once($CFG->libdir.'/ldaplib.php');
|
||||
|
||||
if (!defined('TEST_AUTH_LDAP_HOST_URL') or !defined('TEST_AUTH_LDAP_BIND_DN') or !defined('TEST_AUTH_LDAP_BIND_PW') or !defined('TEST_AUTH_LDAP_DOMAIN')) {
|
||||
$this->markTestSkipped('External LDAP test server not configured.');
|
||||
}
|
@ -25,6 +25,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2023100900; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2024011900; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2023100400; // Requires this Moodle version.
|
||||
$plugin->component = 'auth_ldap'; // Full name of the plugin (used for diagnostics)
|
||||
|
Loading…
x
Reference in New Issue
Block a user