Skip to content

Commit

Permalink
Merge branch 'wip-MSFTMPP-426-m28' into MOODLE_28_STABLE
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesmcq committed Mar 29, 2016
2 parents 0ae8883 + ab4194f commit 65078e7
Show file tree
Hide file tree
Showing 12 changed files with 184 additions and 28 deletions.
2 changes: 1 addition & 1 deletion classes/feature/calsync/task/importfromoutlook.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public function execute() {
mtrace($errmsg);
}
} catch (\Exception $e) {
\local_o365\utils::debug('Error syncing events', 'importfromoutlook', $e->getMessage());
\local_o365\utils::debug('Error syncing events: '.$e->getMessage(), 'importfromoutlook', $e);
mtrace('Error: '.$e->getMessage());
}
}
Expand Down
2 changes: 1 addition & 1 deletion classes/feature/calsync/task/syncoldevents.php
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ protected function sync_userevents($userid, $timecreated) {
$usertoken = $calsync->get_user_token($userid);
if (empty($usertoken)) {
// No token, can't sync.
\local_o365\utils::debug('Could not get user token for calendar sync.');
\local_o365\utils::debug('Could not get user token for calendar sync.', 'sync_userevents');
return false;
}

Expand Down
92 changes: 92 additions & 0 deletions classes/healthcheck/ratelimit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?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/>.

/**
* @package local_o365
* @author James McQuillan <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @copyright (C) 2014 onwards Microsoft Open Technologies, Inc. (http://msopentech.com/)
*/

namespace local_o365\healthcheck;

/**
* Checks current recorded rate limit
*/
class ratelimit implements \local_o365\healthcheck\healthcheckinterface {
/**
* Run the health check.
*
* @return array Array of result data. Must include:
* bool result Whether the health check passed or not.
* int severity If the health check failed, how bad a problem is it? This is one of the SEVERITY_* constants.
* string message A message to show the user.
* string fixlink If the healthcheck failed, a link to help resolve the problem.
*/
public function run() {
$ratelimitdisabled = get_config('local_o365', 'ratelimitdisabled');
if (!empty($ratelimitdisabled)) {
return [
'result' => false,
'severity' => static::SEVERITY_TRIVIAL,
'message' => get_string('healthcheck_ratelimit_result_disabled', 'local_o365'),
];
}

$ratelimit = get_config('local_o365', 'ratelimit');
$ratelimit = explode(':', $ratelimit, 2);

if (!empty($ratelimit[0]) && $ratelimit[1] > (time() - (10 * MINSECS))) {
$a = new \stdClass;
$a->level = $ratelimit[0];
$a->timestart = date('c', $ratelimit[1]);
if ($ratelimit[0] < 4) {
return [
'result' => false,
'severity' => static::SEVERITY_TRIVIAL,
'message' => get_string('healthcheck_ratelimit_result_notice', 'local_o365', $a),
];
} else {
return [
'result' => false,
'severity' => static::SEVERITY_TRIVIAL,
'message' => get_string('healthcheck_ratelimit_result_warning', 'local_o365', $a),
];
}
} else {
return [
'result' => true,
'severity' => static::SEVERITY_OK,
'message' => get_string('healthcheck_ratelimit_result_passed', 'local_o365'),
];
}

if (empty($systemtoken)) {

} else {

}
}

/**
* Get a human-readable name for the health check.
*
* @return string A name for the health check.
*/
public function get_name() {
return get_string('healthcheck_ratelimit_title', 'local_o365');
}
}
10 changes: 5 additions & 5 deletions classes/observers.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public static function handle_oidc_user_connected(\auth_oidc\event\user_connecte
$token = \local_o365\oauth2\token::instance($eventdata['userid'], $azureresource, $clientdata, $httpclient);
return true;
} catch (\Exception $e) {
\local_o365\utils::debug($e->getMessage());
\local_o365\utils::debug($e->getMessage(), 'handle_oidc_user_connected', $e);
return false;
}
}
Expand Down Expand Up @@ -205,7 +205,7 @@ public static function get_additional_user_info($userid, $eventtype) {
}
return true;
} catch (\Exception $e) {
\local_o365\utils::debug($e->getMessage());
\local_o365\utils::debug($e->getMessage(), 'get_additional_user_info', $e);
}
return false;
}
Expand Down Expand Up @@ -240,7 +240,7 @@ public static function handle_user_enrolment_created(\core\event\user_enrolment_
return true;
}
} catch (\Exception $e) {
\local_o365\utils::debug($e->getMessage());
\local_o365\utils::debug($e->getMessage(), 'handle_user_enrolment_created', $e);
}
return false;
}
Expand Down Expand Up @@ -280,7 +280,7 @@ public static function handle_user_enrolment_deleted(\core\event\user_enrolment_
return true;
}
} catch (\Exception $e) {
\local_o365\utils::debug($e->getMessage());
\local_o365\utils::debug($e->getMessage(), 'handle_user_enrolment_deleted', $e);
}
return false;
}
Expand All @@ -303,7 +303,7 @@ public static function construct_sharepoint_api_with_system_user() {
}
}
} catch (\Exception $e) {
\local_o365\utils::debug($e->getMessage(), get_called_class());
\local_o365\utils::debug($e->getMessage(), get_called_class(), $e);
}
return false;
}
Expand Down
17 changes: 13 additions & 4 deletions classes/page/acp.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,31 @@ public function mode_healthcheck() {
echo \html_writer::tag('h2', get_string('acp_healthcheck', 'local_o365'));
echo '<br />';

$healthchecks = ['systemapiuser'];
$healthchecks = ['systemapiuser', 'ratelimit'];
foreach ($healthchecks as $healthcheck) {
$healthcheckclass = '\local_o365\healthcheck\\'.$healthcheck;
$healthcheck = new $healthcheckclass();
$result = $healthcheck->run();

echo '<h5>'.$healthcheck->get_name().'</h5>';
if ($result['result'] === true) {
echo '<div class="alert alert-success">'.$result['message'].'</div>';
echo '<div class="alert alert-success">'.$result['message'].'</div><br />';
} else {
echo '<div class="alert alert-error">';
switch ($result['severity']) {
case \local_o365\healthcheck\healthcheckinterface::SEVERITY_TRIVIAL:
$severityclass = 'alert-info';
break;

default:
$severityclass = 'alert-error';
}

echo '<div class="alert '.$severityclass.'">';
echo $result['message'];
if (isset($result['fixlink'])) {
echo '<br /><br />'.\html_writer::link($result['fixlink'], get_string('healthcheck_fixlink', 'local_o365'));
}
echo '</div>';
echo '</div><br />';
}
}

Expand Down
8 changes: 4 additions & 4 deletions classes/page/ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public function mode_detectserviceresource() {
die();
}
} catch (\Exception $e) {
\local_o365\utils::debug($e->getMessage(), 'detect aadtenant');
\local_o365\utils::debug($e->getMessage(), 'detect aadtenant', $e);
echo $this->error_response(get_string('settings_serviceresourceabstract_noperms', 'local_o365'));
die();
}
Expand All @@ -179,7 +179,7 @@ public function mode_detectserviceresource() {
die();
}
} catch (\Exception $e) {
\local_o365\utils::debug($e->getMessage(), 'detect odburl');
\local_o365\utils::debug($e->getMessage(), 'detect odburl', $e);
echo $this->error_response(get_string('settings_serviceresourceabstract_noperms', 'local_o365'));
die();
}
Expand Down Expand Up @@ -258,7 +258,7 @@ public function mode_checksetup() {
$legacyapi->missingperms = $missingperms;
$legacyapi->haswrite = $haswrite;
} catch (\Exception $e) {
\local_o365\utils::debug($e->getMessage(), 'mode_checksetup:legacy');
\local_o365\utils::debug($e->getMessage(), 'mode_checksetup:legacy', $e);
$legacyapi->error = $e->getMessage();
}
$data->legacyapi = $legacyapi;
Expand All @@ -284,7 +284,7 @@ public function mode_checksetup() {
}
} catch (\Exception $e) {
$unifiedapi->active = false;
\local_o365\utils::debug($e->getMessage(), 'mode_checksetup:unified');
\local_o365\utils::debug($e->getMessage(), 'mode_checksetup:unified', $e);
$unifiedapi->error = $e->getMessage();
}
}
Expand Down
2 changes: 1 addition & 1 deletion classes/rest/azuread.php
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ public static function get_muser_upn($user) {
try {
$clientdata = \local_o365\oauth2\clientdata::instance_from_oidc();
} catch (\Exception $e) {
\local_o365\utils::debug($e->getMessage());
\local_o365\utils::debug($e->getMessage(), 'rest\azuread\get_muser_upn', $e);
return false;
}
$resource = static::get_resource();
Expand Down
49 changes: 41 additions & 8 deletions classes/rest/o365api.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ abstract class o365api {
/** @var \local_o365\httpclientinterface An HTTP client to use for communication. */
protected $httpclient;

/** @var bool Disable rate limit protection. */
protected $disableratelimit = false;

/**
* Constructor.
*
Expand Down Expand Up @@ -135,6 +132,14 @@ protected function transform_full_request_uri($requesturi) {
* @return string The result of the API call.
*/
public function apicall($httpmethod, $apimethod, $params = '', $options = array()) {
// Used if we have to retry due to rate limiting.
$origparam = [
'httpmethod' => $httpmethod,
'apimethod' => $apimethod,
'params' => $params,
'options' => $options,
];

$tokenvalid = $this->checktoken();
if ($tokenvalid !== true) {
throw new \moodle_exception('erroro365apiinvalidtoken', 'local_o365');
Expand Down Expand Up @@ -168,14 +173,42 @@ public function apicall($httpmethod, $apimethod, $params = '', $options = array(
$this->httpclient->resetHeader();
$this->httpclient->setHeader($header);

// Sleep to avoid rate limiting.
if (empty($this->disableratelimit)) {
usleep(1050000);
// Check if we were rate limited in the last 10 minutes.
$ratelimitlevel = 0;
$ratelimittime = 0;
$ratelimit = get_config('local_o365', 'ratelimit');
$ratelimitdisabled = get_config('local_o365', 'ratelimitdisabled');
if (empty($ratelimitdisabled)) {
if (!empty($ratelimit)) {
$ratelimit = explode(':', $ratelimit, 2);
if ($ratelimit[1] > (time() - (10 * MINSECS))) {
// Rate limiting enabled.
$ratelimittime = $ratelimit[1];
$ratelimitlevel = $ratelimit[0];
if ($ratelimitlevel >= 4) {
$ratelimitlevel = 4;
}
}
}
}

// Throttle if enabled.
if (!empty($ratelimitlevel)) {
usleep((260000 * $ratelimitlevel));
} else {
usleep(250000);
// Small sleep to help prevent throttling in the first place.
usleep(100000);
}

return $this->httpclient->$httpmethod($requesturi, $params, $options);
$result = $this->httpclient->$httpmethod($requesturi, $params, $options);
if ($this->httpclient->info['http_code'] == 429) {
// We are being throttled.
$ratelimitlevel++;
set_config('ratelimit', $ratelimitlevel.':'.time(), 'local_o365');
return $this->apicall($origparam['httpmethod'], $origparam['apimethod'], $origparam['params'], $origparam['options']);
} else {
return $result;
}
}

/**
Expand Down
2 changes: 0 additions & 2 deletions classes/rest/unified.php
Original file line number Diff line number Diff line change
Expand Up @@ -685,11 +685,9 @@ public function get_required_permissions() {
*/
public function check_permissions() {
$this->token->refresh();
$this->disableratelimit = true;
$currentperms = $this->get_unified_api_permissions();
$neededperms = $this->get_required_permissions();
$availableperms = $this->get_available_permissions();
$this->disableratelimit = false;

if ($currentperms === null || $availableperms === null) {
return null;
Expand Down
18 changes: 18 additions & 0 deletions classes/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ public static function is_o365_connected($userid) {
}
}

/**
* Convert any value into a debuggable string.
*
* @param mixed $val The variable to convert.
* @return string A string representation.
*/
public static function tostring($val) {
if (is_scalar($val)) {
if (is_bool($val)) {
Expand All @@ -132,6 +138,18 @@ public static function tostring($val) {
}
} else if (is_null($val)) {
return '(null)';
} else if ($val instanceof \Exception) {
$valinfo = [
'file' => $val->getFile(),
'line' => $val->getLine(),
'message' => $val->getMessage(),
];
if ($val instanceof \moodle_exception) {
$valinfo['debuginfo'] = $val->debuginfo;
$valinfo['errorcode'] = $val->errorcode;
$valinfo['module'] = $val->module;
}
return print_r($valinfo, true);
} else {
return print_r($val, true);
}
Expand Down
7 changes: 6 additions & 1 deletion lang/en/local_o365.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
$string['healthcheck_systemtoken_result_noclientcreds'] = 'There are not application credentials present in the OpenID Connect plugin. Without these credentials, Moodle cannot perform any communication with Office&nbsp;365. Click here to visit the settings page and enter your credentials.';
$string['healthcheck_systemtoken_result_badtoken'] = 'There was a problem communicating with Office&nbsp;365 as the system API user. This can usually be resolved by resetting the system API user.';
$string['healthcheck_systemtoken_result_passed'] = 'Moodle can communicate with Office&nbsp;365 as the system API user.';
$string['healthcheck_ratelimit_title'] = 'API Throttling';
$string['healthcheck_ratelimit_result_notice'] = 'Slight throttling has been enabled to handle increased Moodle site load. <br /><br />All Office 365 features are functional, this just spaces out requests slightly to prevent interruption of Office 365 services. Once Moodle activity decreases, everything will return to normal. <br />(Level {$a->level} / started {$a->timestart})';
$string['healthcheck_ratelimit_result_warning'] = 'Increased throttling has been enabled to handle significant Moodle site load. <br /><br />All Office 365 features are still functional, but Office 365 requests may take longer to complete. Once Moodle site activity has decreased, everything will return to normal. <br />(Level {$a->level} / started {$a->timestart})';
$string['healthcheck_ratelimit_result_disabled'] = 'Rate limiting features have been disabled.';
$string['healthcheck_ratelimit_result_passed'] = 'Office 365 API calls are executing at full speed.';

$string['settings_aadsync'] = 'Sync users with Azure AD';
$string['settings_aadsync_details'] = 'When enabled, Moodle and Azure AD users are synced according to the above options.<br /><br /><b>Note: </b>The sync job runs in the Moodle cron, and syncs 1000 users at a time. By default, this runs once per day at 1:00 AM in the time zone local to your server. To sync large sets of users more quickly, you can increase the freqency of the <b>Sync users with Azure AD</b> task using the <a href="{$a}">Scheduled tasks management page.</a><br /><br />For more detailed instructions, see the <a href="https://docs.moodle.org/28/en/Office365#User_sync">user sync documentation</a>.<br /><br />';
Expand Down Expand Up @@ -140,7 +145,7 @@
$string['settings_o365china'] = 'Office&nbsp;365 for China';
$string['settings_o365china_details'] = 'Check this if you are using Office&nbsp;365 for China.';
$string['settings_debugmode'] = 'Record debug messages';
$string['settings_debugmode_details'] = 'If enabled, information will be logged to the Moodle log that can help in identifying problems.';
$string['settings_debugmode_details'] = 'If enabled, information will be logged to the Moodle log that can help in identifying problems. <a href="{$a}">View recorded log messages.</a>';
$string['settings_detectoidc'] = 'Application Credentials';
$string['settings_detectoidc_details'] = 'To communicate with Office&nbsp;365, Moodle needs credentials to identify itself. These are set in the "OpenID Connect" authentication plugin.';
$string['settings_detectoidc_credsvalid'] = 'Credentials have been set.';
Expand Down
3 changes: 2 additions & 1 deletion settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@
$settings->add(new \admin_setting_configcheckbox('local_o365/creategroups', $label, $desc, '0'));

$label = get_string('settings_debugmode', 'local_o365');
$desc = get_string('settings_debugmode_details', 'local_o365');
$logurl = new \moodle_url('/report/log/index.php', ['chooselog' => '1', 'modid' => 'site_errors']);
$desc = get_string('settings_debugmode_details', 'local_o365', $logurl->out());
$settings->add(new \admin_setting_configcheckbox('local_o365/debugmode', $label, $desc, '0'));

$label = get_string('settings_photoexpire', 'local_o365');
Expand Down

0 comments on commit 65078e7

Please sign in to comment.