diff --git a/htdocs/auth/webservice/db/install.xml b/htdocs/auth/webservice/db/install.xml index a4746d477c4c158c4f0746ecf955239843ae02f5..209adb51b1a8c3550209f37c0bf42fe986471b1a 100644 --- a/htdocs/auth/webservice/db/install.xml +++ b/htdocs/auth/webservice/db/install.xml @@ -58,7 +58,7 @@ - + @@ -175,7 +175,7 @@ - + diff --git a/htdocs/auth/webservice/db/upgrade.php b/htdocs/auth/webservice/db/upgrade.php index b787a1b984b2dc820cd254722b58e46b84bc28e6..178a4eeb896c42cc2ae42f557a0616b6f4dbafb9 100644 --- a/htdocs/auth/webservice/db/upgrade.php +++ b/htdocs/auth/webservice/db/upgrade.php @@ -564,6 +564,20 @@ function xmldb_auth_webservice_upgrade($oldversion=0) { add_field($table, $field); } + if ($oldversion < 2016101100) { + log_debug('Make external_tokens.institution nullable'); + $table = new XMLDBTable('external_tokens'); + $field = new XMLDBField('institution'); + $field->setAttributes(XMLDB_TYPE_CHAR, 255, null, null); + change_field_notnull($table, $field, false); + + log_debug('Allow null institution in external_services_logs'); + $table = new XMLDBTable('external_services_logs'); + $field = new XMLDBField('institution'); + $field->setAttributes(XMLDB_TYPE_CHAR, 255); + change_field_notnull($table, $field, false); + } + // sweep for webservice updates everytime $status = external_reload_webservices(); diff --git a/htdocs/auth/webservice/version.php b/htdocs/auth/webservice/version.php index 4a4c9bb8512e3bcce217599c6906a25ee5e296be..5413cf04727c67ae973fb1c35c4721e9be872875 100644 --- a/htdocs/auth/webservice/version.php +++ b/htdocs/auth/webservice/version.php @@ -12,7 +12,7 @@ defined('INTERNAL') || die(); $config = new stdClass(); -$config->version = 2016090700; -$config->release = '2.0.0'; +$config->version = 2016101100; +$config->release = '2.0.1'; $config->requires_config = 0; $config->requires_parent = 0; diff --git a/htdocs/module/mobileapi/apps.php b/htdocs/module/mobileapi/apps.php new file mode 100644 index 0000000000000000000000000000000000000000..8854d3a0c0a9bb6f61a1482889a58cdc44f64472 --- /dev/null +++ b/htdocs/module/mobileapi/apps.php @@ -0,0 +1,295 @@ +docroot . 'webservice/lib.php'); +safe_require('module', 'mobileapi'); +define('TITLE', get_string('mytokenspagetitle', 'module.mobileapi')); + +// Users shouldn't be able to access this page if webservices are not enabled. +if (!PluginModuleMobileapi::is_service_ready()) { + throw new AccessDeniedException(get_string('featuredisabled', 'auth.webservice')); +} + +// get the list of services that are available for User Access Tokens usage +// determine if there is a corresponding token for the service +$dbservices = get_records_sql_array( + "SELECT + es.id || '_' || et.id || '_' || es.id as dispid, + es.id, + es.name, + es.enabled, + es.restrictedusers, + et.token, + " . db_format_tsfield('et.mtime', 'token_mtime') . ', + ' . db_format_tsfield('et.ctime', 'token_ctime') . ', + et.institution, + et.validuntil as token_validuntil, + et.clientname, + et.clientenv, + esu.validuntil as user_validuntil, + esu.iprestriction + FROM + {external_services} es + LEFT JOIN {external_tokens} et + ON et.externalserviceid = es.id + AND et.userid = ? + AND et.tokentype = ? + LEFT JOIN {external_services_users} esu + ON esu.externalserviceid = es.id + AND esu.userid = ? + WHERE + es.tokenusers = 1 + AND (es.restrictedusers = 0 OR esu.id IS NOT NULL) + AND (et.id IS NOT NULL OR esu.id IS NOT NULL)' + ,array( + $USER->get('id'), + EXTERNAL_TOKEN_USER, + $USER->get('id') + ) +); + +/* + * display the access tokens for services + */ +if (empty($dbservices)) { + $userform = get_string('nopersonaltokens', 'module.mobileapi'); +} +else { + $userform = array( + 'name' => 'webservices_user_tokens', + 'elementclasses' => false, + 'successcallback' => 'webservices_user_tokens_submit', + 'renderer' => 'multicolumntable', + ); + $elements = array(); + $elements['client_info'] = array( + 'title' => ' ', + 'datatable' => true, + 'type' => 'html', + 'value' => get_string('clientinfo', 'module.mobileapi'), + ); + + if (get_config_plugin('module', 'mobileapi', 'manualtokens')) { + $elements['token'] = array( + 'title' => ' ', + 'datatable' => true, + 'type' => 'html', + 'value' => get_string('token', 'module.mobileapi'), + ); + } + + $elements['created'] = array( + 'title' => ' ', + 'datatable' => true, + 'type' => 'html', + 'value' => get_string('tokencreated', 'module.mobileapi'), + ); + + // Action buttons (no title) + $elements['actions'] = array( + 'title' => ' ', + 'datatable' => true, + 'type' => 'html', + 'value' => '', + ); + $userform['elements'] = $elements; + + foreach ($dbservices as $service) { + + $client = '

