From 0c62ca25636b963ced0364689072e9ce1ebedad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Dagef=C3=B6rde?= Date: Thu, 23 Jun 2016 14:36:34 +0200 Subject: [PATCH 1/6] MDL-28574 webservice: Allow admins to manage tokens created by others. Until now, admins could only modify (or even see) tokens they have created themselves. Tokens created by other users or even other admins were invisible unless you were looking into the database. In case there are former admins, their successors can be unable to inspect or delete existing tokens. --- admin/webservice/tokens.php | 2 +- lib/adminlib.php | 21 ++++++++++++++------- webservice/lib.php | 28 ++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/admin/webservice/tokens.php b/admin/webservice/tokens.php index 60ea271005f..a1272231801 100644 --- a/admin/webservice/tokens.php +++ b/admin/webservice/tokens.php @@ -101,7 +101,7 @@ switch ($action) { break; case 'delete': - $token = $webservicemanager->get_created_by_user_ws_token($USER->id, $tokenid); + $token = $webservicemanager->get_token_by_id_with_details($tokenid); //Delete the token if ($confirm and confirm_sesskey()) { diff --git a/lib/adminlib.php b/lib/adminlib.php index 53866698d66..a8408feb8e1 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -9531,6 +9531,7 @@ class admin_setting_managewebservicetokens extends admin_setting { $strtoken = get_string('token', 'webservice'); $strservice = get_string('service', 'webservice'); $struser = get_string('user'); + $strcreator = get_string('tokencreator', 'webservice'); $strcontext = get_string('context', 'webservice'); $strvaliduntil = get_string('validuntil', 'webservice'); $striprestriction = get_string('iprestriction', 'webservice'); @@ -9538,8 +9539,8 @@ class admin_setting_managewebservicetokens extends admin_setting { $return = $OUTPUT->box_start('generalbox webservicestokenui'); $table = new html_table(); - $table->head = array($strtoken, $struser, $strservice, $striprestriction, $strvaliduntil, $stroperation); - $table->colclasses = array('leftalign', 'leftalign', 'leftalign', 'centeralign', 'centeralign', 'centeralign'); + $table->head = array($strtoken, $struser, $strservice, $striprestriction, $strvaliduntil, $strcreator, $stroperation); + $table->colclasses = array('leftalign', 'leftalign', 'leftalign', 'centeralign', 'centeralign', 'leftalign', 'centeralign'); $table->id = 'webservicetokens'; $table->attributes['class'] = 'admintable generaltable'; $table->data = array(); @@ -9549,10 +9550,11 @@ class admin_setting_managewebservicetokens extends admin_setting { //TODO: in order to let the administrator delete obsolete token, split this request in multiple request or use LEFT JOIN //here retrieve token list (including linked users firstname/lastname and linked services name) - $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, s.id AS serviceid - FROM {external_tokens} t, {user} u, {external_services} s - WHERE t.creatorid=? AND t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id"; - $tokens = $DB->get_records_sql($sql, array($USER->id, EXTERNAL_TOKEN_PERMANENT)); + $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, + s.id AS serviceid, c.id AS creatorid, c.username AS creator + FROM {external_tokens} t, {user} u, {external_services} s, {user} c + WHERE t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id AND t.creatorid = c.id"; + $tokens = $DB->get_records_sql($sql, array(EXTERNAL_TOKEN_PERMANENT)); if (!empty($tokens)) { foreach ($tokens as $token) { //TODO: retrieve context @@ -9594,7 +9596,12 @@ class admin_setting_managewebservicetokens extends admin_setting { } } - $table->data[] = array($token->token, $useratag, $token->name, $iprestriction, $validuntil, $delete); + $creatorprofileurl = new moodle_url('/user/profile.php?id='.$token->creatorid); + $creatoratag = html_writer::start_tag('a', array('href' => $creatorprofileurl)); + $creatoratag .= $token->creator; + $creatoratag .= html_writer::end_tag('a'); + + $table->data[] = array($token->token, $useratag, $token->name, $iprestriction, $validuntil, $creatoratag, $delete); } $return .= html_writer::table($table); diff --git a/webservice/lib.php b/webservice/lib.php index 0101ccacded..89e1499ab0e 100644 --- a/webservice/lib.php +++ b/webservice/lib.php @@ -402,6 +402,34 @@ class webservice { return $token; } + /** + * Return a token of an arbitrary user by tokenid, including details of the associated user and the service name. + * If no tokens exist an exception is thrown + * + * The returned value is a stdClass: + * ->id token id + * ->token + * ->firstname user firstname + * ->lastname + * ->name service name + * + * @param int $tokenid token id + * @return stdClass + */ + public function get_token_by_id_with_details($tokenid) { + global $DB; + $sql = "SELECT + t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name + FROM + {external_tokens} t, {user} u, {external_services} s + WHERE + t.id=? AND t.tokentype = " + . EXTERNAL_TOKEN_PERMANENT + . " AND s.id = t.externalserviceid AND t.userid = u.id"; + $token = $DB->get_record_sql($sql, array($tokenid), MUST_EXIST); + return $token; + } + /** * Return a database token record for a token id * From ad2ae6e3dbd2dcfc97883b87a399a57ab3979b38 Mon Sep 17 00:00:00 2001 From: John Okely Date: Mon, 3 Jul 2017 16:21:33 +0800 Subject: [PATCH 2/6] MDL-28574 webservices: Add new capability for managing all tokens --- admin/webservice/tokens.php | 4 ++++ lang/en/role.php | 2 +- lib/db/access.php | 7 +++++++ version.php | 2 +- webservice/lib.php | 13 ++++--------- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/admin/webservice/tokens.php b/admin/webservice/tokens.php index a1272231801..f15f7c20578 100644 --- a/admin/webservice/tokens.php +++ b/admin/webservice/tokens.php @@ -103,6 +103,10 @@ switch ($action) { case 'delete': $token = $webservicemanager->get_token_by_id_with_details($tokenid); + if ($token->creatorid != $USER->id) { + require_capability("moodle/webservice:managealltokens", context_system::instance()); + } + //Delete the token if ($confirm and confirm_sesskey()) { $webservicemanager->delete_user_ws_token($token->id); diff --git a/lang/en/role.php b/lang/en/role.php index 45870a2f0ba..2bfc5df9fdd 100644 --- a/lang/en/role.php +++ b/lang/en/role.php @@ -455,9 +455,9 @@ $string['useshowadvancedtochange'] = 'Use \'Show advanced\' to change'; $string['viewingdefinitionofrolex'] = 'Viewing the definition of role \'{$a}\''; $string['viewrole'] = 'View role details'; $string['webservice:createtoken'] = 'Create a web service token'; +$string['webservice:managealltokens'] = 'Manage all users\' web services'; $string['webservice:createmobiletoken'] = 'Create a web service token for mobile access'; $string['whydoesuserhavecap'] = 'Why does {$a->fullname} have capability {$a->capability} in context {$a->context}?'; $string['whydoesusernothavecap'] = 'Why does {$a->fullname} not have capability {$a->capability} in context {$a->context}?'; $string['xroleassignments'] = '{$a}\'s role assignments'; $string['xuserswiththerole'] = 'Users with the role "{$a->role}"'; - diff --git a/lib/db/access.php b/lib/db/access.php index 808d0b9c1ea..86c450ca279 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -1845,6 +1845,13 @@ $capabilities = array( 'manager' => CAP_ALLOW ) ), + 'moodle/webservice:managealltokens' => array( + + 'riskbitmask' => RISK_CONFIG | RISK_DATALOSS | RISK_PERSONAL, + 'captype' => 'write', + 'contextlevel' => CONTEXT_SYSTEM, + 'archetypes' => array() + ), 'moodle/webservice:createmobiletoken' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, diff --git a/version.php b/version.php index 7ae262ab3e4..cf1bb369b02 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2017072700.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2017072700.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. diff --git a/webservice/lib.php b/webservice/lib.php index 89e1499ab0e..2117e8bc28e 100644 --- a/webservice/lib.php +++ b/webservice/lib.php @@ -418,15 +418,10 @@ class webservice { */ public function get_token_by_id_with_details($tokenid) { global $DB; - $sql = "SELECT - t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name - FROM - {external_tokens} t, {user} u, {external_services} s - WHERE - t.id=? AND t.tokentype = " - . EXTERNAL_TOKEN_PERMANENT - . " AND s.id = t.externalserviceid AND t.userid = u.id"; - $token = $DB->get_record_sql($sql, array($tokenid), MUST_EXIST); + $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.creatorid + FROM {external_tokens} t, {user} u, {external_services} s + WHERE t.id=? AND t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id"; + $token = $DB->get_record_sql($sql, array($tokenid, EXTERNAL_TOKEN_PERMANENT), MUST_EXIST); return $token; } From 031877d2eeaec2d57a6e001ee8d261a9f08ddaa9 Mon Sep 17 00:00:00 2001 From: John Okely Date: Mon, 3 Jul 2017 16:27:13 +0800 Subject: [PATCH 3/6] MDL-28574 webservices: Show all tokens for users with capability --- lib/adminlib.php | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/lib/adminlib.php b/lib/adminlib.php index a8408feb8e1..123235efde0 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -9538,8 +9538,14 @@ class admin_setting_managewebservicetokens extends admin_setting { $return = $OUTPUT->box_start('generalbox webservicestokenui'); + $showalltokens = has_capability('moodle/webservice:managealltokens', context_system::instance()); + $table = new html_table(); - $table->head = array($strtoken, $struser, $strservice, $striprestriction, $strvaliduntil, $strcreator, $stroperation); + if ($showalltokens) { + $table->head = array($strtoken, $struser, $strservice, $striprestriction, $strvaliduntil, $strcreator, $stroperation); + } else { + $table->head = array($strtoken, $struser, $strservice, $striprestriction, $strvaliduntil, $stroperation); + } $table->colclasses = array('leftalign', 'leftalign', 'leftalign', 'centeralign', 'centeralign', 'leftalign', 'centeralign'); $table->id = 'webservicetokens'; $table->attributes['class'] = 'admintable generaltable'; @@ -9549,12 +9555,22 @@ class admin_setting_managewebservicetokens extends admin_setting { //TODO: in order to let the administrator delete obsolete token, split this request in multiple request or use LEFT JOIN + $params = []; //here retrieve token list (including linked users firstname/lastname and linked services name) - $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, - s.id AS serviceid, c.id AS creatorid, c.username AS creator - FROM {external_tokens} t, {user} u, {external_services} s, {user} c - WHERE t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id AND t.creatorid = c.id"; - $tokens = $DB->get_records_sql($sql, array(EXTERNAL_TOKEN_PERMANENT)); + if ($showalltokens) { + // Show all tokens. + $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, s.id AS serviceid, t.creatorid, c.username creator + FROM {external_tokens} t, {user} u, {external_services} s, {user} c + WHERE t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id AND c.id = t.creatorid"; + $params = [EXTERNAL_TOKEN_PERMANENT]; + } else { + // Only show tokens created by the current user. + $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, s.id AS serviceid, t.creatorid, c.username creator + FROM {external_tokens} t, {user} u, {external_services} s, {user} c + WHERE t.creatorid=? AND t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id AND c.id = t.creatorid"; + $params = [$USER->id, EXTERNAL_TOKEN_PERMANENT]; + } + $tokens = $DB->get_records_sql($sql, $params); if (!empty($tokens)) { foreach ($tokens as $token) { //TODO: retrieve context @@ -9601,7 +9617,16 @@ class admin_setting_managewebservicetokens extends admin_setting { $creatoratag .= $token->creator; $creatoratag .= html_writer::end_tag('a'); - $table->data[] = array($token->token, $useratag, $token->name, $iprestriction, $validuntil, $creatoratag, $delete); + if ($token->creatorid != $USER->id) { // TODO: is creatorid NOT NULL? What if it's NULL? + $token->token = ''; + // TODO: Should I also remove permissions? + } + + if ($showalltokens) { + $table->data[] = array($token->token, $useratag, $token->name, $iprestriction, $validuntil, $creatoratag, $delete); + } else { + $table->data[] = array($token->token, $useratag, $token->name, $iprestriction, $validuntil, $delete); + } } $return .= html_writer::table($table); From fc7a345c35dfd83af0c71f83cfe2d0b3d3de8e41 Mon Sep 17 00:00:00 2001 From: John Okely Date: Thu, 20 Jul 2017 15:48:01 +0800 Subject: [PATCH 4/6] MDL-28574 webservices: Use table_sql on manage tokens page For pagination and sorting an partial loading. --- lib/adminlib.php | 113 ++----------- webservice/classes/token_table.php | 260 +++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+), 102 deletions(-) create mode 100644 webservice/classes/token_table.php diff --git a/lib/adminlib.php b/lib/adminlib.php index 123235efde0..68ec79f7f48 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -9524,116 +9524,25 @@ class admin_setting_managewebservicetokens extends admin_setting { * @return string */ public function output_html($data, $query='') { - global $CFG, $OUTPUT, $DB, $USER; + global $CFG, $OUTPUT; - // display strings - $stroperation = get_string('operation', 'webservice'); - $strtoken = get_string('token', 'webservice'); - $strservice = get_string('service', 'webservice'); - $struser = get_string('user'); - $strcreator = get_string('tokencreator', 'webservice'); - $strcontext = get_string('context', 'webservice'); - $strvaliduntil = get_string('validuntil', 'webservice'); - $striprestriction = get_string('iprestriction', 'webservice'); + require_once($CFG->dirroot . '/webservice/classes/token_table.php'); + $baseurl = new moodle_url('/' . $CFG->admin . '/settings.php?section=webservicetokens'); $return = $OUTPUT->box_start('generalbox webservicestokenui'); - $showalltokens = has_capability('moodle/webservice:managealltokens', context_system::instance()); - - $table = new html_table(); - if ($showalltokens) { - $table->head = array($strtoken, $struser, $strservice, $striprestriction, $strvaliduntil, $strcreator, $stroperation); - } else { - $table->head = array($strtoken, $struser, $strservice, $striprestriction, $strvaliduntil, $stroperation); - } - $table->colclasses = array('leftalign', 'leftalign', 'leftalign', 'centeralign', 'centeralign', 'leftalign', 'centeralign'); - $table->id = 'webservicetokens'; - $table->attributes['class'] = 'admintable generaltable'; + $table = new \webservice\token_table('webservicetokens'); + $table->define_baseurl($baseurl); + $table->attributes['class'] = 'admintable generaltable'; // Any need changing? $table->data = array(); + ob_start(); + $table->out(10, false); + $tablehtml = ob_get_contents(); + ob_end_clean(); + $return .= $tablehtml; $tokenpageurl = "$CFG->wwwroot/$CFG->admin/webservice/tokens.php?sesskey=" . sesskey(); - //TODO: in order to let the administrator delete obsolete token, split this request in multiple request or use LEFT JOIN - - $params = []; - //here retrieve token list (including linked users firstname/lastname and linked services name) - if ($showalltokens) { - // Show all tokens. - $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, s.id AS serviceid, t.creatorid, c.username creator - FROM {external_tokens} t, {user} u, {external_services} s, {user} c - WHERE t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id AND c.id = t.creatorid"; - $params = [EXTERNAL_TOKEN_PERMANENT]; - } else { - // Only show tokens created by the current user. - $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, s.id AS serviceid, t.creatorid, c.username creator - FROM {external_tokens} t, {user} u, {external_services} s, {user} c - WHERE t.creatorid=? AND t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id AND c.id = t.creatorid"; - $params = [$USER->id, EXTERNAL_TOKEN_PERMANENT]; - } - $tokens = $DB->get_records_sql($sql, $params); - if (!empty($tokens)) { - foreach ($tokens as $token) { - //TODO: retrieve context - - $delete = "id."\">"; - $delete .= get_string('delete').""; - - $validuntil = ''; - if (!empty($token->validuntil)) { - $validuntil = userdate($token->validuntil, get_string('strftimedatetime', 'langconfig')); - } - - $iprestriction = ''; - if (!empty($token->iprestriction)) { - $iprestriction = $token->iprestriction; - } - - $userprofilurl = new moodle_url('/user/profile.php?id='.$token->userid); - $useratag = html_writer::start_tag('a', array('href' => $userprofilurl)); - $useratag .= $token->firstname." ".$token->lastname; - $useratag .= html_writer::end_tag('a'); - - //check user missing capabilities - require_once($CFG->dirroot . '/webservice/lib.php'); - $webservicemanager = new webservice(); - $usermissingcaps = $webservicemanager->get_missing_capabilities_by_users( - array(array('id' => $token->userid)), $token->serviceid); - - if (!is_siteadmin($token->userid) and - array_key_exists($token->userid, $usermissingcaps)) { - $missingcapabilities = implode(', ', - $usermissingcaps[$token->userid]); - if (!empty($missingcapabilities)) { - $useratag .= html_writer::tag('div', - get_string('usermissingcaps', 'webservice', - $missingcapabilities) - . ' ' . $OUTPUT->help_icon('missingcaps', 'webservice'), - array('class' => 'missingcaps')); - } - } - - $creatorprofileurl = new moodle_url('/user/profile.php?id='.$token->creatorid); - $creatoratag = html_writer::start_tag('a', array('href' => $creatorprofileurl)); - $creatoratag .= $token->creator; - $creatoratag .= html_writer::end_tag('a'); - - if ($token->creatorid != $USER->id) { // TODO: is creatorid NOT NULL? What if it's NULL? - $token->token = ''; - // TODO: Should I also remove permissions? - } - - if ($showalltokens) { - $table->data[] = array($token->token, $useratag, $token->name, $iprestriction, $validuntil, $creatoratag, $delete); - } else { - $table->data[] = array($token->token, $useratag, $token->name, $iprestriction, $validuntil, $delete); - } - } - - $return .= html_writer::table($table); - } else { - $return .= get_string('notoken', 'webservice'); - } - $return .= $OUTPUT->box_end(); // add a token to the table $return .= ""; diff --git a/webservice/classes/token_table.php b/webservice/classes/token_table.php new file mode 100644 index 00000000000..fba6aa2787e --- /dev/null +++ b/webservice/classes/token_table.php @@ -0,0 +1,260 @@ +. + +/** + * Contains the class used for the displaying the tokens table. + * + * @package core_webservice + * @copyright 2017 John Okely + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace webservice; + +defined('MOODLE_INTERNAL') || die; + +require_once($CFG->libdir . '/tablelib.php'); +require_once($CFG->dirroot . '/webservice/lib.php'); +require_once($CFG->dirroot . '/user/lib.php'); + +/** + * Class for the displaying the participants table. + * + * @package core_webservice + * @copyright 2017 John Okely + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class token_table extends \table_sql { + + /** + * @var bool $showalltokens Whether or not the user is able to see all tokens. + */ + protected $showalltokens; + + /** + * Sets up the table. + * @param int $id The id of the table + */ + public function __construct($id) { + parent::__construct($id); + + // Get the context. + $context = \context_system::instance(); + + // Can we see tokens created by all users? + $this->showalltokens = has_capability('moodle/webservice:managealltokens', $context); + + // Define the headers and columns. + $headers = []; + $columns = []; + + $headers[] = get_string('token', 'webservice'); + $columns[] = 'token'; + $headers[] = get_string('user'); + $columns[] = 'fullname'; + $headers[] = get_string('service', 'webservice'); + $columns[] = 'name'; + $headers[] = get_string('iprestriction', 'webservice'); + $columns[] = 'iprestriction'; + $headers[] = get_string('validuntil', 'webservice'); + $columns[] = 'validuntil'; + if ($this->showalltokens) { + // Only need to show creator if you can see tokens created by other people. + $headers[] = get_string('tokencreator', 'webservice'); + $columns[] = 'creatorlastname'; // So we can have semi-useful sorting. Table SQL doesn't two fullname collumns. + } + $headers[] = get_string('operation', 'webservice'); + $columns[] = 'operation'; + + $this->define_columns($columns); + $this->define_headers($headers); + + $this->no_sorting('operation'); + $this->no_sorting('token'); + $this->no_sorting('iprestriction'); + + $this->set_attribute('id', $id); + } + + /** + * Generate the operation column. + * + * @param \stdClass $data Data for the current row + * @return string Content for the column + */ + public function col_operation($data) { + $tokenpageurl = new \moodle_url( + "/admin/webservice/tokens.php", + [ + "sesskey" => sesskey(), + "action" => "delete", + "tokenid" => $data->id + ] + ); + return \html_writer::link($tokenpageurl, get_string("delete")); + } + + /** + * Generate the validuntil column. + * + * @param \stdClass $data Data for the current row + * @return string Content for the column + */ + public function col_validuntil($data) { + if (empty($data->validuntil)) { + return ''; + } else { + return userdate($data->validuntil, get_string('strftimedatetime', 'langconfig')); + } + } + + /** + * Generate the fullname column. Also includes capabilities the user is missing for the webservice (if any) + * + * @param \stdClass $data Data for the current row + * @return string Content for the column + */ + public function col_fullname($data) { + global $OUTPUT; + + $userprofilurl = new \moodle_url('/user/profile.php', ['id' => $data->userid]); + $content = \html_writer::link($userprofilurl, fullname($data)); + + // Make up list of capabilities that the user is missing for the given webservice. + $webservicemanager = new \webservice(); + $usermissingcaps = $webservicemanager->get_missing_capabilities_by_users([['id' => $data->userid]], $data->serviceid); + + if (!is_siteadmin($data->userid) && array_key_exists($data->userid, $usermissingcaps)) { + $missingcapabilities = implode(', ', $usermissingcaps[$data->userid]); + if (!empty($missingcapabilities)) { + $capabilitiesstring = get_string('usermissingcaps', 'webservice', $missingcapabilities) . ' ' . + $OUTPUT->help_icon('missingcaps', 'webservice'); + $content .= \html_writer::div($capabilitiesstring, 'missingcaps'); + } + } + + return $content; + } + + /** + * Generate the token column. + * + * @param \stdClass $data Data for the current row + * @return string Content for the column + */ + public function col_token($data) { + global $USER; + // Hide the token if it wasn't created by the current user. + if ($data->creatorid != $USER->id) { + return ''; + } + + return $data->token; + } + + /** + * Generate the creator column. + * + * @param \stdClass $data + * @return string + */ + public function col_creatorlastname($data) { + // We have loaded all the name fields for the creator, with the 'creator' prefix. + // So just remove the prefix and make up a user object. + $user = []; + foreach ($data as $key => $value) { + if (strpos($key, 'creator') !== false) { + $newkey = str_replace('creator', '', $key); + $user[$newkey] = $value; + } + } + + $creatorprofileurl = new \moodle_url('/user/profile.php', ['id' => $data->creatorid]); + return \html_writer::link($creatorprofileurl, fullname((object)$user)); + } + + /** + * This function is used for the extra user fields. + * + * These are being dynamically added to the table so there are no functions 'col_' as + * the list has the potential to increase in the future and we don't want to have to remember to add + * a new method to this class. We also don't want to pollute this class with unnecessary methods. + * + * @param string $colname The column name + * @param \stdClass $data + * @return string + */ + public function other_cols($colname, $data) { + return s($data->{$colname}); + } + + /** + * Query the database for results to display in the table. + * + * Note: Initial bars are not implemented for this table because it includes user details twice and the initial bars do not work + * when the user table is included more than once. + * + * @param int $pagesize size of page for paginated displayed table. + * @param bool $useinitialsbar Not implemented. Please pass false. + */ + public function query_db($pagesize, $useinitialsbar = false) { + global $DB, $USER; + + if ($useinitialsbar) { + debugging('Initial bar not implemented yet. Call out($pagesize, false)'); + } + + $usernamefields = get_all_user_name_fields(true, 'u'); + $creatorfields = get_all_user_name_fields(true, 'c', null, 'creator'); + + $params = ["tokenmode" => EXTERNAL_TOKEN_PERMANENT]; + + // TODO: in order to let the administrator delete obsolete token, split the request in multiple request or use LEFT JOIN. + + if ($this->showalltokens) { + // Show all tokens. + $sql = "SELECT t.id, t.token, u.id AS userid, $usernamefields, s.name, t.iprestriction, t.validuntil, s.id AS serviceid, + t.creatorid, $creatorfields + FROM {external_tokens} t, {user} u, {external_services} s, {user} c + WHERE t.tokentype = :tokenmode AND s.id = t.externalserviceid AND t.userid = u.id AND c.id = t.creatorid"; + $countsql = "SELECT COUNT(t.id) + FROM {external_tokens} t, {user} u, {external_services} s, {user} c + WHERE t.tokentype = :tokenmode AND s.id = t.externalserviceid AND t.userid = u.id AND c.id = t.creatorid"; + } else { + // Only show tokens created by the current user. + $sql = "SELECT t.id, t.token, u.id AS userid, $usernamefields, s.name, t.iprestriction, t.validuntil, s.id AS serviceid, + t.creatorid, $creatorfields + FROM {external_tokens} t, {user} u, {external_services} s, {user} c + WHERE t.creatorid=:userid AND t.tokentype = :tokenmode AND s.id = t.externalserviceid AND t.userid = u.id AND + c.id = t.creatorid"; + $countsql = "SELECT COUNT(t.id) + FROM {external_tokens} t, {user} u, {external_services} s, {user} c + WHERE t.creatorid=:userid AND t.tokentype = :tokenmode AND s.id = t.externalserviceid AND + t.userid = u.id AND c.id = t.creatorid"; + $params["userid"] = $USER->id; + } + + $sort = $this->get_sql_sort(); + if ($sort) { + $sql = $sql . ' ORDER BY ' . $sort; + } + + $total = $DB->count_records_sql($countsql, $params); + $this->pagesize($pagesize, $total); + + $this->rawdata = $DB->get_recordset_sql($sql, $params, $this->get_page_start(), $this->get_page_size()); + } +} From 11df55c0dd2a087845c7d92aea5c3879d299257c Mon Sep 17 00:00:00 2001 From: John Okely Date: Fri, 21 Jul 2017 11:23:32 +0800 Subject: [PATCH 5/6] MDL-28574 adminlib: Token behat tests --- admin/tests/behat/manage_tokens.feature | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 admin/tests/behat/manage_tokens.feature diff --git a/admin/tests/behat/manage_tokens.feature b/admin/tests/behat/manage_tokens.feature new file mode 100644 index 00000000000..d30e2300bd1 --- /dev/null +++ b/admin/tests/behat/manage_tokens.feature @@ -0,0 +1,26 @@ +@core @core_admin +Feature: Manage tokens + In order to manage webservice usage + As an admin + I need to be able to create and delete tokens + + Background: + Given the following "users" exist: + | username | password | firstname | lastname | + | testuser | testuser | Joe | Bloggs | + | testuser2 | testuser2 | TestFirstname | TestLastname | + And I log in as "admin" + And I am on site homepage + + @javascript + Scenario: Add & delete a token + Given I navigate to "Plugins > Web services > Manage tokens" in site administration + And I follow "Add" + And I set the field "User" to "Joe Bloggs" + And I set the field "IP restriction" to "127.0.0.1" + When I press "Save changes" + Then I should see "Joe Bloggs" + And I should see "127.0.0.1" + And I follow "Delete" + And I press "Delete" + And I should not see "Joe Bloggs" From 72e688d7ff6f08cd9c76c1185c8174cae1fdcf97 Mon Sep 17 00:00:00 2001 From: John Okely Date: Mon, 31 Jul 2017 15:31:35 +0800 Subject: [PATCH 6/6] MDL-28574 webservices: Explain tokens you can edit but can't see --- lang/en/webservice.php | 1 + lib/adminlib.php | 4 ++++ webservice/classes/token_table.php | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lang/en/webservice.php b/lang/en/webservice.php index ebfe9384082..057f9f609b1 100644 --- a/lang/en/webservice.php +++ b/lang/en/webservice.php @@ -131,6 +131,7 @@ $string['norequiredcapability'] = 'No required capability'; $string['notoken'] = 'The token list is empty.'; $string['onesystemcontrolling'] = 'Allow an external system to control Moodle'; $string['onesystemcontrollingdescription'] = 'The following steps help you to set up the Moodle web services to allow an external system to interact with Moodle. This includes setting up a token (security key) authentication method.'; +$string['onlyseecreatedtokens'] = 'Only tokens you own or created can be seen. You can still delete other tokens.'; $string['operation'] = 'Operation'; $string['optional'] = 'Optional'; $string['passwordisexpired'] = 'Password is expired.'; diff --git a/lib/adminlib.php b/lib/adminlib.php index 68ec79f7f48..410130ec38a 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -9531,6 +9531,10 @@ class admin_setting_managewebservicetokens extends admin_setting { $return = $OUTPUT->box_start('generalbox webservicestokenui'); + if (has_capability('moodle/webservice:managealltokens', context_system::instance())) { + $return .= \html_writer::div(get_string('onlyseecreatedtokens', 'webservice')); + } + $table = new \webservice\token_table('webservicetokens'); $table->define_baseurl($baseurl); $table->attributes['class'] = 'admintable generaltable'; // Any need changing? diff --git a/webservice/classes/token_table.php b/webservice/classes/token_table.php index fba6aa2787e..059fc049d8f 100644 --- a/webservice/classes/token_table.php +++ b/webservice/classes/token_table.php @@ -159,7 +159,7 @@ class token_table extends \table_sql { global $USER; // Hide the token if it wasn't created by the current user. if ($data->creatorid != $USER->id) { - return ''; + return '-'; } return $data->token;