MDL-58090 oauth2: Store a list of oauth2 services

Build an admin page where OAuth 2 services can be installed and configured.

Part of MDL-58220
This commit is contained in:
Damyon Wiese
2017-04-03 13:39:02 +08:00
parent bf919ddf02
commit 60237253a2
34 changed files with 2512 additions and 22 deletions
+11
View File
@@ -30,6 +30,17 @@
require_once(__DIR__ . '/../config.php');
$error = optional_param('error', '', PARAM_RAW);
if ($error) {
$message = optional_param('error_description', '', PARAM_RAW);
if ($message) {
print_error($message);
} else {
print_error($error);
}
die();
}
// The authorization code generated by the authorization server.
$code = required_param('code', PARAM_RAW);
// The state parameter we've given (used in moodle as a redirect url).
+107
View File
@@ -0,0 +1,107 @@
<?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/>.
/**
* This file contains the form add/update oauth2 issuer.
*
* @package tool_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_oauth2\form;
defined('MOODLE_INTERNAL') || die();
use stdClass;
use core\form\persistent;
/**
* Issuer form.
*
* @package tool_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class issuer extends persistent {
protected static $persistentclass = 'core\\oauth2\\issuer';
protected static $fieldstoremove = array('submitbutton', 'action');
/**
* Define the form - called by parent constructor
*/
public function definition() {
global $PAGE;
$mform = $this->_form;
$provider = $this->get_persistent();
$mform->addElement('header', 'generalhdr', get_string('general'));
// Name.
$mform->addElement('text', 'name', get_string('issuername', 'tool_oauth2'), 'maxlength="255"');
$mform->addRule('name', null, 'required', null, 'client');
$mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('name', 'issuername', 'tool_oauth2');
// Client ID.
$mform->addElement('text', 'clientid', get_string('issuerclientid', 'tool_oauth2'), 'maxlength="255"');
$mform->addRule('clientid', null, 'required', null, 'client');
$mform->addRule('clientid', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('clientid', 'issuerclientid', 'tool_oauth2');
// Client Secret.
$mform->addElement('text', 'clientsecret', get_string('issuerclientsecret', 'tool_oauth2'), 'maxlength="255"');
$mform->addRule('clientsecret', null, 'required', null, 'client');
$mform->addRule('clientsecret', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('clientsecret', 'issuerclientsecret', 'tool_oauth2');
// Base Url.
$mform->addElement('text', 'baseurl', get_string('issuerbaseurl', 'tool_oauth2'), 'maxlength="1024"');
$mform->addRule('baseurl', null, 'required', null, 'client');
$mform->addRule('baseurl', get_string('maximumchars', '', 1024), 'maxlength', 1024, 'client');
$mform->addHelpButton('baseurl', 'issuerbaseurl', 'tool_oauth2');
// Offline access type
$options = $provider->get_behaviour_list();
$mform->addElement('select', 'behaviour', get_string('issuerbehaviour', 'tool_oauth2'), $options);
$mform->addHelpButton('behaviour', 'issuerbehaviour', 'tool_oauth2');
// Image.
$mform->addElement('text', 'image', get_string('issuerimage', 'tool_oauth2'), 'maxlength="1024"');
$mform->addRule('image', get_string('maximumchars', '', 1024), 'maxlength', 1024, 'client');
$mform->addHelpButton('image', 'issuername', 'tool_oauth2');
// Show on login page.
$mform->addElement('checkbox', 'showonloginpage', get_string('issuershowonloginpage', 'tool_oauth2'));
$mform->addHelpButton('showonloginpage', 'issuershowonloginpage', 'tool_oauth2');
$mform->addElement('hidden', 'sortorder');
$mform->setType('sortorder', PARAM_INT);
$mform->addElement('hidden', 'action', 'edit');
$mform->setType('action', PARAM_RAW);
$mform->addElement('hidden', 'id', $provider->get('id'));
$mform->setType('id', PARAM_INT);
$this->add_action_buttons(true, get_string('savechanges', 'tool_oauth2'));
}
}
@@ -0,0 +1,167 @@
<?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/>.
/**
* Output rendering for the plugin.
*
* @package tool_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_oauth2\output;
use plugin_renderer_base;
use html_table;
use html_table_cell;
use html_table_row;
use html_writer;
use core\oauth2\issuer;
use core\oauth2\api;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* Implements the plugin renderer
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends plugin_renderer_base {
/**
* This function will render one beautiful table with all the issuers.
*
* @param \core\oauth2\issuer[] $issuers - list of all issuers.
* @return string HTML to output.
*/
public function issuers_table($issuers) {
global $CFG, $OUTPUT;
$table = new html_table();
$table->head = [
get_string('name'),
get_string('configuredstatus', 'tool_oauth2'),
get_string('loginissuer', 'tool_oauth2'),
get_string('discoverystatus', 'tool_oauth2'),
get_string('systemauthstatus', 'tool_oauth2'),
get_string('edit'),
];
$table->attributes['class'] = 'admintable generaltable';
$data = [];
$index = 0;
foreach ($issuers as $issuer) {
// We need to handle the first and last ones specially.
$first = false;
if ($index == 0) {
$first = true;
}
$last = false;
if ($index == count($issuers) - 1) {
$last = true;
}
// Name.
$name = $issuer->get('name');
$image = $issuer->get('image');
if ($image) {
$name = '<img width=24 height=24 alt="" src="' . $image . '"> ' . $name;
}
$namecell = new html_table_cell($name);
$namecell->header = true;
// Configured.
if (!empty($issuer->get('clientid')) && !empty($issuer->get('clientsecret'))) {
$configured = $OUTPUT->pix_icon('yes', get_string('configured', 'tool_oauth2'), 'tool_oauth2');
} else {
$configured = $OUTPUT->pix_icon('no', get_string('notconfigured', 'tool_oauth2'), 'tool_oauth2');
}
$configuredstatuscell = new html_table_cell($configured);
// Login issuer.
if (!empty($issuer->get('showonloginpage'))) {
$loginissuer = $OUTPUT->pix_icon('yes', get_string('loginissuer', 'tool_oauth2'), 'tool_oauth2');
} else {
$loginissuer = $OUTPUT->pix_icon('no', get_string('notloginissuer', 'tool_oauth2'), 'tool_oauth2');
}
$loginissuerstatuscell = new html_table_cell($loginissuer);
// Discovered.
if (!empty($issuer->get('scopessupported'))) {
$discovered = $OUTPUT->pix_icon('yes', get_string('discovered', 'tool_oauth2'), 'tool_oauth2');
} else {
$discovered = $OUTPUT->pix_icon('no', get_string('notdiscovered', 'tool_oauth2'), 'tool_oauth2');
}
$discoverystatuscell = new html_table_cell($discovered);
// Connected.
if ($issuer->is_system_account_connected()) {
$systemauth = $OUTPUT->pix_icon('yes', get_string('systemaccountconnected', 'tool_oauth2'), 'tool_oauth2');
} else {
$systemauth = $OUTPUT->pix_icon('no', get_string('systemaccountnotconnected', 'tool_oauth2'), 'tool_oauth2');
}
if ($issuer->is_system_account_setup_supported()) {
$params = ['id' => $issuer->get('id'), 'action' => 'auth'];
$authurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
$icon = $OUTPUT->pix_icon('auth', get_string('connectsystemaccount', 'tool_oauth2'), 'tool_oauth2');
$authlink = html_writer::link($authurl, $icon);
$systemauth .= ' ' . $authlink;
}
$systemauthstatuscell = new html_table_cell($systemauth);
// Action links.
$links = '';
$editurl = new moodle_url('/admin/tool/oauth2/issuers.php', ['id' => $issuer->get('id'), 'action' => 'edit']);
$editlink = html_writer::link($editurl, $OUTPUT->pix_icon('t/edit', get_string('edit')));
$links .= ' ' . $editlink;
$deleteurl = new moodle_url('/admin/tool/oauth2/issuers.php', ['id' => $issuer->get('id'), 'action' => 'delete']);
$deletelink = html_writer::link($deleteurl, $OUTPUT->pix_icon('t/delete', get_string('delete')));
$links .= ' ' . $deletelink;
if (!$last) {
$params = ['id' => $issuer->get('id'), 'action' => 'movedown', 'sesskey' => sesskey()];
$movedownurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
$movedownlink = html_writer::link($movedownurl, $OUTPUT->pix_icon('t/down', get_string('movedown')));
$links .= ' ' . $movedownlink;
}
if (!$first) {
$params = ['id' => $issuer->get('id'), 'action' => 'moveup', 'sesskey' => sesskey()];
$moveupurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
$moveuplink = html_writer::link($moveupurl, $OUTPUT->pix_icon('t/up', get_string('moveup')));
$links .= ' ' . $moveuplink;
}
$editcell = new html_table_cell($links);
$row = new html_table_row([
$namecell,
$configuredstatuscell,
$loginissuerstatuscell,
$discoverystatuscell,
$systemauthstatuscell,
$editcell,
]);
$data[] = $row;
$index++;
}
$table->data = $data;
return html_writer::table($table);
}
}
+142
View File
@@ -0,0 +1,142 @@
<?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/>.
/**
* OAuth 2 Configuration page.
*
* @package tool_oauth2
* @copyright 2017 Damyon Wiese <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/tablelib.php');
$PAGE->set_url('/admin/tool/oauth2/issuers.php');
$PAGE->set_context(context_system::instance());
$PAGE->set_pagelayout('admin');
$strheading = get_string('pluginname', 'tool_oauth2');
$PAGE->set_title($strheading);
$PAGE->set_heading($strheading);
require_login();
require_capability('moodle/site:config', context_system::instance());
$renderer = $PAGE->get_renderer('tool_oauth2');
$action = optional_param('action', '', PARAM_ALPHAEXT);
$idpid = optional_param('id', '', PARAM_RAW);
$issuer = null;
$mform = null;
if ($idpid) {
$issuer = \core\oauth2\api::get_issuer($idpid);
if (!$issuer) {
print_error('invaliddata');
}
}
if ($action == 'edit') {
if ($issuer) {
$PAGE->navbar->add(get_string('editissuer', 'tool_oauth2', $issuer->get('name')));
} else {
$PAGE->navbar->add(get_string('createnewissuer', 'tool_oauth2'));
}
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer]);
}
if ($mform && $mform->is_cancelled()) {
redirect(new moodle_url('/admin/tool/oauth2/issuers.php'));
} else if ($action == 'edit') {
if ($data = $mform->get_data()) {
try {
if (!empty($data->id)) {
core\oauth2\api::update_issuer($data);
} else {
core\oauth2\api::create_issuer($data);
}
redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
} catch (Exception $e) {
redirect($PAGE->url, $e->getMessage(), null, \core\output\notification::NOTIFY_ERROR);
}
} else {
echo $OUTPUT->header();
if ($issuer) {
echo $OUTPUT->heading(get_string('editissuer', 'tool_oauth2', $issuer->get('name')));
} else {
echo $OUTPUT->heading(get_string('createnewissuer', 'tool_oauth2'));
}
$mform->display();
echo $OUTPUT->footer();
}
} else if ($action == 'delete') {
if (!optional_param('confirm', false, PARAM_BOOL)) {
$continueparams = ['action' => 'delete', 'id' => $idpid, 'sesskey' => sesskey(), 'confirm' => true];
$continueurl = new moodle_url('/admin/tool/oauth2/issuers.php', $continueparams);
$cancelurl = new moodle_url('/admin/tool/oauth2/issuers.php');
echo $OUTPUT->header();
echo $OUTPUT->confirm(get_string('deleteconfirm', 'tool_oauth2', $issuer->get('name')), $continueurl, $cancelurl);
echo $OUTPUT->footer();
} else {
require_sesskey();
core\oauth2\api::delete_issuer($idpid);
redirect($PAGE->url, get_string('issuerdeleted', 'tool_oauth2'), null, \core\output\notification::NOTIFY_SUCCESS);
}
} else if ($action == 'auth') {
if (!optional_param('confirm', false, PARAM_BOOL)) {
$continueparams = ['action' => 'auth', 'id' => $idpid, 'sesskey' => sesskey(), 'confirm' => true];
$continueurl = new moodle_url('/admin/tool/oauth2/issuers.php', $continueparams);
$cancelurl = new moodle_url('/admin/tool/oauth2/issuers.php');
echo $OUTPUT->header();
echo $OUTPUT->confirm(get_string('authconfirm', 'tool_oauth2', $issuer->get('name')), $continueurl, $cancelurl);
echo $OUTPUT->footer();
} else {
require_sesskey();
$params = ['sesskey' => sesskey(), 'id' => $idpid, 'action' => 'auth', 'confirm' => true, 'response' => true];
if (core\oauth2\api::connect_system_account($issuer, new moodle_url('/admin/tool/oauth2/issuers.php', $params))) {
redirect($PAGE->url, get_string('authconnected', 'tool_oauth2'), null, \core\output\notification::NOTIFY_SUCCESS);
} else {
redirect($PAGE->url, get_string('authnotconnected', 'tool_oauth2'), null, \core\output\notification::NOTIFY_ERROR);
}
}
} else if ($action == 'moveup') {
require_sesskey();
core\oauth2\api::move_up_issuer($idpid);
redirect($PAGE->url);
} else if ($action == 'movedown') {
require_sesskey();
core\oauth2\api::move_down_issuer($idpid);
redirect($PAGE->url);
} else {
echo $OUTPUT->header();
$issuers = core\oauth2\api::get_all_issuers();
echo $renderer->issuers_table($issuers);
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', ['action' => 'edit']);
echo $renderer->single_button($addurl, get_string('createnewissuer', 'tool_oauth2'));
echo $OUTPUT->footer();
}
+63
View File
@@ -0,0 +1,63 @@
<?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/>.
/**
* Strings for component 'tool_oauth2', language 'en'
*
* @package tool_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Open ID Connect configuration';
$string['editissuer'] = 'Edit identity issuer: {$a}';
$string['issuername'] = 'Name';
$string['issuername_help'] = 'Name of the identity issuer. May be displayed on login page.';
$string['issuerimage'] = 'Logo URL';
$string['issuerimage_help'] = 'An image url used to show a logo for this issuer. May be displayed on login page.';
$string['issuerclientid'] = 'Client Id';
$string['issuerclientid_help'] = 'The OAuth client ID for this issuer.';
$string['issuerclientsecret'] = 'Client Secret';
$string['issuerclientsecret_help'] = 'The OAuth client secret for this issuer.';
$string['issuerbaseurl'] = 'Service base url';
$string['issuerbaseurl_help'] = 'Base url used to access the service.';
$string['issuershowonloginpage'] = 'Show on login page.';
$string['issuershowonloginpage_help'] = 'If the OpenID Connect Authentication plugin is enabled, this login issuer will be listed on the login page to allow users to login with accounts from this issuer.';
$string['issuerbehaviour'] = 'Behaviour';
$string['issuerbehaviour_help'] = 'Choose from one of the supported behaviours.
* OAuth 2.0 - OAuth 2.0 API with no authentication
* OpenID Connect - Standards based OAuth 2.0 API with Authentication
* Microsoft OAuth 2.0 - Non-standard OAuth 2.0 combined with Microsoft Graph API';
$string['savechanges'] = 'Save changes';
$string['configuredstatus'] = 'Configured';
$string['discoverystatus'] = 'Discovery';
$string['systemauthstatus'] = 'System account connected';
$string['configured'] = 'Configured';
$string['notconfigured'] = 'Not configured';
$string['discovered'] = 'Service discovery successful';
$string['notdiscovered'] = 'Service discovery not successful';
$string['loginissuer'] = 'Allow login';
$string['notloginissuer'] = 'Do not allow login';
$string['systemaccountconnected'] = 'System account connected';
$string['systemaccountnotconnected'] = 'System account not connected';
$string['createnewissuer'] = 'Create new identity issuer';
$string['deleteconfirm'] = 'Are you sure you want to delete the identity issuer "{$a}"? Any plugins relying on this issuer will stop working.';
$string['issuerdeleted'] = 'Identity issuer deleted';
$string['connectsystemaccount'] = 'Connect to a system account';
$string['authconfirm'] = 'This action will grant permanent API access to Moodle for the authenticated account. This is intended to be used as a system account for managing files owned by Moodle.';
$string['authconnected'] = 'The system account is now connected for offline access';
$string['authnotconnected'] = 'The system account was not connected for offline access';
+3
View File
@@ -0,0 +1,3 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M9 16H1c-.5 0-1-.5-1-1V1c0-.5.5-1 1-1h8v2H3c-.6 0-1 .4-1 1v10c0 .6.4 1 1 1h6v2zM5 7.5v1c0 .5.5 1 1 1h4.7l-1.1 1.1c-.4.4-.4 1 0 1.4l.7.7c.4.4 1 .4 1.4 0l4-4c.4-.4.4-1 0-1.4l-4-4c-.4-.4-1-.4-1.4 0l-.7.7c-.4.4-.4 1 0 1.4l1 1H6c-.5.1-1 .6-1 1.1z" fill="#989898"/></svg>

After

Width:  |  Height:  |  Size: 569 B

+3
View File
@@ -0,0 +1,3 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-1.6 -0.5 16 16" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M12.8 2.7L10.1 0S8.5 1.5 6.4 4C4.3 1.5 2.7 0 2.7 0L0 2.7S1.9 4 4.6 6.4C3 8.7 1.3 11.6 0 14.9c2.2-2.7 4.4-5 6.4-6.9 2 1.9 4.2 4.2 6.4 6.9-1.3-3.3-3-6.2-4.6-8.6 2.7-2.3 4.6-3.6 4.6-3.6z" fill="#FF403C"/></svg>

After

Width:  |  Height:  |  Size: 517 B

+3
View File
@@ -0,0 +1,3 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-0.1 0 16 16" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M6.4 11.1c-2-2.5-3.7-4-3.7-4L0 9.8C5 13.1 8.1 16 8.1 16s.2-.7.6-1.8c.9-2.7 3.2-8.1 7.1-14.2-4.6 3.7-7.7 8.2-9.4 11.1z" fill="#9C3"/></svg>

After

Width:  |  Height:  |  Size: 445 B

+29
View File
@@ -0,0 +1,29 @@
<?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/>.
/**
* Oauth2 system configuration.
*
* @package tool_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
if ($hassiteconfig) {
$ADMIN->add('server', new admin_externalpage('oauth2', new lang_string('pluginname','tool_oauth2'), "$CFG->wwwroot/$CFG->admin/tool/oauth2/issuers.php"));
}
@@ -0,0 +1,53 @@
@tool @tool_task @javascript
Feature: Manage scheduled tasks
In order to configure scheduled tasks
As an admin
I need to be able to disable, enable, edit and reset to default scheduled tasks
Background:
Given I log in as "admin"
And I navigate to "Scheduled tasks" node in "Site administration > Server"
Scenario: Disable scheduled task
When I click on "Edit task schedule: Log table cleanup" "link" in the "Log table cleanup" "table_row"
Then I should see "Edit task schedule: Log table cleanup"
And I set the following fields to these values:
| disabled | 1 |
And I press "Save changes"
Then I should see "Changes saved"
And I should see "Task disabled" in the "Log table cleanup" "table_row"
Scenario: Enable scheduled task
When I click on "Edit task schedule: Log table cleanup" "link" in the "Log table cleanup" "table_row"
Then I should see "Edit task schedule: Log table cleanup"
And I set the following fields to these values:
| disabled | 0 |
And I press "Save changes"
Then I should see "Changes saved"
And I should not see "Task disabled" in the "Log table cleanup" "table_row"
Scenario: Edit scheduled task
When I click on "Edit task schedule: Log table cleanup" "link" in the "Log table cleanup" "table_row"
Then I should see "Edit task schedule: Log table cleanup"
And I set the following fields to these values:
| minute | */5 |
| hour | 1 |
| day | 2 |
| month | 3 |
| dayofweek | 4 |
And I press "Save changes"
Then I should see "Changes saved"
And the following should exist in the "admintable" table:
| Component | Minute | Hour | Day | Day of week | Month |
| Standard log | */5 | 1 | 2 | 4 | 3 |
Scenario: Reset scheduled task to default
When I click on "Edit task schedule: Log table cleanup" "link" in the "Log table cleanup" "table_row"
Then I should see "Edit task schedule: Log table cleanup"
And I set the following fields to these values:
| resettodefaults | 1 |
And I press "Save changes"
Then I should see "Changes saved"
And the following should not exist in the "admintable" table:
| Name | Component | Minute | Hour | Day | Day of week | Month |
| Log table cleanup | Standard log | */5 | 1 | 2 | 4 | 3 |
+275
View File
@@ -0,0 +1,275 @@
<?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/>.
/**
* File containing tests for the mform class.
*
* @package tool_task
* @copyright 2014 onwards Ankit Agarwal <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
/**
* Mform test class.
*
* @package tool_task
* @copyright 2014 onwards Ankit Agarwal <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
*/
class tool_task_form_testcase extends advanced_testcase {
/**
* Test validations for minute field.
*/
public function test_validate_fields_minute() {
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '*');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '1');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '65');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '*/');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '*/1');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '*/20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '*/65');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '1,2');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '2,20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '20,30,45');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '65,20,30');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '25,75');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '1-2');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '2-20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '20-30');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '65-20');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('minute', '25-75');
$this->assertFalse($valid);
}
/**
* Test validations for minute hour.
*/
public function test_validate_fields_hour() {
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '*');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '1');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '65');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '*/');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '*/1');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '*/20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '*/65');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '1,2');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '2,20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '20,30,45');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '65,20,30');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '25,75');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '1-2');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '2-20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '20-30');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '65-20');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('hour', '25-75');
$this->assertFalse($valid);
}
/**
* Test validations for day field.
*/
public function test_validate_fields_day() {
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '1');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '65');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '35');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*/');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*/1');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*/20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*/65');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '*/35');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '1,2');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '2,20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '20,30,25');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '65,20,30');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '25,35');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '1-2');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '2-20');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '20-30');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '65-20');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('day', '25-35');
$this->assertFalse($valid);
}
/**
* Test validations for month field.
*/
public function test_validate_fields_month() {
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '1');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '10');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '13');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '35');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*/');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*/1');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*/12');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*/13');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '*/35');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '1,2');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '2,11');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '2,10,12');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '65,2,13');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '25,35');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '1-2');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '2-12');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '3-6');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '65-2');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('month', '25-26');
$this->assertFalse($valid);
}
/**
* Test validations for dayofweek field.
*/
public function test_validate_fields_dayofweek() {
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '0');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '1');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '6');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '7');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '20');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*/');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*/1');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*/6');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*/13');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '*/35');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '1,2');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '2,6');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '2,6,3');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '65,2,13');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '25,35');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '1-2');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '2-6');
$this->assertTrue($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '65-2');
$this->assertFalse($valid);
$valid = \tool_task_edit_scheduled_task_form::validate_fields('dayofweek', '3-7');
$this->assertFalse($valid);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?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/>.
/**
* Plugin version info
*
* @package tool_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2016120500; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2016112900; // Requires this Moodle version
$plugin->component = 'tool_oauth2'; // Full name of the plugin (used for diagnostics)
+9 -2
View File
@@ -121,10 +121,17 @@ class login implements renderable, templatable {
}
public function export_for_template(renderer_base $output) {
global $CFG;
global $CFG, $OUTPUT;
$identityproviders = array_map(function($idp) use ($output) {
$idp['icon'] = $idp['icon']->export_for_template($output);
global $OUTPUT;
if (!empty($idp['icon'])) {
$idp['iconurl'] = $OUTPUT->pix_url($idp['icon']->key, $idp['icon']->component);
} else if ($idp['iconurl'] instanceof moodle_url) {
$idp['iconurl'] = $idp['iconurl']->out(false);
}
unset($idp['icon']);
if ($idp['url'] instanceof moodle_url) {
$idp['url'] = $idp['url']->out(false);
}
+40
View File
@@ -0,0 +1,40 @@
<?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/>.
/**
* Open ID authentication.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/authlib.php');
/**
* Plugin for oauth2 authentication.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
class auth_plugin_oauth2 extends \auth_oauth2\auth {
}
+350
View File
@@ -0,0 +1,350 @@
<?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/>.
/**
* Anobody can login with any password.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
namespace auth_oauth2;
defined('MOODLE_INTERNAL') || die();
use pix_icon;
use moodle_url;
use core_text;
use stdClass;
use core\oauth2\issuer;
use core\oauth2\client;
require_once($CFG->libdir.'/authlib.php');
/**
* Plugin for oauth2 authentication.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
class auth extends \auth_plugin_base {
/**
* @var stdClass $userinfo The set of user info returned from the oauth handshake
*/
private static $userinfo;
/**
* @var stdClass $userpicture The url to a picture.
*/
private static $userpicture;
/**
* Constructor.
*/
public function __construct() {
$this->authtype = 'oauth2';
$this->config = get_config('auth_oauth2');
}
/**
* Returns true if the username and password work or don't exist and false
* if the user exists and the password is wrong.
*
* @param string $username The username
* @param string $password The password
* @return bool Authentication success or failure.
*/
public function user_login($username, $password) {
$cached = $this->get_static_user_info();
$verifyusername = $cached['username'];
if ($verifyusername == $username) {
return true;
}
}
/**
* We don't want to allow users setting an internal password.
*
* @return bool
*/
public function prevent_local_passwords() {
return true;
}
/**
* Returns true if this authentication plugin is 'internal'.
*
* @return bool
*/
public function is_internal() {
return false;
}
/**
* Indicates if moodle should automatically update internal user
* records with data from external sources using the information
* from auth_plugin_base::get_userinfo().
*
* @return bool true means automatically copy data from ext to user table
*/
public function is_synchronised_with_external() {
return true;
}
/**
* Returns true if this authentication plugin can change the user's
* password.
*
* @return bool
*/
public function can_change_password() {
return false;
}
/**
* Returns the URL for changing the user's pw, or empty if the default can
* be used.
*
* @return moodle_url
*/
public function change_password_url() {
return null;
}
/**
* Returns true if plugin allows resetting of internal password.
*
* @return bool
*/
public function can_reset_password() {
return false;
}
/**
* Returns true if plugin can be manually set.
*
* @return bool
*/
public function can_be_manually_set() {
return true;
}
/**
* Prints a form for configuring this authentication plugin.
*
* This function is called from admin/auth.php, and outputs a full page with
* a form for configuring this plugin.
*
* @param stdClass $config
* @param string $err
* @param array userfields
*/
public function config_form($config, $err, $userfields) {
echo get_string('plugindescription', 'auth_oauth2');
// Force all fields updated on login and locked.
foreach ($userfields as $field) {
set_config('field_updatelocal_' . $field, 'onlogin', 'auth_oauth2');
set_config('field_lock_' . $field, 'unlockedifempty', 'auth_oauth2');
}
return;
}
/**
* Return the userinfo from the oauth handshake. Will only be valid
* for the logged in user.
*/
public function get_userinfo($username) {
$cached = $this->get_static_user_info();
if (!empty($cached) && $cached['username'] == $username) {
return $cached;
}
return false;
}
private function is_ready_for_login_page($issuer) {
return !empty($issuer->get('clientid')) &&
!empty($issuer->get('clientsecret')) &&
$issuer->is_authentication_supported() &&
!empty($issuer->get('showonloginpage'));
}
/**
* Return a list of identity providers to display on the login page.
*/
public function loginpage_idp_list($wantsurl) {
$providers = \core\oauth2\api::get_all_issuers();
$result = [];
if (empty($wantsurl)) {
$wantsurl = '/';
}
foreach ($providers as $idp) {
if ($this->is_ready_for_login_page($idp)) {
$params = ['id' => $idp->get('id'), 'wantsurl' => $wantsurl, 'sesskey' => sesskey()];
$url = new moodle_url('/auth/oauth2/login.php', $params);
$icon = $idp->get('image');
$result[] = ['url' => $url, 'iconurl' => $icon, 'name' => $idp->get('name')];
}
}
return $result;
}
/**
* Statically cache the user info from the oauth handshake
* @param stdClass $userinfo
*/
private function set_static_user_info($userinfo) {
self::$userinfo = $userinfo;
}
/**
* Get the static cached user info
* @return stdClass
*/
private function get_static_user_info() {
return self::$userinfo;
}
/**
* Statically cache the user picture from the oauth handshake
* @param string $userpicture
*/
private function set_static_user_picture($userpicture) {
self::$userpicture = $userpicture;
}
/**
* Get the static cached user picture
* @return string
*/
private function get_static_user_picture() {
return self::$userpicture;
}
/**
* If this user has no picture - but we got one from oauth - set it.
* @return boolean True if the image was updated.
*/
private function update_picture($user) {
global $CFG, $DB, $USER;
require_once($CFG->libdir . '/filelib.php');
require_once($CFG->libdir . '/gdlib.php');
$fs = get_file_storage();
$userid = $user->id;
if (!empty($user->picture)) {
return false;
}
$picture = $this->get_static_user_picture();
if (empty($picture)) {
return false;
}
$context = \context_user::instance($userid, MUST_EXIST);
$fs->delete_area_files($context->id, 'user', 'newicon');
$filerecord = array(
'contextid' => $context->id,
'component' => 'user',
'filearea' => 'newicon',
'itemid' => 0,
'filepath' => '/',
'filename' => 'image'
);
try {
$fs->create_file_from_string($filerecord, $picture);
} catch (\file_exception $e) {
return get_string($e->errorcode, $e->module, $e->a);
}
$iconfile = $fs->get_area_files($context->id, 'user', 'newicon', false, 'itemid', false);
// There should only be one.
$iconfile = reset($iconfile);
// Something went wrong while creating temp file - remove the uploaded file.
if (!$iconfile = $iconfile->copy_content_to_temp()) {
$fs->delete_area_files($context->id, 'user', 'newicon');
return false;
}
// Copy file to temporary location and the send it for processing icon.
$newpicture = (int) process_new_icon($context, 'user', 'icon', 0, $iconfile);
// Delete temporary file.
@unlink($iconfile);
// Remove uploaded file.
$fs->delete_area_files($context->id, 'user', 'newicon');
// Set the user's picture.
$updateuser = new stdClass();
$updateuser->id = $userid;
$updateuser->picture = $newpicture;
$USER->picture = $newpicture;
user_update_user($updateuser);
return true;
}
/**
* Complete the login process after oauth handshake is complete.
* @param \core\oauth2\client $client
* @param string $redirecturl
* @return none Either redirects or throws an exception
*/
public function complete_login(client $client, $redirecturl) {
global $CFG, $SESSION;
$userinfo = $client->get_userinfo();
if (!$userinfo) {
$errormsg = get_string('notloggedin', 'auth_oauth2');
$SESSION->loginerrormsg = $errormsg;
redirect(new moodle_url($CFG->httpswwwroot . '/login/index.php'));
}
$userinfo['username'] = trim(core_text::strtolower($userinfo['username']));
if (!empty($userinfo['picture'])) {
$this->set_static_user_picture($userinfo['picture']);
unset($userinfo['picture']);
}
if (!empty($userinfo['lang'])) {
$userinfo['lang'] = str_replace('-', '_', trim(core_text::strtolower($userinfo['lang'])));
if (!get_string_manager()->translation_exists($userinfo['lang'], false)) {
unset($userinfo['lang']);
}
}
$this->set_static_user_info($userinfo);
$user = authenticate_user_login($userinfo['username'], '');
if ($user) {
complete_user_login($user);
$this->update_picture($user);
redirect($redirecturl);
}
$errormsg = get_string('notloggedin', 'auth_oauth2');
$SESSION->loginerrormsg = $errormsg;
redirect(new moodle_url($CFG->httpswwwroot . '/login/index.php'));
}
}
+29
View File
@@ -0,0 +1,29 @@
<?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/>.
/**
* Strings for component 'auth_oauth2', language 'en'.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['auth_oauth2description'] = 'OAuth 2 standards based authentication';
$string['auth_oauth2settings'] = 'OAuth 2 authentication settings.';
$string['pluginname'] = 'OAuth 2';
$string['plugindescription'] = 'This authentication plugin displays a list of the configured identity providers on the moodle login page. Selecting an identity provider allows users to login with their credentials from an OAuth 2 provider.';
$string['notloggedin'] = 'The login attempt failed.';
+45
View File
@@ -0,0 +1,45 @@
<?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/>.
/**
* Open ID authentication.
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
require_once('../../config.php');
$issuerid = required_param('id', PARAM_INT);
$wantsurl = new moodle_url(optional_param('wantsurl', '/', PARAM_URL));
require_sesskey();
$issuer = new \core\oauth2\issuer($issuerid);
$returnparams = ['wantsurl' => $wantsurl, 'sesskey' => sesskey(), 'id' => $issuerid];
$returnurl = new moodle_url('/auth/oauth2/login.php', $returnparams);
$client = \core\oauth2\api::get_user_oauth_client($issuer, $returnurl);
if ($client) {
$auth = new \auth_oauth2\auth();
$auth->complete_login($client, $wantsurl);
} else {
throw new moodle_exception('Could not get an OAuth client.');
}
+29
View File
@@ -0,0 +1,29 @@
<?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/>.
/**
* Version information
*
* @package auth_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2016120500; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2016112900; // Requires this Moodle version
$plugin->component = 'auth_oauth2'; // Full name of the plugin (used for diagnostics)
+352
View File
@@ -0,0 +1,352 @@
<?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/>.
/**
* Class for loading/storing oauth2 endpoints from the DB.
*
* @package core_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\oauth2;
require_once($CFG->libdir . '/filelib.php');
use context_system;
use curl;
use stdClass;
use moodle_exception;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* Static list of api methods for system oauth2 configuration.
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class api {
/**
* Called from install.php and upgrade.php - install the default list of issuers
* @return int The number of issuers installed.
*/
public static function install_default_issuers() {
// Setup default list of identity issuers.
$record = (object) [
'name' => 'Google',
'image' => 'https://accounts.google.com/favicon.ico',
'behaviour' => issuer::BEHAVIOUR_OPENID_CONNECT,
'baseurl' => 'http://accounts.google.com/',
'clientid' => '',
'clientsecret' => '',
'showonloginpage' => true
];
$issuer = new issuer(0, $record);
$issuer->create();
$record = (object) [
'issuerid' => $issuer->get('id'),
'name' => 'discovery_endpoint',
'url' => 'https://accounts.google.com/.well-known/openid-configuration'
];
$endpoint = new endpoint(0, $record);
$endpoint->create();
$record = (object) [
'name' => 'Microsoft',
'image' => 'https://www.microsoft.com/favicon.ico',
'behaviour' => issuer::BEHAVIOUR_MICROSOFT,
'baseurl' => 'http://login.microsoftonline.com/common/oauth2/v2.0/',
'clientid' => '',
'clientsecret' => '',
'showonloginpage' => true
];
$issuer = new issuer(0, $record);
$issuer->create();
$record = (object) [
'issuerid' => $issuer->get('id'),
'name' => 'authorization_endpoint',
'url' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
];
$endpoint = new endpoint(0, $record);
$endpoint->create();
$record = (object) [
'issuerid' => $issuer->get('id'),
'name' => 'token_endpoint',
'url' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token'
];
$endpoint = new endpoint(0, $record);
$endpoint->create();
$record = (object) [
'issuerid' => $issuer->get('id'),
'name' => 'end_session_endpoint',
'url' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token'
];
$endpoint = new endpoint(0, $record);
$endpoint->create();
return issuer::count_records();
}
public static function get_all_issuers() {
return issuer::get_records([], 'sortorder');
}
public static function get_issuer($id) {
return new issuer($id);
}
public static function get_system_account(issuer $issuer) {
return system_account::get_record(['issuerid' => $issuer->get('id')]);
}
public static function get_system_oauth_client(issuer $issuer) {
}
public static function get_user_oauth_client(issuer $issuer, moodle_url $currenturl, $additionalscopes = '') {
$client = \core\oauth2\client::create($issuer, $currenturl, $additionalscopes);
if (!$client->is_logged_in()) {
redirect($client->get_login_url());
}
return $client;
}
public static function get_endpoints(issuer $issuer) {
require_capability('moodle/site:config', context_system::instance());
return endpoint::get_records(['issuerid' => $issuer->get('id')]);
}
protected static function guess_image($issuer) {
if (empty($issuer->get('image'))) {
$baseurl = parse_url($issuer->get('discoveryurl'));
$imageurl = $baseurl['scheme'] . '://' . $baseurl['host'] . '/favicon.ico';
$issuer->set('image', $imageurl);
$issuer->update();
}
}
/**
* If the behaviour supports discovery for this issuer, try and determine the list of valid endpoints.
*
* @param issuer $issuer
* @return int The number of discovered services.
*/
protected static function discover_endpoints($issuer) {
$curl = new curl();
if ($issuer->get('behaviour') != issuer::BEHAVIOUR_OPENID_CONNECT) {
return 0;
}
$url = $issuer->get_endpoint_url('discovery');
if (!$url) {
$url = $issuer->get('url') . '/.well-known/openid-configuration';
}
if (!$json = $curl->get($issuer->get_endpoint_url('discovery'))) {
$msg = 'Could not discover end points for identity issuer' . $issuer->get('name');
throw new moodle_exception($msg);
}
if ($msg = $curl->error) {
throw new moodle_exception('Could not discover service endpoints: ' . $msg);
}
$info = json_decode($json);
if (empty($info)) {
$msg = 'Could not discover end points for identity issuer' . $issuer->get('name');
throw new moodle_exception($msg);
}
foreach (endpoint::get_records(['issuerid' => $issuer->get('id')]) as $endpoint) {
if ($endpoint->get('name') != 'discovery_endpoint') {
$endpoint->delete();
}
}
foreach ($info as $key => $value) {
if (substr_compare($key, '_endpoint', - strlen('_endpoint')) === 0) {
$record = new stdClass();
$record->issuerid = $issuer->get('id');
$record->name = $key;
$record->url = $value;
$endpoint = new endpoint(0, $record);
$endpoint->create();
}
if ($key == 'scopes_supported') {
$issuer->set('scopessupported', implode(' ', $value));
$issuer->update();
}
}
return endpoint::count_records(['issuerid' => $issuer->get('id')]);
}
public static function update_issuer($data) {
require_capability('moodle/site:config', context_system::instance());
$issuer = new issuer(0, $data);
// Will throw exceptions on validation failures.
$issuer->update();
// Perform service discovery.
self::discover_endpoints($issuer);
self::guess_image($issuer);
}
public static function create_issuer($data) {
require_capability('moodle/site:config', context_system::instance());
$issuer = new issuer(0, $data);
// Will throw exceptions on validation failures.
$issuer->create();
// Perform service discovery.
self::discover_endpoints($issuer);
self::guess_image($issuer);
}
/**
* Reorder this identity issuer.
*
* Requires moodle/site:config capability at the system context.
*
* @param int $id The id of the identity issuer to move.
* @return boolean
*/
public static function move_up_issuer($id) {
require_capability('moodle/site:config', context_system::instance());
$current = new issuer($id);
$sortorder = $current->get('sortorder');
if ($sortorder == 0) {
return false;
}
$sortorder = $sortorder - 1;
$current->set('sortorder', $sortorder);
$filters = array('sortorder' => $sortorder);
$children = issuer::get_records($filters, 'id');
foreach ($children as $needtoswap) {
$needtoswap->set('sortorder', $sortorder + 1);
$needtoswap->update();
}
// OK - all set.
$result = $current->update();
return $result;
}
public static function move_down_issuer($id) {
require_capability('moodle/site:config', context_system::instance());
$current = new issuer($id);
$max = issuer::count_records();
if ($max > 0) {
$max--;
}
$sortorder = $current->get('sortorder');
if ($sortorder >= $max) {
return false;
}
$sortorder = $sortorder + 1;
$current->set('sortorder', $sortorder);
$filters = array('sortorder' => $sortorder);
$children = issuer::get_records($filters);
foreach ($children as $needtoswap) {
$needtoswap->set('sortorder', $sortorder - 1);
$needtoswap->update();
}
// OK - all set.
$result = $current->update();
return $result;
}
public static function delete_issuer($id) {
require_capability('moodle/site:config', context_system::instance());
$issuer = new issuer($id);
$systemaccount = self::get_system_account($issuer);
if ($systemaccount) {
$systemaccount->delete();
}
$endpoints = self::get_endpoints($issuer);
if ($endpoints) {
foreach ($endpoints as $endpoint) {
$endpoint->delete();
}
}
// Will throw exceptions on validation failures.
$issuer->delete();
}
public static function connect_system_account($issuer, $returnurl) {
require_capability('moodle/site:config', context_system::instance());
// We need to authenticate with an oauth 2 client AS a system user and get a refresh token for offline access.
$scopesrequired = 'openid email profile';
// Allow callbacks to inject non-standard scopes to the auth request.
$client = client::create($issuer, $returnurl, $scopesrequired, true);
if (!optional_param('response', false, PARAM_BOOL)) {
$client->log_out();
}
if (!$client->is_logged_in()) {
redirect($client->get_login_url());
}
$refreshtoken = $client->get_refresh_token();
if (!$refreshtoken) {
return false;
}
$systemaccount = self::get_system_account($issuer);
if ($systemaccount) {
$systemaccount->delete();
}
$record = new stdClass();
$record->issuerid = $issuer->get('id');
$record->refreshtoken = $refreshtoken;
$record->grantedscopes = $scopesrequired;
$systemaccount = new system_account(0, $record);
$systemaccount->create();
$client->log_out();
return true;
}
}
+117
View File
@@ -0,0 +1,117 @@
<?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/>.
/**
* Configurable oauth2 client class.
*
* @package core\oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\oauth2;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/oauthlib.php');
require_once($CFG->libdir . '/filelib.php');
use moodle_url;
use curl;
/**
* Configurable oauth2 client class where the urls come from DB.
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class client extends \oauth2_client {
/** @var \core\oauth2\issuer $issuer */
private $issuer;
/** @var bool $system */
protected $system = false;
/**
* Constructor.
*
* @param issuer $issuer
* @param moodle_url $returnurl
*/
public function __construct(issuer $issuer, moodle_url $returnurl, $scopesrequired, $system) {
$this->issuer = $issuer;
$this->system = $system;
$scopes = $this->get_login_scopes();
$additionalscopes = explode(' ', $scopesrequired);
foreach ($additionalscopes as $scope) {
if (strpos(' ' . $scopes . ' ', ' ' . $scope . ' ') === false) {
$scopes .= ' ' . $scope;
}
}
parent::__construct($issuer->get('clientid'), $issuer->get('clientsecret'), $returnurl, $scopes);
}
public static function create(issuer $issuer, moodle_url $returnurl, $scopesrequired, $system = false) {
if ($issuer->get('behaviour') == issuer::BEHAVIOUR_OPENID_CONNECT) {
return new client_openid_connect($issuer, $returnurl, $scopesrequired, $system);
} else if ($issuer->get('behaviour') == issuer::BEHAVIOUR_OAUTH2) {
return new client_oauth2($issuer, $returnurl, $scopesrequired, $system);
} else if ($issuer->get('behaviour') == issuer::BEHAVIOUR_MICROSOFT) {
return new client_microsoft($issuer, $returnurl, $scopesrequired, $system);
}
}
/**
* Returns the auth url for OAuth 2.0 request
* @return string the auth url
*/
protected function auth_url() {
return $this->issuer->get_endpoint_url('authorization');
}
public function get_issuer() {
return $this->issuer;
}
public function get_additional_login_parameters() {
if ($this->issuer->get('behaviour') == issuer::BEHAVIOUR_OPENID_CONNECT) {
return ['access_type' => 'offline', 'prompt' => 'consent'];
}
return [];
}
protected function get_login_scopes() {
return 'openid profile email';
}
/**
* Returns the token url for OAuth 2.0 request
* @return string the auth url
*/
protected function token_url() {
return $this->issuer->get_endpoint_url('token');
}
protected function get_tokenname() {
$name = static::class;
if ($this->system) {
$name .= '-system';
}
return $name;
}
}
+54
View File
@@ -0,0 +1,54 @@
<?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/>.
/**
* Configurable oauth2 client class.
*
* @package core\oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\oauth2;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
/**
* We have to call directly to the graph APIs because the Microsoft Open ID Connect API is
* lame.
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class client_microsoft extends client {
public function get_additional_login_parameters() {
return ['prompt' => 'consent'];
}
public function get_login_scopes() {
return 'openid profile email user.read';
}
public function get_userinfo() {
$me = $client->get('https://graph.microsoft.com/v1.0/me/');
var_dump($me);
}
}
+43
View File
@@ -0,0 +1,43 @@
<?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/>.
/**
* Configurable oauth2 client class.
*
* @package core\oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\oauth2;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
/**
* Configurable oauth2 client class where the urls come from DB.
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class client_oauth2 extends client {
public function get_additional_login_parameters() {
return ['access_type' => 'offline', 'prompt' => 'consent'];
}
}
@@ -0,0 +1,98 @@
<?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/>.
/**
* Configurable oauth2 client class.
*
* @package core\oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\oauth2;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
use stdClass;
use Exception;
/**
* Configurable oauth2 client class where the urls come from DB.
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class client_openid_connect extends client {
/**
* Returns a mapping of openid properties to moodle properties.
*
* @return array
*/
private function get_mapping() {
return [
'given_name' => 'firstname',
'middle_name' => 'middlename',
'family_name' => 'lastname',
'email' => 'email',
'username' => 'username',
'website' => 'url',
'nickname' => 'alternatename',
'picture' => 'picture',
'address' => 'address',
'phone' => 'phone',
'locale' => 'lang'
];
}
public function get_additional_login_parameters() {
if ($this->system) {
return ['access_type' => 'offline', 'prompt' => 'consent'];
}
return [];
}
public function get_userinfo() {
$url = $this->get_issuer()->get_endpoint_url('userinfo');
$response = $this->get($url);
if (!$response) {
return false;
}
$userinfo = new stdClass();
try {
$userinfo = json_decode($response);
} catch (Exception $e) {
return false;
}
if (!empty($userinfo->preferred_username)) {
$userinfo->username = $userinfo->preferred_username;
} else {
$userinfo->username = $userinfo->sub;
}
$map = $this->get_mapping();
$user = new stdClass();
foreach ($map as $openidproperty => $moodleproperty) {
if (!empty($userinfo->$openidproperty)) {
$user->$moodleproperty = $userinfo->$openidproperty;
}
}
return (array)$user;
}
}
+58
View File
@@ -0,0 +1,58 @@
<?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/>.
/**
* Class for loading/storing oauth2 endpoints from the DB.
*
* @package core_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\oauth2;
defined('MOODLE_INTERNAL') || die();
use core\persistent;
/**
* Class for loading/storing oauth2 endpoints from the DB
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class endpoint extends persistent {
const TABLE = 'oauth2_endpoint';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'issuerid' => array(
'type' => PARAM_INT
),
'name' => array(
'type' => PARAM_RAW,
),
'url' => array(
'type' => PARAM_URL,
)
);
}
}
+131
View File
@@ -0,0 +1,131 @@
<?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/>.
/**
* Class for loading/storing issuers from the DB.
*
* @package core_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\oauth2;
defined('MOODLE_INTERNAL') || die();
use core\persistent;
/**
* Class for loading/storing issuer from the DB
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class issuer extends persistent {
const TABLE = 'oauth2_issuer';
const BEHAVIOUR_OPENID_CONNECT = 'Open ID Connect';
const BEHAVIOUR_MICROSOFT = 'Microsoft OAuth 2.0';
const BEHAVIOUR_OAUTH2 = 'OAuth 2.0';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'name' => array(
'type' => PARAM_TEXT
),
'image' => array(
'type' => PARAM_URL,
'null' => NULL_ALLOWED,
'default' => null
),
'clientid' => array(
'type' => PARAM_RAW
),
'clientsecret' => array(
'type' => PARAM_RAW
),
'behaviour' => array(
'type' => PARAM_NOTAGS,
'choices' => array(self::BEHAVIOUR_OPENID_CONNECT, self::BEHAVIOUR_MICROSOFT, self::BEHAVIOUR_OAUTH2),
'default' => self::BEHAVIOUR_OPENID_CONNECT
),
'baseurl' => array(
'type' => PARAM_URL
),
'showonloginpage' => array(
'type' => PARAM_BOOL,
'default' => false
),
'scopessupported' => array(
'type' => PARAM_RAW,
'null' => NULL_ALLOWED,
'default' => null
),
'sortorder' => array(
'type' => PARAM_INT,
'default' => 0,
)
);
}
public function get_endpoint_url($type) {
$endpoint = endpoint::get_record([
'issuerid' => $this->get('id'),
'name' => $type . '_endpoint'
]);
if ($endpoint) {
return $endpoint->get('url');
}
return false;
}
public function is_authentication_supported() {
$supportedloginbehaviours = [
self::BEHAVIOUR_OPENID_CONNECT,
self::BEHAVIOUR_MICROSOFT,
];
return in_array($this->get('behaviour'), $supportedloginbehaviours);
}
public function is_system_account_setup_supported() {
$supportedsystemaccountbehaviours = [
self::BEHAVIOUR_OPENID_CONNECT,
self::BEHAVIOUR_MICROSOFT,
];
return in_array($this->get('behaviour'), $supportedsystemaccountbehaviours);
}
public function get_behaviour_list() {
return [
self::BEHAVIOUR_OPENID_CONNECT => self::BEHAVIOUR_OPENID_CONNECT,
self::BEHAVIOUR_OAUTH2 => self::BEHAVIOUR_OAUTH2,
self::BEHAVIOUR_MICROSOFT => self::BEHAVIOUR_MICROSOFT
];
}
public function is_system_account_connected() {
$sys = system_account::get_record(['issuerid' => $this->get('id')]);
if (!empty($sys) and !empty($sys->get('refreshtoken'))) {
return true;
}
return false;
}
}
+60
View File
@@ -0,0 +1,60 @@
<?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/>.
/**
* When using OAuth sometimes it makes sense to authenticate as a system user, and not the current user.
* In this case we use a refresh token to get an access token and the system admin must manually authorize the
* system account.
*
* @package core_oauth2
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\oauth2;
defined('MOODLE_INTERNAL') || die();
use core\persistent;
/**
* Class for loading/storing oauth2 refresh tokens from the DB.
*
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class system_account extends persistent {
const TABLE = 'oauth2_system_account';
/**
* Return the definition of the properties of this model.
*
* @return array
*/
protected static function define_properties() {
return array(
'issuerid' => array(
'type' => PARAM_INT
),
'refreshtoken' => array(
'type' => PARAM_RAW,
),
'grantedscopes' => array(
'type' => PARAM_RAW,
)
);
}
}
+2
View File
@@ -321,4 +321,6 @@ function xmldb_main_install() {
require_once($CFG->libdir . '/db/upgradelib.php');
make_default_scale();
make_competence_scale();
\core\oauth2\api::install_default_providers();
}
Regular → Executable
+51 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20170220" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20170221" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@@ -3463,5 +3463,55 @@
<INDEX NAME="cmidcompetencyid" UNIQUE="true" FIELDS="cmid, competencyid"/>
</INDEXES>
</TABLE>
<TABLE NAME="oauth2_endpoint" COMMENT="Describes the named endpoint for an oauth2 service.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The time this record was created."/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The time this record was modified."/>
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The user who modified this record."/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The service name."/>
<FIELD NAME="url" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The url to the endpoint"/>
<FIELD NAME="issuerid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The identity provider this service belongs to."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="issuer_id_key" TYPE="foreign" FIELDS="issuerid" REFTABLE="oauth2_issuer" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="oauth2_issuer" COMMENT="Details for an oauth 2 connect identity issuer.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Time this record was created."/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Time this record was modified."/>
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The user who modified this record"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The name of this identity issuer"/>
<FIELD NAME="image" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="baseurl" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The base url to the issuer"/>
<FIELD NAME="clientid" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The client id used to connect to this oauth2 service."/>
<FIELD NAME="clientsecret" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The secret used to connect to this oauth2 service."/>
<FIELD NAME="behaviour" TYPE="char" LENGTH="32" NOTNULL="true" DEFAULT="none" SEQUENCE="false" COMMENT="The type of behaviour for this oauth client."/>
<FIELD NAME="scopessupported" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="The list of scopes this service supports."/>
<FIELD NAME="showonloginpage" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The defined sort order."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="oauth2_system_account" COMMENT="Stored details used to get an access token as a system user for this oauth2 service.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Time this record was created."/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Time this record was modified."/>
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The user who modified this record."/>
<FIELD NAME="issuerid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The id of the oauth 2 identity issuer"/>
<FIELD NAME="refreshtoken" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The refresh token used to request access tokens."/>
<FIELD NAME="grantedscopes" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The scopes that this system account has been granted access to."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="issueridkey" TYPE="foreign-unique" FIELDS="issuerid" REFTABLE="oauth2_issuer" REFFIELDS="id"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
+93
View File
@@ -2611,5 +2611,98 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2017031400.00);
}
if ($oldversion < 2017032400.21) {
// Define table oauth2_issuer to be created.
$table = new xmldb_table('oauth2_issuer');
// Adding fields to table oauth2_issuer.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
$table->add_field('image', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
$table->add_field('baseurl', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
$table->add_field('clientid', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
$table->add_field('clientsecret', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
$table->add_field('behaviour', XMLDB_TYPE_CHAR, '32', null, XMLDB_NOTNULL, null, 'none');
$table->add_field('scopessupported', XMLDB_TYPE_TEXT, null, null, null, null, null);
$table->add_field('showonloginpage', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1');
$table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
// Adding keys to table oauth2_issuer.
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
// Conditionally launch create table for oauth2_issuer.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2017032400.21);
}
if ($oldversion < 2017032400.22) {
// Define table oauth2_endpoint to be created.
$table = new xmldb_table('oauth2_endpoint');
// Adding fields to table oauth2_endpoint.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
$table->add_field('url', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
$table->add_field('issuerid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
// Adding keys to table oauth2_endpoint.
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('issuer_id_key', XMLDB_KEY_FOREIGN, array('issuerid'), 'oauth2_issuer', array('id'));
// Conditionally launch create table for oauth2_endpoint.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2017032400.22);
}
if ($oldversion < 2017032400.23) {
// Define table oauth2_system_account to be created.
$table = new xmldb_table('oauth2_system_account');
// Adding fields to table oauth2_system_account.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('issuerid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('refreshtoken', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
$table->add_field('grantedscopes', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
// Adding keys to table oauth2_system_account.
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('issueridkey', XMLDB_KEY_FOREIGN_UNIQUE, array('issuerid'), 'oauth2_issuer', array('id'));
// Conditionally launch create table for oauth2_system_account.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2017032400.23);
}
if ($oldversion < 2017033100.01) {
\core\oauth2\api::install_default_issuers();
// Main savepoint reached.
upgrade_main_savepoint(true, 2017033100.01);
}
return true;
}
+3 -2
View File
@@ -2766,9 +2766,9 @@ class curl {
// All settings of this class should be init here.
$this->resetopt();
if (!empty($settings['debug'])) {
//if (!empty($settings['debug'])) {
$this->debug = true;
}
//}
if (!empty($settings['cookie'])) {
if($settings['cookie'] === true) {
$this->cookie = $CFG->dataroot.'/curl_cookie.txt';
@@ -2949,6 +2949,7 @@ class curl {
* Set HTTP Request Header
*
* @param array $header
* @param bool $replace If true, will remove any existing headers before appending the new one.
*/
public function setHeader($header) {
if (is_array($header)) {
+52 -12
View File
@@ -393,6 +393,8 @@ abstract class oauth2_client extends curl {
private $scope = '';
/** var stdClass access token object */
private $accesstoken = null;
/** var stdClass refresh token string */
private $refreshtoken = '';
/**
* Returns the auth url for OAuth 2.0 request
@@ -463,6 +465,10 @@ abstract class oauth2_client extends curl {
return new moodle_url('/admin/oauth2callback.php');
}
public function get_additional_login_parameters() {
return [];
}
/**
* Returns the login link for this oauth request
*
@@ -471,15 +477,32 @@ abstract class oauth2_client extends curl {
public function get_login_url() {
$callbackurl = self::callback_url();
$url = new moodle_url($this->auth_url(),
array('client_id' => $this->clientid,
'response_type' => 'code',
'redirect_uri' => $callbackurl->out(false),
'state' => $this->returnurl->out_as_local_url(false),
'scope' => $this->scope,
));
$params = array_merge(
[
'client_id' => $this->clientid,
'response_type' => 'code',
'redirect_uri' => $callbackurl->out(false),
'state' => $this->returnurl->out_as_local_url(false),
'scope' => $this->scope,
],
$this->get_additional_login_parameters()
);
return $url;
return new moodle_url($this->auth_url(), $params);
}
/**
* Given an array of name value pairs - build a valid HTTP POST application/x-www-form-urlencoded string.
*
* @param array $params Name / value pairs.
* @return string POST data.
*/
public function build_post_data($params) {
$result = [];
foreach ($params as $name => $value) {
$result[] = str_replace('&', '%26', $name) . '=' . str_replace('&', '%26', $value);
}
return implode('&', $result);
}
/**
@@ -490,10 +513,10 @@ abstract class oauth2_client extends curl {
*/
public function upgrade_token($code) {
$callbackurl = self::callback_url();
$params = array('client_id' => $this->clientid,
$params = array('code' => $code,
'client_id' => $this->clientid,
'client_secret' => $this->clientsecret,
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $callbackurl->out(false),
);
@@ -501,7 +524,7 @@ abstract class oauth2_client extends curl {
if ($this->use_http_get()) {
$response = $this->get($this->token_url(), $params);
} else {
$response = $this->post($this->token_url(), $params);
$response = $this->post($this->token_url(), $this->build_post_data($params));
}
if (!$this->info['http_code'] === 200) {
@@ -514,6 +537,10 @@ abstract class oauth2_client extends curl {
return false;
}
if (isset($r->refresh_token)) {
$this->refreshtoken = $r->refresh_token;
}
// Store the token an expiry time.
$accesstoken = new stdClass;
$accesstoken->token = $r->access_token;
@@ -552,7 +579,11 @@ abstract class oauth2_client extends curl {
}
}
return parent::request($murl->out(false), $options);
$response = parent::request($murl->out(false), $options);
$this->resetHeader();
return $response;
}
/**
@@ -603,6 +634,15 @@ abstract class oauth2_client extends curl {
}
}
/**
* Get a refresh token!!!
*
* @return string
*/
public function get_refresh_token() {
return $this->refreshtoken;
}
/**
* Retrieve a token stored.
*
+3 -3
View File
@@ -148,9 +148,9 @@
{{#identityproviders}}
<div class="potentialidp">
<a href="{{url}}" title={{#quote}}{{name}}{{/quote}}>
{{#icon}}
{{>core/pix_icon}}
{{/icon}}
{{#iconurl}}
<img src="{{iconurl}}" alt="" width="24" height="24"/>
{{/iconurl}}
{{name}}
</a>
</div>
+6 -1
View File
@@ -178,7 +178,12 @@
<div class="potentialidplist" class="m-t-1">
{{#identityproviders}}
<div class="potentialidp">
<a href="{{url}}" title={{#quote}}{{name}}{{/quote}}>{{#icon}}{{>core/pix_icon}}{{/icon}}{{name}}</a>
<a href="{{url}}" title={{#quote}}{{name}}{{/quote}} class="btn btn-secondary">
{{#iconurl}}
<img src="{{iconurl}}" alt="" width="24" height="24"/>
{{/iconurl}}
{{name}}
</a>
</div>
{{/identityproviders}}
</div>
+1 -1
View File
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2017033100.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2017033100.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.