Skip to content

Commit

Permalink
MDL-71017 oauth2: configure some oauth2 services for login only
Browse files Browse the repository at this point in the history
  • Loading branch information
marinaglancy committed Apr 20, 2021
1 parent 0fd37bf commit 0152fdb
Show file tree
Hide file tree
Showing 18 changed files with 175 additions and 52 deletions.
85 changes: 66 additions & 19 deletions admin/tool/oauth2/classes/form/issuer.php
Expand Up @@ -115,54 +115,69 @@ public function definition() {
$mform->addElement('checkbox', 'basicauth', get_string('usebasicauth', 'tool_oauth2'));
$mform->addHelpButton('basicauth', 'usebasicauth', 'tool_oauth2');

// Base Url.
$mform->addElement('text', 'baseurl', get_string('issuerbaseurl', 'tool_oauth2'));
$mform->addRule('baseurl', get_string('maximumchars', '', 1024), 'maxlength', 1024, 'client');
$mform->addHelpButton('baseurl', 'issuerbaseurl', 'tool_oauth2');
if ($this->type && $this->type == 'nextcloud') {
$mform->addRule('baseurl', null, 'required', null, 'client');
}

// 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.
$options = [
\core\oauth2\issuer::EVERYWHERE => get_string('issueruseineverywhere', 'tool_oauth2'),
\core\oauth2\issuer::LOGINONLY => get_string('issueruseinloginonly', 'tool_oauth2'),
\core\oauth2\issuer::SERVICEONLY => get_string('issueruseininternalonly', 'tool_oauth2'),
];
$mform->addElement('select', 'showonloginpage', get_string('issuerusein', 'tool_oauth2'), $options);
$mform->addHelpButton('showonloginpage', 'issuerusein', 'tool_oauth2');

// Name on login page.
$mform->addElement('text', 'loginpagename', get_string('issuerloginpagename', 'tool_oauth2'));
$mform->addRule('loginpagename', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('loginpagename', 'issuerloginpagename', 'tool_oauth2');
$mform->hideIf('loginpagename', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);

// Login scopes.
$mform->addElement('text', 'loginscopes', get_string('issuerloginscopes', 'tool_oauth2'));
$mform->addRule('loginscopes', null, 'required', null, 'client');
$mform->addRule('loginscopes', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('loginscopes', 'issuerloginscopes', 'tool_oauth2');
$mform->hideIf('loginscopes', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);

// Login scopes offline.
$mform->addElement('text', 'loginscopesoffline', get_string('issuerloginscopesoffline', 'tool_oauth2'));
$mform->addRule('loginscopesoffline', null, 'required', null, 'client');
$mform->addRule('loginscopesoffline', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('loginscopesoffline', 'issuerloginscopesoffline', 'tool_oauth2');
$mform->hideIf('loginscopesoffline', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);

// Login params.
$mform->addElement('text', 'loginparams', get_string('issuerloginparams', 'tool_oauth2'));
$mform->addRule('loginparams', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('loginparams', 'issuerloginparams', 'tool_oauth2');
$mform->hideIf('loginparams', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);

// Login params offline.
$mform->addElement('text', 'loginparamsoffline', get_string('issuerloginparamsoffline', 'tool_oauth2'));
$mform->addRule('loginparamsoffline', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('loginparamsoffline', 'issuerloginparamsoffline', 'tool_oauth2');

// Base Url.
$mform->addElement('text', 'baseurl', get_string('issuerbaseurl', 'tool_oauth2'));
$mform->addRule('baseurl', get_string('maximumchars', '', 1024), 'maxlength', 1024, 'client');
$mform->addHelpButton('baseurl', 'issuerbaseurl', 'tool_oauth2');
if ($this->type && $this->type == 'nextcloud') {
$mform->addRule('baseurl', null, 'required', null, 'client');
}
$mform->hideIf('loginparamsoffline', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);

// Allowed Domains.
$mform->addElement('text', 'alloweddomains', get_string('issueralloweddomains', 'tool_oauth2'));
$mform->addRule('alloweddomains', get_string('maximumchars', '', 1024), 'maxlength', 1024, 'client');
$mform->addHelpButton('alloweddomains', 'issueralloweddomains', '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->hideIf('alloweddomains', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);

if ($this->showrequireconfirm) {
// Require confirmation email for new accounts.
$mform->addElement('advcheckbox', 'requireconfirmation', get_string('issuerrequireconfirmation', 'tool_oauth2'));
$mform->addHelpButton('requireconfirmation', 'issuerrequireconfirmation', 'tool_oauth2');
$mform->hideIf('requireconfirmation', 'showonloginpage', 'eq', \core\oauth2\issuer::SERVICEONLY);
}

if ($this->type == 'imsobv2p1' || $issuer->get('servicetype') == 'imsobv2p1') {
Expand Down Expand Up @@ -209,4 +224,36 @@ public function definition_after_data() {
$mform->getElement('servicetype')->setValue($this->type);
}
}

/**
* Define extra validation mechanims.
*
* The data here:
* - does not include {@link self::$fieldstoremove}.
* - does include {@link self::$foreignfields}.
* - was converted to map persistent-like data, e.g. array $description to string $description + int $descriptionformat.
*
* You can modify the $errors parameter in order to remove some validation errors should you
* need to. However, the best practice is to return new or overriden errors. Only modify the
* errors passed by reference when you have no other option.
*
* Do not add any logic here, it is only intended to be used by child classes.
*
* @param stdClass $data Data to validate.
* @param array $files Array of files.
* @param array $errors Currently reported errors.
* @return array of additional errors, or overridden errors.
*/
protected function extra_validation($data, $files, array &$errors) {
$errors = [];
if ($data->showonloginpage != \core\oauth2\issuer::SERVICEONLY) {
if (!strlen(trim($data->loginscopes))) {
$errors['loginscopes'] = get_string('required');
}
if (!strlen(trim($data->loginscopesoffline))) {
$errors['loginscopesoffline'] = get_string('required');
}
}
return $errors;
}
}
18 changes: 16 additions & 2 deletions admin/tool/oauth2/classes/output/renderer.php
Expand Up @@ -54,7 +54,9 @@ public function issuers_table($issuers) {
$table->head = [
get_string('name'),
get_string('configuredstatus', 'tool_oauth2'),
get_string('loginissuer', 'tool_oauth2'),
get_string('issuerusedforlogin', 'tool_oauth2'),
get_string('issuerdisplayas', 'tool_oauth2'),
get_string('issuerusedforinternal', 'tool_oauth2'),
get_string('discoverystatus', 'tool_oauth2') . ' ' . $this->help_icon('discovered', 'tool_oauth2'),
get_string('systemauthstatus', 'tool_oauth2') . ' ' . $this->help_icon('systemaccountconnected', 'tool_oauth2'),
get_string('edit'),
Expand Down Expand Up @@ -93,13 +95,23 @@ public function issuers_table($issuers) {
$configuredstatuscell = new html_table_cell($configured);

// Login issuer.
if (!empty($issuer->get('showonloginpage'))) {
if ((int)$issuer->get('showonloginpage') != issuer::SERVICEONLY) {
$loginissuer = $this->pix_icon('yes', get_string('loginissuer', 'tool_oauth2'), 'tool_oauth2');
$logindisplayas = s($issuer->get_display_name());
} else {
$loginissuer = $this->pix_icon('no', get_string('notloginissuer', 'tool_oauth2'), 'tool_oauth2');
$logindisplayas = '';
}
$loginissuerstatuscell = new html_table_cell($loginissuer);

// Internal services issuer.
if ((int)$issuer->get('showonloginpage') != issuer::LOGINONLY) {
$loginissuer = $this->pix_icon('yes', get_string('loginissuer', 'tool_oauth2'), 'tool_oauth2');
} else {
$loginissuer = $this->pix_icon('no', get_string('notloginissuer', 'tool_oauth2'), 'tool_oauth2');
}
$internalissuerstatuscell = new html_table_cell($loginissuer);

// Discovered.
if (!empty($issuer->get('scopessupported'))) {
$discovered = $this->pix_icon('yes', get_string('discovered', 'tool_oauth2'), 'tool_oauth2');
Expand Down Expand Up @@ -188,6 +200,8 @@ public function issuers_table($issuers) {
$namecell,
$configuredstatuscell,
$loginissuerstatuscell,
$logindisplayas,
$internalissuerstatuscell,
$discoverystatuscell,
$systemauthstatuscell,
$editcell,
Expand Down
2 changes: 1 addition & 1 deletion admin/tool/oauth2/issuers.php
Expand Up @@ -195,7 +195,7 @@
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('pluginname', 'tool_oauth2'));
echo $OUTPUT->doc_link('OAuth2_Services', get_string('serviceshelp', 'tool_oauth2'));
$issuers = core\oauth2\api::get_all_issuers();
$issuers = core\oauth2\api::get_all_issuers(true);
echo $renderer->issuers_table($issuers);

echo $renderer->container_start();
Expand Down
10 changes: 10 additions & 0 deletions admin/tool/oauth2/lang/en/tool_oauth2.php
Expand Up @@ -70,6 +70,8 @@
$string['issuerenabled'] = 'Identity issuer enabled';
$string['issuerimage_help'] = 'An image URL used to show a logo for this issuer. May be displayed on login page.';
$string['issuerimage'] = 'Logo URL';
$string['issuerloginpagename'] = 'Name displayed on the login page';
$string['issuerloginpagename_help'] = 'If specified, this name will be used on the login page instead of the service name above';
$string['issuerloginparams'] = 'Additional parameters included in a login request.';
$string['issuerloginparams_help'] = 'Some systems require additional parameters for a login request in order to read the user\'s basic profile.';
$string['issuerloginparamsoffline'] = 'Additional parameters included in a login request for offline access.';
Expand All @@ -85,6 +87,14 @@
$string['issuerrequireconfirmation_help'] = 'Require that all users verify their email address before they can log in with OAuth. This applies to newly created accounts as part of the login process, or when an existing Moodle account is connected to an OAuth login via matching email addresses.';
$string['issuerrequireconfirmation'] = 'Require email verification';
$string['issuers'] = 'Issuers';
$string['issuerusein'] = 'This service will be used';
$string['issuerusein_help'] = 'OAuth 2 services can be used in some internal services, on the login page, or both, if needed';
$string['issueruseineverywhere'] = 'Login page and internal services';
$string['issueruseininternalonly'] = 'Internal services only';
$string['issueruseinloginonly'] = 'Login page only';
$string['issuerusedforlogin'] = 'Login';
$string['issuerusedforinternal'] = 'Services';
$string['issuerdisplayas'] = 'Display as';
$string['loginissuer'] = 'Allow login';
$string['microsoft_service'] = 'Microsoft';
$string['nextcloud_service'] = 'Nextcloud';
Expand Down
17 changes: 3 additions & 14 deletions auth/oauth2/classes/auth.php
Expand Up @@ -166,35 +166,24 @@ public function get_userinfo($username) {
return false;
}

/**
* Do some checks on the identity provider before showing it on the login page.
* @param core\oauth2\issuer $issuer
* @return boolean
*/
private function is_ready_for_login_page(\core\oauth2\issuer $issuer) {
return $issuer->get('enabled') &&
$issuer->is_configured() &&
!empty($issuer->get('showonloginpage'));
}

/**
* Return a list of identity providers to display on the login page.
*
* @param string|moodle_url $wantsurl The requested URL.
* @return array List of arrays with keys url, iconurl and name.
*/
public function loginpage_idp_list($wantsurl) {
$providers = \core\oauth2\api::get_all_issuers();
$providers = \core\oauth2\api::get_all_issuers(true);
$result = [];
if (empty($wantsurl)) {
$wantsurl = '/';
}
foreach ($providers as $idp) {
if ($this->is_ready_for_login_page($idp)) {
if ($idp->is_available_for_login()) {
$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')];
$result[] = ['url' => $url, 'iconurl' => $icon, 'name' => $idp->get_display_name()];
}
}
return $result;
Expand Down
4 changes: 2 additions & 2 deletions auth/oauth2/classes/output/renderer.php
Expand Up @@ -28,7 +28,7 @@
use html_table_cell;
use html_table_row;
use html_writer;
use auth\oauth2\linked_login;
use auth_oauth2\linked_login;
use moodle_url;

defined('MOODLE_INTERNAL') || die();
Expand All @@ -43,7 +43,7 @@ class renderer extends plugin_renderer_base {
/**
* This function will render one beautiful table with all the linked_logins.
*
* @param \auth\oauth2\linked_login[] $linkedlogins - list of all linked logins.
* @param linked_login[] $linkedlogins - list of all linked logins.
* @return string HTML to output.
*/
public function linked_logins_table($linkedlogins) {
Expand Down
9 changes: 5 additions & 4 deletions auth/oauth2/linkedlogins.php
Expand Up @@ -45,7 +45,7 @@
$issuerid = required_param('issuerid', PARAM_INT);
$issuer = \core\oauth2\api::get_issuer($issuerid);

if (!$issuer->is_authentication_supported() || !$issuer->get('showonloginpage') || !$issuer->get('enabled')) {
if (!$issuer->is_available_for_login()) {
throw new \moodle_exception('issuernologin', 'auth_oauth2');
}

Expand Down Expand Up @@ -89,19 +89,20 @@

auth_oauth2\api::clean_orphaned_linked_logins();

$issuers = \core\oauth2\api::get_all_issuers();
$issuers = \core\oauth2\api::get_all_issuers(true);

$anyshowinloginpage = false;
$issuerbuttons = array();
foreach ($issuers as $issuer) {
if (!$issuer->is_authentication_supported() || !$issuer->get('showonloginpage') || !$issuer->get('enabled')) {
if (!$issuer->is_available_for_login()) {
continue;
}
$anyshowinloginpage = true;

$addparams = ['action' => 'new', 'issuerid' => $issuer->get('id'), 'sesskey' => sesskey(), 'logout' => true];
$addurl = new moodle_url('/auth/oauth2/linkedlogins.php', $addparams);
$issuerbuttons[$issuer->get('id')] = $renderer->single_button($addurl, get_string('createnewlinkedlogin', 'auth_oauth2', s($issuer->get('name'))));
$issuerbuttons[$issuer->get('id')] = $renderer->single_button($addurl, get_string('createnewlinkedlogin', 'auth_oauth2',
s($issuer->get_display_name())));
}

if (!$anyshowinloginpage) {
Expand Down
3 changes: 3 additions & 0 deletions auth/oauth2/login.php
Expand Up @@ -37,6 +37,9 @@
}

$issuer = new \core\oauth2\issuer($issuerid);
if (!$issuer->is_available_for_login()) {
throw new \moodle_exception('issuernologin', 'auth_oauth2');
}

$returnparams = ['wantsurl' => $wantsurl, 'sesskey' => sesskey(), 'id' => $issuerid];
$returnurl = new moodle_url('/auth/oauth2/login.php', $returnparams);
Expand Down
10 changes: 8 additions & 2 deletions lib/classes/oauth2/api.php
Expand Up @@ -110,10 +110,16 @@ public static function create_standard_issuer($type, $baseurl = false) {

/**
* List all the issuers, ordered by the sortorder field
*
* @param bool $showall also include issues that are configured to be shown only on login page
* @return \core\oauth2\issuer[]
*/
public static function get_all_issuers() {
return issuer::get_records([], 'sortorder');
public static function get_all_issuers(bool $showall = false) {
if ($showall) {
return issuer::get_records([], 'sortorder');
} else {
return array_values(issuer::get_records_select('showonloginpage<>?', [issuer::LOGINONLY], 'sortorder'));
}
}

/**
Expand Down
41 changes: 39 additions & 2 deletions lib/classes/oauth2/issuer.php
Expand Up @@ -36,6 +36,13 @@
*/
class issuer extends persistent {

/** @var int Issuer is displayed on both login page and in the services lists */
const EVERYWHERE = 1;
/** @var int Issuer is displayed on the login page only */
const LOGINONLY = 2;
/** @var int Issuer is displayed only in the services lists and can not be used for login */
const SERVICEONLY = 0;

const TABLE = 'oauth2_issuer';

/**
Expand Down Expand Up @@ -70,8 +77,8 @@ protected static function define_properties() {
'default' => true
),
'showonloginpage' => array(
'type' => PARAM_BOOL,
'default' => false
'type' => PARAM_INT,
'default' => self::SERVICEONLY,
),
'basicauth' => array(
'type' => PARAM_BOOL,
Expand Down Expand Up @@ -115,6 +122,11 @@ protected static function define_properties() {
'null' => NULL_ALLOWED,
'default' => null,
),
'loginpagename' => array(
'type' => PARAM_TEXT,
'null' => NULL_ALLOWED,
'default' => null,
),
);
}

Expand Down Expand Up @@ -173,9 +185,25 @@ public function is_valid_login_domain($email) {
* @return boolean
*/
public function is_authentication_supported() {
debugging('Method is_authentication_supported() is deprecated, please use is_available_for_login()',
DEBUG_DEVELOPER);
return (!empty($this->get_endpoint_url('userinfo')));
}

/**
* Is this issue fully configured and enabled and can be used for login/signup
*
* @return bool
* @throws \coding_exception
*/
public function is_available_for_login() {
return $this->get('id') &&
$this->is_configured() &&
$this->get('showonloginpage') != issuer::SERVICEONLY &&
$this->get('enabled') &&
!empty($this->get_endpoint_url('userinfo'));
}

/**
* Return true if this issuer looks like it has been configured.
*
Expand Down Expand Up @@ -231,4 +259,13 @@ protected function validate_baseurl($value) {
}
return true;
}

/**
* Display name for the issuers used on the login page
*
* @return string
*/
public function get_display_name() {
return $this->get('loginpagename') ? $this->get('loginpagename') : $this->get('name');
}
}

0 comments on commit 0152fdb

Please sign in to comment.