Skip to content

Commit

Permalink
MDL-73431 enrol: Introduce a sync interval
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitriim committed May 30, 2022
1 parent 117b240 commit a6cad23
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 7 deletions.
11 changes: 11 additions & 0 deletions config-dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,17 @@
// Settings this to anonymous will enable CORS requests for media elements to have the credentials
// flag set to 'same-origin'. This may be needed when using tool_objectfs as an alternative file
// system with CloudFront configured.
//
// Enrolments sync interval
//
// The minimum time in seconds between re-synchronization of enrollment via enrol_check_plugins which is
// a potentially expensive operation and otherwise happens every time a user is authenticated. This only
// applies to web requests without a session such as webservice calls, tokenpluginfile.php and rss links
// where the user is re-authenticated on every request. Set it to 0 to force enrollment checking constantly
// and increase this number to improve performance at the cost of adding a latency for enrollment updates.
// Defaults to 60 minutes.
//
// $CFG->enrolments_sync_interval = 3600

//=========================================================================
// 7. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
Expand Down
93 changes: 93 additions & 0 deletions enrol/tests/enrollib_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -1488,4 +1488,97 @@ public function get_enrolled_with_capabilities_join_cannotmatchanyrows_data() {
],
];
}

/**
* Test last_time_enrolments_synced not recorded with "force" option for enrol_check_plugins.
* @covers ::enrol_check_plugins
*/
public function test_enrol_check_plugins_with_forced_option() {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();

$this->assertNull(get_user_preferences('last_time_enrolments_synced', null, $user));
enrol_check_plugins($user);
$this->assertNull(get_user_preferences('last_time_enrolments_synced', null, $user));
}

/**
* Data provided for test_enrol_check_plugins_with_empty_config_value test.
* @return array
*/
public function empty_config_data_provider(): array {
return [
[0],
["0"],
[false],
[''],
['string'],
];
}

/**
* Test that empty 'enrolments_sync_interval' is treated as forced option for enrol_check_plugins.
*
* @dataProvider empty_config_data_provider
* @covers ::enrol_check_plugins
*
* @param mixed $config Config value.
*/
public function test_enrol_check_plugins_with_empty_config_value($config) {
global $CFG;

$this->resetAfterTest();
$CFG->enrolments_sync_interval = $config;
$user = $this->getDataGenerator()->create_user();

$this->assertNull(get_user_preferences('last_time_enrolments_synced', null, $user));
enrol_check_plugins($user, false);
$this->assertNull(get_user_preferences('last_time_enrolments_synced', null, $user));
}

/**
* Test last_time_enrolments_synced is recorded without "force" option for enrol_check_plugins.
* @covers ::enrol_check_plugins
*/
public function test_last_time_enrolments_synced_is_set_if_not_forced() {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();

$this->assertNull(get_user_preferences('last_time_enrolments_synced', null, $user));

enrol_check_plugins($user, false);
$firstrun = get_user_preferences('last_time_enrolments_synced', null, $user);
$this->assertNotNull($firstrun);
sleep(1);

enrol_check_plugins($user, false);
$secondrun = get_user_preferences('last_time_enrolments_synced', null, $user);
$this->assertNotNull($secondrun);
$this->assertTrue((int)$secondrun == (int)$firstrun);
}