'; + if ($service->clientname) { + $client .= $service->clientname; + } + else { + $client .= get_string('clientnotspecified', 'module.mobileapi'); + } + $client .= '

'; + + if ($service->clientenv) { + $client .= " ({$service->clientenv})"; + } + + // information about the client that generated it + $userform['elements']['id' . $service->dispid . '_client_info'] = array( + 'value' => $client, + 'type' => 'html', + 'key' => $service->dispid, + ); + + if (get_config_plugin('module', 'mobileapi', 'manualtokens')) { + $userform['elements']['id' . $service->dispid . '_token'] = array( + 'value' => $service->token, + 'type' => 'html', + 'key' => $service->dispid, + ); + } + + $userform['elements']['id' . $service->dispid . '_ctime'] = array( + 'value' => format_date($service->token_ctime), + 'type' => 'html', + 'key' => $service->dispid, + ); + + // generate button + // delete button + $userform['elements']['id' . $service->dispid . '_actions'] = array( + 'value' => pieform( + array( + 'name' => 'webservices_user_token_delete_' . $service->dispid, + 'renderer' => 'div', + 'elementclasses' => false, + 'successcallback' => 'webservices_user_token_submit', + 'class' => 'form-as-button pull-left', + 'jsform' => false, + 'elements' => array( + 'token' => array('type' => 'hidden', 'value' => $service->token), + 'action' => array('type' => 'hidden', 'value' => 'delete'), + 'submit' => array( + 'type' => 'button', + 'usebuttontag' => true, + 'class' => 'btn-default btn-sm', + 'value' => '' . get_string('delete'), + 'elementtitle' => get_string('deletespecific', 'mahara', $service->dispid), + ), + ), + ) + ), + 'type' => 'html', + 'key' => $service->dispid, + 'class' => 'webserviceconfigcontrols' . (empty($service->token) ? ' only-button only-button-top' : ''), + ); + } + $pieform = pieform_instance($userform); + $userform = $pieform->build(false); +} + +$page_elements = array( + // fieldset for managing service function list + 'user_tokens' => array( + 'type' => 'fieldset', + 'legend' => get_string('mytokenspagedesc', 'module.mobileapi'), + 'elements' => array( + 'sflist' => array( + 'type' => 'html', + 'value' => $userform, + ) + ), + 'collapsible' => false, + ) +); + +// TODO: Currently this is hardcoded to only allow self-generation of the +// maharamobile service. +$service = get_record('external_services', 'component', 'module/mobileapi', 'shortname', 'maharamobile'); +if (get_config_plugin('module', 'mobileapi', 'manualtokens')) { + $page_elements['generate_user_token'] = array( + 'type' => 'fieldset', + 'legend' => get_string('generateusertoken', 'module.mobileapi'), + 'elements' => array( + 'generate_user_token_html' => array( + 'type' => 'html', + 'value' => pieform( + array( + 'name' => 'webservices_user_token_generate_' . $service->id, + 'renderer' => 'div', + 'elementclasses' => false, + 'successcallback' => 'webservices_user_token_submit', + 'class' => 'form-as-button pull-left', + 'jsform' => false, + 'elements' => array( + 'action' => array('type' => 'hidden', 'value' => 'generate'), + 'submit' => array( + 'type' => 'button', + 'usebuttontag' => true, + 'class' => 'btn-default btn-sm', + 'value' => ' ' . get_string('gen', 'auth.webservice'), + 'elementtitle' => get_string('gen', 'auth.webservice') + ), + ), + ) + ) + ) + ) + ); +} + +$form = array( + 'renderer' => 'div', + 'type' => 'div', + 'id' => 'maintable', + 'name' => 'maincontainer', + 'dieaftersubmit' => false, + 'successcallback' => 'webservice_main_submit', + 'elements' => $page_elements, +); + +/** + * handle the callback for actions on the user token panel + * - generate noew token + * - delete token + * + * @param Pieform $form + * @param array $values + */ +function webservices_user_token_submit(Pieform $form, $values) { + global $USER, $SESSION; + if ($values['action'] == 'generate') { + // TODO: Currently this is hard-coded to only the maharamobile service + if ( + get_config_plugin('module', 'mobileapi', 'manualtokens') + && ($service = get_record('external_services', 'component', 'module/mobileapi', 'shortname', 'maharamobile', 'tokenusers', 1)) + ) { + $token = webservice_generate_token( + EXTERNAL_TOKEN_USER, + $service, + $USER->get('id'), + null, + null, + null, + get_string('tokenmanuallycreated', 'auth.webservice') + ); + $SESSION->add_ok_msg(get_string('token_generated', 'auth.webservice')); + } + else { + $SESSION->add_error_msg(get_string('noservices', 'auth.webservice')); + } + } + else if ($values['action'] == 'delete') { + delete_records('external_tokens', 'userid', $USER->get('id'), 'token', $values['token']); + $SESSION->add_ok_msg(get_string('appaccessrevoked', 'module.mobileapi')); + } + redirect('/module/mobileapi/apps.php'); +} + +// render the page +$pieform = pieform_instance($form); +$form = $pieform->build(false); + +$smarty = smarty(); +setpageicon($smarty, 'icon-globe'); +safe_require('auth', 'webservice'); + +$smarty->assign('form', $form); + +$smarty->display('form.tpl'); diff --git a/htdocs/module/mobileapi/lang/en.utf8/module.mobileapi.php b/htdocs/module/mobileapi/lang/en.utf8/module.mobileapi.php index 49a0553cdbbb983ca46bc6b153dfbd786cf57e20..411f03dae3d5f121e34563971d4233668d48cee2 100644 --- a/htdocs/module/mobileapi/lang/en.utf8/module.mobileapi.php +++ b/htdocs/module/mobileapi/lang/en.utf8/module.mobileapi.php @@ -24,4 +24,17 @@ $string['noticenotenabled'] = 'The Mahara mobile apps API is not currentl $string['notreadylabel'] = 'Not ready'; $string['readylabel'] = 'Ready'; $string['restprotocolenabled'] = 'REST protocol enabled'; -$string['webserviceproviderenabled'] = 'Incoming web service requests allowed'; \ No newline at end of file +$string['webserviceproviderenabled'] = 'Incoming web service requests allowed'; + +// User management of webservice access tokens +$string['mytokensmenutitle'] = 'Apps'; +$string['mytokenspagetitle'] = 'Applications'; +$string['mytokenspagedesc'] = 'These applications can access your Mahara account.'; +$string['nopersonaltokens'] = 'You have not granted access to any applications.'; +$string['clientinfo'] = 'App'; +$string['token'] = 'Access Token'; +$string['tokencreated'] = 'Created'; +$string['tokenmanuallycreated'] = 'Manually created'; +$string['clientnotspecified'] = '(Unknown)'; +$string['generateusertoken'] = 'Generate an app access token'; +$string['appaccessrevoked'] = 'Access revoked'; \ No newline at end of file diff --git a/htdocs/module/mobileapi/lib.php b/htdocs/module/mobileapi/lib.php index d7b1c27f510cf85542af221d73f294bf131f5512..17d6c957c6c5fbe55dc72b875a6b0148e5be1005 100644 --- a/htdocs/module/mobileapi/lib.php +++ b/htdocs/module/mobileapi/lib.php @@ -143,5 +143,21 @@ class PluginModuleMobileapi extends PluginModule { return true; } + public static function right_nav_menu_items() { + if (PluginModuleMobileapi::is_service_ready()) { + return array( + 'settings/webservice' => array( + 'path' => 'settings/webservice', + 'url' => 'module/mobileapi/apps.php', + 'title' => get_string('mytokensmenutitle', 'module.mobileapi'), + 'weight' => 50, + 'iconclass' => 'flag' + ), + ); + } + else { + return array(); + } + } } diff --git a/htdocs/webservice/admin/index.php b/htdocs/webservice/admin/index.php index c998f99b3c03c5d89f7538c0f018db90e20d4d46..990c0b77d09401b21052ec2492634446fd4d1cb7 100644 --- a/htdocs/webservice/admin/index.php +++ b/htdocs/webservice/admin/index.php @@ -476,6 +476,8 @@ function webservice_provider_protocols_submit(Pieform $form, $values) { $enabled = $values['enabled'] ? 0 : 1; $proto = $values['protocol']; set_config('webservice_provider_'.$proto.'_enabled', $enabled); + // Show/hide the account settings webservice tokens page + clear_menu_cache(); if (param_boolean('ajax')) { exit; }