/**
* Test last_time_enrolments_synced is recorded correctly without "force" option for enrol_check_plugins.
* @covers ::enrol_check_plugins
*/
public function test_last_time_enrolments_synced_is_set_if_not_forced_if_have_not_passed_interval() {
global $CFG;

$this->resetAfterTest();
$CFG->enrolments_sync_interval = 1;
$user = $this->getDataGenerator()->create_user();

$this->assertNull(get_user_preferences('last_time_enrolments_synced', null, $user));

enrol_check_plugins($user, false);
$firstrun = get_user_preferences('last_time_enrolments_synced', null, $user);
$this->assertNotNull($firstrun);
sleep(2);

enrol_check_plugins($user, false);
$secondrun = get_user_preferences('last_time_enrolments_synced', null, $user);
$this->assertNotNull($secondrun);
$this->assertTrue((int)$secondrun > (int)$firstrun);
}

}
21 changes: 18 additions & 3 deletions lib/enrollib.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,14 @@ function enrol_is_enabled($enrol) {
/**
* Check all the login enrolment information for the given user object
* by querying the enrolment plugins
*
* This function may be very slow, use only once after log-in or login-as.
*
* @param stdClass $user
* @param stdClass $user User object.
* @param bool $ignoreintervalcheck Force to ignore checking configured sync intervals.
*
* @return void
*/
function enrol_check_plugins($user) {
function enrol_check_plugins($user, bool $ignoreintervalcheck = true) {
global $CFG;

if (empty($user->id) or isguestuser($user)) {
Expand All @@ -237,6 +238,16 @@ function enrol_check_plugins($user) {
return;
}

$syncinterval = isset($CFG->enrolments_sync_interval) ? (int)$CFG->enrolments_sync_interval : HOURSECS;
$needintervalchecking = !$ignoreintervalcheck && !empty($syncinterval);

if ($needintervalchecking) {
$lastsync = get_user_preferences('last_time_enrolments_synced', 0, $user);
if (time() - $lastsync < $syncinterval) {
return;
}
}

$inprogress[$user->id] = true; // Set the flag

$enabled = enrol_get_plugins(true);
Expand All @@ -245,6 +256,10 @@ function enrol_check_plugins($user) {
$enrol->sync_user_enrolments($user);
}

if ($needintervalchecking) {
set_user_preference('last_time_enrolments_synced', time(), $user);
}

unset($inprogress[$user->id]); // Unset the flag
}

Expand Down
2 changes: 1 addition & 1 deletion lib/moodlelib.php
Original file line number Diff line number Diff line change
Expand Up @@ -3291,7 +3291,7 @@ function require_user_key_login($script, $instance = null, $keyvalue = null) {
core_user::require_active_user($user, true, true);

// Emulate normal session.
enrol_check_plugins($user);
enrol_check_plugins($user, false);
\core\session\manager::set_user($user);

// Note we are not using normal login.
Expand Down
6 changes: 6 additions & 0 deletions lib/upgrade.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
This files describes API changes in core libraries and APIs,
information provided here is intended especially for developers.

=== 4.1 ===

* New setting $CFG->enrolments_sync_interval controls the minimum time in seconds between re-synchronization of enrollment via enrol_check_plugins.
This only applies to web requests without a session such as webservice calls, tokenpluginfile.php and rss links Function
enrol_check_plugins now has $force flag to bypass checking for that setting.

=== 4.0 ===

* To better detect wrong floats (like, for example, unformatted, using local-dependent separators ones) a number of
Expand Down
2 changes: 1 addition & 1 deletion rss/file.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
$user = get_complete_user_data('id', $userid);

// Let enrol plugins deal with new enrolments if necessary.
enrol_check_plugins($user);
enrol_check_plugins($user, false);

\core\session\manager::set_user($user); // For login and capability checks.

Expand Down
4 changes: 2 additions & 2 deletions webservice/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public function authenticate_user($token) {
$user = $DB->get_record('user', array('id' => $token->userid, 'deleted' => 0), '*', MUST_EXIST);

// let enrol plugins deal with new enrolments if necessary
enrol_check_plugins($user);
enrol_check_plugins($user, false);

// setup user session to check capability
\core\session\manager::set_user($user);
Expand Down Expand Up @@ -1131,7 +1131,7 @@ protected function authenticate_user() {
}

// now fake user login, the session is completely empty too
enrol_check_plugins($user);
enrol_check_plugins($user, false);
\core\session\manager::set_user($user);
set_login_session_preferences();
$this->userid = $user->id;
Expand Down

0 comments on commit a6cad23

Please sign in to comment.