From d733a8ccc3919d721ecc87783e1390d870347284 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Fri, 20 Sep 2013 17:24:48 +0800 Subject: [PATCH] MDL-40050 webservice: Replace add_to_log() with new events --- admin/webservice/service.php | 21 +- admin/webservice/service_users.php | 18 +- lang/en/webservice.php | 8 + .../event/webservice_function_called.php | 101 ++++++ lib/classes/event/webservice_login_failed.php | 107 ++++++ .../event/webservice_service_created.php | 88 +++++ .../event/webservice_service_deleted.php | 88 +++++ .../event/webservice_service_updated.php | 97 +++++ .../event/webservice_service_user_added.php | 98 +++++ .../event/webservice_service_user_removed.php | 98 +++++ .../event/webservice_token_created.php | 100 +++++ lib/classes/event/webservice_token_sent.php | 76 ++++ login/token.php | 25 +- webservice/lib.php | 163 ++++++++- webservice/tests/events.php | 342 ++++++++++++++++++ 15 files changed, 1398 insertions(+), 32 deletions(-) create mode 100644 lib/classes/event/webservice_function_called.php create mode 100644 lib/classes/event/webservice_login_failed.php create mode 100644 lib/classes/event/webservice_service_created.php create mode 100644 lib/classes/event/webservice_service_deleted.php create mode 100644 lib/classes/event/webservice_service_updated.php create mode 100644 lib/classes/event/webservice_service_user_added.php create mode 100644 lib/classes/event/webservice_service_user_removed.php create mode 100644 lib/classes/event/webservice_token_created.php create mode 100644 lib/classes/event/webservice_token_sent.php create mode 100644 webservice/tests/events.php diff --git a/admin/webservice/service.php b/admin/webservice/service.php index a933ec4af411e..13f3c8d294ea4 100644 --- a/admin/webservice/service.php +++ b/admin/webservice/service.php @@ -58,7 +58,12 @@ } //The user has confirmed the deletion, delete and redirect $webservicemanager->delete_service($service->id); - add_to_log(SITEID, 'webservice', 'delete', $returnurl, get_string('deleteservice', 'webservice', $service)); + $params = array( + 'objectid' => $service->id + ); + $event = \core\event\webservice_service_deleted::create($params); + $event->add_record_snapshot('external_services', $service); + $event->trigger(); redirect($returnurl); } @@ -75,7 +80,12 @@ //create operation if (empty($servicedata->id)) { $servicedata->id = $webservicemanager->add_external_service($servicedata); - add_to_log(SITEID, 'webservice', 'add', $returnurl, get_string('addservice', 'webservice', $servicedata)); + $params = array( + 'objectid' => $servicedata->id + ); + $event = \core\event\webservice_service_updated::create($params); + $event->add_record_snapshot('external_services', $servicedata); + $event->trigger(); //redirect to the 'add functions to service' page $addfunctionpage = new moodle_url( @@ -85,7 +95,12 @@ } else { //update operation $webservicemanager->update_external_service($servicedata); - add_to_log(SITEID, 'webservice', 'edit', $returnurl, get_string('editservice', 'webservice', $servicedata)); + $params = array( + 'objectid' => $servicedata->id + ); + $event = \core\event\webservice_service_created::create($params); + $event->add_record_snapshot('external_services', $servicedata); + $event->trigger(); } redirect($returnurl); diff --git a/admin/webservice/service_users.php b/admin/webservice/service_users.php index ad67c6680e419..907a4cc9c1faf 100644 --- a/admin/webservice/service_users.php +++ b/admin/webservice/service_users.php @@ -57,8 +57,13 @@ $serviceuser->externalserviceid = $id; $serviceuser->userid = $adduser->id; $webservicemanager->add_ws_authorised_user($serviceuser); - add_to_log(SITEID, 'core', 'assign', $CFG->admin . '/webservice/service_users.php?id=' - . $id, 'add', '', $adduser->id); + + $params = array( + 'objectid' => $serviceuser->externalserviceid, + 'relateduserid' => $serviceuser->userid + ); + $event = \core\event\webservice_service_user_added::create($params); + $event->trigger(); } $potentialuserselector->invalidate_selected_users(); $alloweduserselector->invalidate_selected_users(); @@ -71,8 +76,13 @@ if (!empty($userstoremove)) { foreach ($userstoremove as $removeuser) { $webservicemanager->remove_ws_authorised_user($removeuser, $id); - add_to_log(SITEID, 'core', 'assign', $CFG->admin . '/webservice/service_users.php?id=' - . $id, 'remove', '', $removeuser->id); + + $params = array( + 'objectid' => $id, + 'relateduserid' => $removeuser->id + ); + $event = \core\event\webservice_service_user_removed::create($params); + $event->trigger(); } $potentialuserselector->invalidate_selected_users(); $alloweduserselector->invalidate_selected_users(); diff --git a/lang/en/webservice.php b/lang/en/webservice.php index 83cdb584e6f84..52966b24e6a14 100644 --- a/lang/en/webservice.php +++ b/lang/en/webservice.php @@ -82,6 +82,14 @@ $string['errorinvalidparam'] = 'The param "{$a}" is invalid.'; $string['errornotemptydefaultparamarray'] = 'The web service description parameter named \'{$a}\' is an single or multiple structure. The default can only be empty array. Check web service description.'; $string['erroroptionalparamarray'] = 'The web service description parameter named \'{$a}\' is an single or multiple structure. It can not be set as VALUE_OPTIONAL. Check web service description.'; +$string['event_webservice_function_called'] = 'Web service function called'; +$string['event_webservice_login_failed'] = 'Web service login failed'; +$string['event_webservice_service_created'] = 'Web service service created'; +$string['event_webservice_service_updated'] = 'Web service service updated'; +$string['event_webservice_service_user_added'] = 'Web service service user added'; +$string['event_webservice_service_user_removed'] = 'Web service service user removed'; +$string['event_webservice_token_created'] = 'Web service token created'; +$string['event_webservice_token_sent'] = 'Web service token sent'; $string['execute'] = 'Execute'; $string['executewarnign'] = 'WARNING: If you press execute your database will be modified and changes can not be reverted automatically!'; $string['externalservice'] = 'External service'; diff --git a/lib/classes/event/webservice_function_called.php b/lib/classes/event/webservice_function_called.php new file mode 100644 index 0000000000000..99143857fcd1f --- /dev/null +++ b/lib/classes/event/webservice_function_called.php @@ -0,0 +1,101 @@ +. + +/** + * core webservice function_called event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core webservice function_called event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class webservice_function_called extends \core\event\base { + + /** + * Legacy log data. + */ + protected $legacylogdata; + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "The web service function '{$this->other['function']}' has been called."; + } + + /** + * Return the legacy event log data. + * + * @return array|null + */ + protected function get_legacy_logdata() { + return $this->legacylogdata; + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('event_webservice_function_called', 'webservice'); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->data['crud'] = 'r'; + $this->data['level'] = self::LEVEL_OTHER; + $this->context = \context_system::instance(); + } + + /** + * Return the legacy event log data. + * + * @return void + */ + public function set_legacy_logdata($legacydata) { + $this->legacylogdata = $legacydata; + } + + /** + * Custom validation. + * + * @throws \coding_exception + * @return void + */ + protected function validate_data() { + if (!isset($this->other['function'])) { + throw new \coding_exception('The key \'function\' needs to be set in $other.'); + } + } + +} diff --git a/lib/classes/event/webservice_login_failed.php b/lib/classes/event/webservice_login_failed.php new file mode 100644 index 0000000000000..02c49a3443162 --- /dev/null +++ b/lib/classes/event/webservice_login_failed.php @@ -0,0 +1,107 @@ +. + +/** + * core web service login failed event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core web service login_failed event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class webservice_login_failed extends \core\event\base { + + /** + * Legacy log data. + * + * @var null|array + */ + protected $legacylogdata; + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "Web service authentication failed with code: {$this->other['reason']}."; + } + + /** + * Return the legacy event log data. + * + * @return array|null + */ + protected function get_legacy_logdata() { + return $this->legacylogdata; + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('event_webservice_login_failed', 'webservice'); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->data['crud'] = 'r'; + $this->data['level'] = self::LEVEL_OTHER; + $this->context = \context_system::instance(); + } + + /** + * Set the legacy event log data. + * + * @param array $logdata The log data. + * @return void + */ + public function set_legacy_logdata($logdata) { + $this->legacylogdata = $logdata; + } + + /** + * Custom validation. + * + * @throws \coding_exception + * @return void + */ + protected function validate_data() { + if (!isset($this->other['reason'])) { + throw new \coding_exception('The key \'reason\' needs to be set in $other.'); + } else if (!isset($this->other['method'])) { + throw new \coding_exception('The key \'method\' needs to be set in $other.'); + } else if (!isset($this->other['token']) && !isset($this->other['tokenid']) && !isset($this->other['username'])) { + throw new \coding_exception('The keys \'username\', \'token\' or \'tokenid\' need to be set in $other.'); + } + } +} diff --git a/lib/classes/event/webservice_service_created.php b/lib/classes/event/webservice_service_created.php new file mode 100644 index 0000000000000..436a23614da4f --- /dev/null +++ b/lib/classes/event/webservice_service_created.php @@ -0,0 +1,88 @@ +. + +/** + * core webservice service created event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core webservice service created event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class webservice_service_created extends \core\event\base { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "The web service service $this->objectid has been created by user $this->userid."; + } + + /** + * Return the legacy event log data. + * + * @return array|null + */ + protected function get_legacy_logdata() { + global $CFG; + $service = $this->get_record_snapshot('external_services', $this->objectid); + return array(SITEID, 'webservice', 'add', $CFG->wwwroot . "/" . $CFG->admin . "/settings.php?section=externalservices", + get_string('addservice', 'webservice', $service)); + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('event_webservice_service_created', 'webservice'); + } + + /** + * Get URL related to the action. + * + * @return \moodle_url + */ + public function get_url() { + return new \moodle_url('/admin/settings.php', array('section' => 'externalservices')); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->context = \context_system::instance(); + $this->data['crud'] = 'c'; + $this->data['level'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'external_services'; + } + +} diff --git a/lib/classes/event/webservice_service_deleted.php b/lib/classes/event/webservice_service_deleted.php new file mode 100644 index 0000000000000..4c497d94e41cf --- /dev/null +++ b/lib/classes/event/webservice_service_deleted.php @@ -0,0 +1,88 @@ +. + +/** + * core webservice service deleted event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core webservice service deleted event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class webservice_service_deleted extends \core\event\base { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "The web service service $this->objectid has been deleted by user $this->userid."; + } + + /** + * Return the legacy event log data. + * + * @return array|null + */ + protected function get_legacy_logdata() { + global $CFG; + $service = $this->get_record_snapshot('external_services', $this->objectid); + return array(SITEID, 'webservice', 'delete', $CFG->wwwroot . "/" . $CFG->admin . "/settings.php?section=externalservices", + get_string('deleteservice', 'webservice', $service)); + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('event_webservice_service_deleted', 'webservice'); + } + + /** + * Get URL related to the action. + * + * @return \moodle_url + */ + public function get_url() { + return new \moodle_url('/admin/settings.php', array('section' => 'externalservices')); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->context = \context_system::instance(); + $this->data['crud'] = 'd'; + $this->data['level'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'external_services'; + } + +} diff --git a/lib/classes/event/webservice_service_updated.php b/lib/classes/event/webservice_service_updated.php new file mode 100644 index 0000000000000..0b82da75ad2f6 --- /dev/null +++ b/lib/classes/event/webservice_service_updated.php @@ -0,0 +1,97 @@ +. + +/** + * core webservice service updated event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core webservice service updated event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class webservice_service_updated extends \core\event\base { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "The web service service $this->objectid has been updated by user $this->userid."; + } + + /** + * Return the legacy event log data. + * + * @return array|null + */ + protected function get_legacy_logdata() { + global $CFG; + $service = $this->get_record_snapshot('external_services', $this->objectid); + return array(SITEID, 'webservice', 'edit', $CFG->wwwroot . "/" . $CFG->admin . "/settings.php?section=externalservices", + get_string('editservice', 'webservice', $service)); + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('event_webservice_service_updated', 'webservice'); + } + + /** + * Get URL related to the action. + * + * @return \moodle_url + */ + public function get_url() { + return new \moodle_url('/admin/settings.php', array('section' => 'externalservices')); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->context = \context_system::instance(); + $this->data['crud'] = 'u'; + $this->data['level'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'external_services'; + } + + /** + * Set the legacy event log data. + * + * @return void + */ + public function set_legacy_logdata($legacylogdata) { + $this->legacylogdata = $legacylogdata; + } + +} diff --git a/lib/classes/event/webservice_service_user_added.php b/lib/classes/event/webservice_service_user_added.php new file mode 100644 index 0000000000000..38309fb094a3f --- /dev/null +++ b/lib/classes/event/webservice_service_user_added.php @@ -0,0 +1,98 @@ +. + +/** + * core webservice service user added event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core webservice service user added event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class webservice_service_user_added extends \core\event\base { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "The user $this->relateduserid has been added to the web service service $this->objectid."; + } + + /** + * Return the legacy event log data. + * + * @return array|null + */ + protected function get_legacy_logdata() { + global $CFG; + return array(SITEID, 'core', 'assign', $CFG->admin . '/webservice/service_users.php?id=' . $this->objectid, 'add', '', + $this->relateduserid); + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('event_webservice_service_user_added', 'webservice'); + } + + /** + * Get URL related to the action. + * + * @return \moodle_url + */ + public function get_url() { + return new \moodle_url('/admin/webservice/service_users.php', array('id' => $this->objectid)); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->context = \context_system::instance(); + $this->data['crud'] = 'c'; + $this->data['level'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'external_services'; + } + + /** + * Custom validation. + * + * @return void + */ + protected function validate_data() { + if (!isset($this->relateduserid)) { + throw new \coding_exception('The relateduserid must be set.'); + } + } + +} diff --git a/lib/classes/event/webservice_service_user_removed.php b/lib/classes/event/webservice_service_user_removed.php new file mode 100644 index 0000000000000..a0f7eecabd153 --- /dev/null +++ b/lib/classes/event/webservice_service_user_removed.php @@ -0,0 +1,98 @@ +. + +/** + * core webservice service user removed event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core webservice service user removed event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class webservice_service_user_removed extends \core\event\base { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "The user $this->relateduserid has been removed to the web service service $this->objectid."; + } + + /** + * Return the legacy event log data. + * + * @return array|null + */ + protected function get_legacy_logdata() { + global $CFG; + return array(SITEID, 'core', 'assign', $CFG->admin . '/webservice/service_users.php?id=' . $this->objectid, 'remove', '', + $this->relateduserid); + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('event_webservice_service_user_removed', 'webservice'); + } + + /** + * Get URL related to the action. + * + * @return \moodle_url + */ + public function get_url() { + return new \moodle_url('/admin/webservice/service_users.php', array('id' => $this->objectid)); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->context = \context_system::instance(); + $this->data['crud'] = 'd'; + $this->data['level'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'external_services'; + } + + /** + * Custom validation. + * + * @return void + */ + protected function validate_data() { + if (!isset($this->relateduserid)) { + throw new \coding_exception('The relateduserid must be set.'); + } + } + +} diff --git a/lib/classes/event/webservice_token_created.php b/lib/classes/event/webservice_token_created.php new file mode 100644 index 0000000000000..5f18336b64d70 --- /dev/null +++ b/lib/classes/event/webservice_token_created.php @@ -0,0 +1,100 @@ +. + +/** + * core webservice token_created event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core webservice token_created event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class webservice_token_created extends \core\event\base { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "A web service token has been created for the user $this->relateduserid."; + } + + /** + * Return the legacy event log data. + * + * @return array|null + */ + protected function get_legacy_logdata() { + if (!empty($this->other['auto'])) { + // The token has been automatically created. + return array(SITEID, 'webservice', 'automatically create user token', '' , 'User ID: ' . $this->relateduserid); + } + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('event_webservice_token_created', 'webservice'); + } + + /** + * Get URL related to the action. + * + * @return \moodle_url + */ + public function get_url() { + return new \moodle_url('/admin/settings.php', array('section' => 'webservicetokens')); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->context = \context_system::instance(); + $this->data['crud'] = 'c'; + $this->data['level'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'external_tokens'; + } + + /** + * Custom validation. + * + * @throws \coding_exception + * @return void + */ + protected function validate_data() { + if (!isset($this->relateduserid)) { + throw new \coding_exception('The property \'relateduserid\' must be set.'); + } + } + +} diff --git a/lib/classes/event/webservice_token_sent.php b/lib/classes/event/webservice_token_sent.php new file mode 100644 index 0000000000000..51775bd380424 --- /dev/null +++ b/lib/classes/event/webservice_token_sent.php @@ -0,0 +1,76 @@ +. + +/** + * core webservice token_sent event. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\event; +defined('MOODLE_INTERNAL') || die(); + +/** + * core webservice token sent event class. + * + * @package core + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class webservice_token_sent extends \core\event\base { + + /** + * Returns description of what happened. + * + * @return string + */ + public function get_description() { + return "The token $this->objectid has been sent to the user $this->userid."; + } + + /** + * Return the legacy event log data. + * + * @return array|null + */ + protected function get_legacy_logdata() { + return array(SITEID, 'webservice', 'sending requested user token', '' , 'User ID: ' . $this->userid); + } + + /** + * Return localised event name. + * + * @return string + */ + public static function get_name() { + return get_string('event_webservice_token_sent', 'webservice'); + } + + /** + * Init method. + * + * @return void + */ + protected function init() { + $this->context = \context_system::instance(); + $this->data['crud'] = 'r'; + $this->data['level'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'external_tokens'; + } + +} diff --git a/login/token.php b/login/token.php index 412b24725e80e..cd2b20288074e 100644 --- a/login/token.php +++ b/login/token.php @@ -151,7 +151,8 @@ or (!is_siteadmin($user) && has_capability('moodle/webservice:createtoken', context_system::instance()))) { // if service doesn't exist, dml will throw exception $service_record = $DB->get_record('external_services', array('shortname'=>$serviceshortname, 'enabled'=>1), '*', MUST_EXIST); - // create a new token + + // Create a new token. $token = new stdClass; $token->token = md5(uniqid(rand(), 1)); $token->userid = $user->id; @@ -160,9 +161,18 @@ $token->creatorid = $user->id; $token->timecreated = time(); $token->externalserviceid = $service_record->id; - $tokenid = $DB->insert_record('external_tokens', $token); - add_to_log(SITEID, 'webservice', 'automatically create user token', '' , 'User ID: ' . $user->id); - $token->id = $tokenid; + $token->id = $DB->insert_record('external_tokens', $token); + + $params = array( + 'objectid' => $token->id, + 'relateduserid' => $user->id, + 'other' => array( + 'auto' => true + ) + ); + $event = \core\event\webservice_token_created::create($params); + $event->add_record_snapshot('external_tokens', $token); + $event->trigger(); } else { throw new moodle_exception('cannotcreatetoken', 'webservice', '', $serviceshortname); } @@ -171,7 +181,12 @@ // log token access $DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id)); - add_to_log(SITEID, 'webservice', 'sending requested user token', '' , 'User ID: ' . $user->id); + $params = array( + 'objectid' => $token->id, + ); + $event = \core\event\webservice_token_sent::create($params); + $event->add_record_snapshot('external_tokens', $token); + $event->trigger(); $usertoken = new stdClass; $usertoken->token = $token->token; diff --git a/webservice/lib.php b/webservice/lib.php index 8e1898f93270d..e20928e44f2e4 100644 --- a/webservice/lib.php +++ b/webservice/lib.php @@ -69,16 +69,36 @@ public function authenticate_user($token) { throw new moodle_exception('invalidtoken', 'webservice'); } + $loginfaileddefaultparams = array( + 'other' => array( + 'method' => WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN, + 'reason' => null, + 'tokenid' => $token->id + ) + ); + // Validate token date if ($token->validuntil and $token->validuntil < time()) { - add_to_log(SITEID, 'webservice', get_string('tokenauthlog', 'webservice'), '', get_string('invalidtimedtoken', 'webservice'), 0); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'token_expired'; + $event = \core\event\webservice_login_failed::create($params); + $event->add_record_snapshot('external_tokens', $token); + $event->set_legacy_logdata(array(SITEID, 'webservice', get_string('tokenauthlog', 'webservice'), '', + get_string('invalidtimedtoken', 'webservice'), 0)); + $event->trigger(); $DB->delete_records('external_tokens', array('token' => $token->token)); throw new webservice_access_exception('Invalid token - token expired - check validuntil time for the token'); } // Check ip if ($token->iprestriction and !address_in_subnet(getremoteaddr(), $token->iprestriction)) { - add_to_log(SITEID, 'webservice', get_string('tokenauthlog', 'webservice'), '', get_string('failedtolog', 'webservice') . ": " . getremoteaddr(), 0); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'ip_restricted'; + $event = \core\event\webservice_login_failed::create($params); + $event->add_record_snapshot('external_tokens', $token); + $event->set_legacy_logdata(array(SITEID, 'webservice', get_string('tokenauthlog', 'webservice'), '', + get_string('failedtolog', 'webservice') . ": " . getremoteaddr(), 0)); + $event->trigger(); throw new webservice_access_exception('Invalid token - IP:' . getremoteaddr() . ' is not supported'); } @@ -142,19 +162,34 @@ public function authenticate_user($token) { //only confirmed user should be able to call web service if (empty($user->confirmed)) { - add_to_log(SITEID, 'webservice', 'user unconfirmed', '', $user->username); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'user_unconfirmed'; + $event = \core\event\webservice_login_failed::create($params); + $event->add_record_snapshot('external_tokens', $token); + $event->set_legacy_logdata(array(SITEID, 'webservice', 'user unconfirmed', '', $user->username)); + $event->trigger(); throw new moodle_exception('usernotconfirmed', 'moodle', '', $user->username); } //check the user is suspended if (!empty($user->suspended)) { - add_to_log(SITEID, 'webservice', 'user suspended', '', $user->username); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'user_suspended'; + $event = \core\event\webservice_login_failed::create($params); + $event->add_record_snapshot('external_tokens', $token); + $event->set_legacy_logdata(array(SITEID, 'webservice', 'user suspended', '', $user->username)); + $event->trigger(); throw new webservice_access_exception('Refused web service access for suspended username: ' . $user->username); } //check if the auth method is nologin (in this case refuse connection) if ($user->auth == 'nologin') { - add_to_log(SITEID, 'webservice', 'nologin auth attempt with web service', '', $user->username); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'nologin'; + $event = \core\event\webservice_login_failed::create($params); + $event->add_record_snapshot('external_tokens', $token); + $event->set_legacy_logdata(array(SITEID, 'webservice', 'nologin auth attempt with web service', '', $user->username)); + $event->trigger(); throw new webservice_access_exception('Refused web service access for nologin authentication username: ' . $user->username); } @@ -163,7 +198,12 @@ public function authenticate_user($token) { if (!empty($auth->config->expiration) and $auth->config->expiration == 1) { $days2expire = $auth->password_expire($user->username); if (intval($days2expire) < 0) { - add_to_log(SITEID, 'webservice', 'expired password', '', $user->username); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'password_expired'; + $event = \core\event\webservice_login_failed::create($params); + $event->add_record_snapshot('external_tokens', $token); + $event->set_legacy_logdata(array(SITEID, 'webservice', 'expired password', '', $user->username)); + $event->trigger(); throw new moodle_exception('passwordisexpired', 'webservice'); } } @@ -823,6 +863,15 @@ protected function authenticate_user() { throw new coding_exception('Cookies must be disabled in WS servers!'); } + $loginfaileddefaultparams = array( + 'context' => context_system::instance(), + 'other' => array( + 'method' => $this->authmethod, + 'reason' => null, + 'token' => $this->token + ) + ); + if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME) { //we check that authentication plugin is enabled @@ -846,8 +895,16 @@ protected function authenticate_user() { } if (!$auth->user_login_webservice($this->username, $this->password)) { - // log failed login attempts - add_to_log(SITEID, 'webservice', get_string('simpleauthlog', 'webservice'), '' , get_string('failedtolog', 'webservice').": ".$this->username."/".$this->password." - ".getremoteaddr() , 0); + + // Log failed login attempts. + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'password'; + $params['other']['username'] = $this->username; + $event = \core\event\webservice_login_failed::create($params); + $event->set_legacy_logdata(array(SITEID, 'webservice', get_string('simpleauthlog', 'webservice'), '' , + get_string('failedtolog', 'webservice').": ".$this->username."/".$this->password." - ".getremoteaddr() , 0)); + $event->trigger(); + throw new moodle_exception('wrongusernamepassword', 'webservice'); } @@ -867,19 +924,37 @@ protected function authenticate_user() { //only confirmed user should be able to call web service if (!empty($user->deleted)) { - add_to_log(SITEID, '', '', '', get_string('wsaccessuserdeleted', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'user_deleted'; + $params['other']['username'] = $user->username; + $event = \core\event\webservice_login_failed::create($params); + $event->set_legacy_logdata(array(SITEID, '', '', '', get_string('wsaccessuserdeleted', 'webservice', + $user->username) . " - ".getremoteaddr(), 0, $user->id)); + $event->trigger(); throw new webservice_access_exception('Refused web service access for deleted username: ' . $user->username); } //only confirmed user should be able to call web service if (empty($user->confirmed)) { - add_to_log(SITEID, '', '', '', get_string('wsaccessuserunconfirmed', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'user_unconfirmed'; + $params['other']['username'] = $user->username; + $event = \core\event\webservice_login_failed::create($params); + $event->set_legacy_logdata(array(SITEID, '', '', '', get_string('wsaccessuserunconfirmed', 'webservice', + $user->username) . " - ".getremoteaddr(), 0, $user->id)); + $event->trigger(); throw new moodle_exception('wsaccessuserunconfirmed', 'webservice', '', $user->username); } //check the user is suspended if (!empty($user->suspended)) { - add_to_log(SITEID, '', '', '', get_string('wsaccessusersuspended', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'user_unconfirmed'; + $params['other']['username'] = $user->username; + $event = \core\event\webservice_login_failed::create($params); + $event->set_legacy_logdata(array(SITEID, '', '', '', get_string('wsaccessusersuspended', 'webservice', + $user->username) . " - ".getremoteaddr(), 0, $user->id)); + $event->trigger(); throw new webservice_access_exception('Refused web service access for suspended username: ' . $user->username); } @@ -892,14 +967,26 @@ protected function authenticate_user() { if (!empty($auth->config->expiration) and $auth->config->expiration == 1) { $days2expire = $auth->password_expire($user->username); if (intval($days2expire) < 0 ) { - add_to_log(SITEID, '', '', '', get_string('wsaccessuserexpired', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'password_expired'; + $params['other']['username'] = $user->username; + $event = \core\event\webservice_login_failed::create($params); + $event->set_legacy_logdata(array(SITEID, '', '', '', get_string('wsaccessuserexpired', 'webservice', + $user->username) . " - ".getremoteaddr(), 0, $user->id)); + $event->trigger(); throw new webservice_access_exception('Refused web service access for password expired username: ' . $user->username); } } //check if the auth method is nologin (in this case refuse connection) if ($user->auth=='nologin') { - add_to_log(SITEID, '', '', '', get_string('wsaccessusernologin', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'login'; + $params['other']['username'] = $user->username; + $event = \core\event\webservice_login_failed::create($params); + $event->set_legacy_logdata(array(SITEID, '', '', '', get_string('wsaccessusernologin', 'webservice', + $user->username) . " - ".getremoteaddr(), 0, $user->id)); + $event->trigger(); throw new webservice_access_exception('Refused web service access for nologin authentication username: ' . $user->username); } @@ -924,9 +1011,24 @@ protected function authenticate_user() { */ protected function authenticate_by_token($tokentype){ global $DB; + + $loginfaileddefaultparams = array( + 'context' => context_system::instance(), + 'other' => array( + 'method' => $this->authmethod, + 'reason' => null, + 'token' => $this->token + ) + ); + if (!$token = $DB->get_record('external_tokens', array('token'=>$this->token, 'tokentype'=>$tokentype))) { - // log failed login attempts - add_to_log(SITEID, 'webservice', get_string('tokenauthlog', 'webservice'), '' , get_string('failedtolog', 'webservice').": ".$this->token. " - ".getremoteaddr() , 0); + // Log failed login attempts. + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'invalid_token'; + $event = \core\event\webservice_login_failed::create($params); + $event->set_legacy_logdata(array(SITEID, 'webservice', get_string('tokenauthlog', 'webservice'), '' , + get_string('failedtolog', 'webservice').": ".$this->token. " - ".getremoteaddr() , 0)); + $event->trigger(); throw new moodle_exception('invalidtoken', 'webservice'); } @@ -944,7 +1046,14 @@ protected function authenticate_by_token($tokentype){ } if ($token->iprestriction and !address_in_subnet(getremoteaddr(), $token->iprestriction)) { - add_to_log(SITEID, 'webservice', get_string('tokenauthlog', 'webservice'), '' , get_string('failedtolog', 'webservice').": ".getremoteaddr() , 0); + $params = $loginfaileddefaultparams; + $params['other']['reason'] = 'ip_restricted'; + $params['other']['tokenid'] = $token->id; + $event = \core\event\webservice_login_failed::create($params); + $event->add_record_snapshot('external_tokens', $token); + $event->set_legacy_logdata(array(SITEID, 'webservice', get_string('tokenauthlog', 'webservice'), '' , + get_string('failedtolog', 'webservice').": ".getremoteaddr() , 0)); + $event->trigger(); throw new webservice_access_exception('Invalid service - IP:' . getremoteaddr() . ' is not supported - check this allowed user'); } @@ -1057,8 +1166,15 @@ public function run() { // tell server what functions are available $this->zend_server->setClass($this->service_class); - //log the web service request - add_to_log(SITEID, 'webservice', '', '' , $this->zend_class." ".getremoteaddr() , 0, $this->userid); + // Log the web service request. + $params = array( + 'other' => array( + 'function' => 'unknown' + ) + ); + $event = \core\event\webservice_function_called::create($params); + $event->set_legacy_logdata(array(SITEID, 'webservice', '', '', $this->zend_class.' '.getremoteaddr(), 0, $this->userid)); + $event->trigger(); //send headers $this->send_headers(); @@ -1518,8 +1634,15 @@ public function run() { // find all needed function info and make sure user may actually execute the function $this->load_function_info(); - //log the web service request - add_to_log(SITEID, 'webservice', $this->functionname, '' , getremoteaddr() , 0, $this->userid); + // Log the web service request. + $params = array( + 'other' => array( + 'function' => $this->functionname + ) + ); + $event = \core\event\webservice_function_called::create($params); + $event->set_legacy_logdata(array(SITEID, 'webservice', $this->functionname, '' , getremoteaddr() , 0, $this->userid)); + $event->trigger(); // finally, execute the function - any errors are catched by the default exception handler $this->execute(); diff --git a/webservice/tests/events.php b/webservice/tests/events.php new file mode 100644 index 0000000000000..b1961007abdfb --- /dev/null +++ b/webservice/tests/events.php @@ -0,0 +1,342 @@ +. + +/** + * Unit tests for Web service events. + * + * @package webservice + * @category phpunit + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Unit tests for Web service events. + * + * @package webservice + * @category phpunit + * @copyright 2013 Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class webservice_events_testcase extends advanced_testcase { + + public function setUp() { + $this->resetAfterTest(); + } + + public function test_function_called() { + // The Web service API doesn't allow the testing of the events directly by + // calling some functions which trigger the events, so what we are going here + // is just checking that the event returns the expected information. + + $sink = $this->redirectEvents(); + + $fakelogdata = array(1, 'B', true, null); + $params = array( + 'other' => array( + 'function' => 'A function' + ) + ); + $event = \core\event\webservice_function_called::create($params); + $event->set_legacy_logdata($fakelogdata); + $event->trigger(); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + $this->assertEquals(context_system::instance(), $event->get_context()); + $this->assertEquals('A function', $event->other['function']); + $this->assertEventLegacyLogData($fakelogdata, $event); + } + + public function test_login_failed() { + // The Web service API doesn't allow the testing of the events directly by + // calling some functions which trigger the events, so what we are going here + // is just checking that the event returns the expected information. + + $sink = $this->redirectEvents(); + + $fakelogdata = array(1, 'B', true, null); + $params = array( + 'other' => array( + 'reason' => 'Unit Test', + 'method' => 'Some method', + 'token' => 'A fake token' + ) + ); + $event = \core\event\webservice_login_failed::create($params); + $event->set_legacy_logdata($fakelogdata); + $event->trigger(); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + $this->assertEquals(context_system::instance(), $event->get_context()); + $this->assertEquals($params['other']['reason'], $event->other['reason']); + $this->assertEquals($params['other']['method'], $event->other['method']); + $this->assertEquals($params['other']['token'], $event->other['token']); + $this->assertEventLegacyLogData($fakelogdata, $event); + } + + public function test_service_created() { + global $CFG, $DB; + + // The Web service API doesn't allow the testing of the events directly by + // calling some functions which trigger the events, so what we are going here + // is just checking that the event returns the expected information. + + $sink = $this->redirectEvents(); + + // Creating a fake service. + $service = (object) array( + 'name' => 'Test', + 'enabled' => 1, + 'requiredcapability' => '', + 'restrictedusers' => 0, + 'component' => null, + 'timecreated' => time(), + 'timemodified' => time(), + 'shortname' => null, + 'downloadfiles' => 0, + 'uploadfiles' => 0 + ); + $service->id = $DB->insert_record('external_services', $service); + + // Trigger the event. + $params = array( + 'objectid' => $service->id, + ); + $event = \core\event\webservice_service_created::create($params); + $event->add_record_snapshot('external_services', $service); + $event->trigger(); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Assert that the event contains the right information. + $this->assertEquals(context_system::instance(), $event->get_context()); + $this->assertEquals($service->id, $event->objectid); + $returnurl = $CFG->wwwroot . "/" . $CFG->admin . "/settings.php?section=externalservices"; + $expected = array(SITEID, 'webservice', 'add', $returnurl, get_string('addservice', 'webservice', $service)); + $this->assertEventLegacyLogData($expected, $event); + } + + public function test_service_updated() { + global $CFG, $DB; + + // The Web service API doesn't allow the testing of the events directly by + // calling some functions which trigger the events, so what we are going here + // is just checking that the event returns the expected information. + + $sink = $this->redirectEvents(); + + // Creating a fake service. + $service = (object) array( + 'name' => 'Test', + 'enabled' => 1, + 'requiredcapability' => '', + 'restrictedusers' => 0, + 'component' => null, + 'timecreated' => time(), + 'timemodified' => time(), + 'shortname' => null, + 'downloadfiles' => 0, + 'uploadfiles' => 0 + ); + $service->id = $DB->insert_record('external_services', $service); + + // Trigger the event. + $params = array( + 'objectid' => $service->id, + ); + $event = \core\event\webservice_service_updated::create($params); + $event->add_record_snapshot('external_services', $service); + $event->trigger(); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Assert that the event contains the right information. + $this->assertEquals(context_system::instance(), $event->get_context()); + $this->assertEquals($service->id, $event->objectid); + $returnurl = $CFG->wwwroot . "/" . $CFG->admin . "/settings.php?section=externalservices"; + $expected = array(SITEID, 'webservice', 'edit', $returnurl, get_string('editservice', 'webservice', $service)); + $this->assertEventLegacyLogData($expected, $event); + } + + public function test_service_deleted() { + global $CFG, $DB; + + // The Web service API doesn't allow the testing of the events directly by + // calling some functions which trigger the events, so what we are going here + // is just checking that the event returns the expected information. + + $sink = $this->redirectEvents(); + + // Creating a fake service. + $service = (object) array( + 'name' => 'Test', + 'enabled' => 1, + 'requiredcapability' => '', + 'restrictedusers' => 0, + 'component' => null, + 'timecreated' => time(), + 'timemodified' => time(), + 'shortname' => null, + 'downloadfiles' => 0, + 'uploadfiles' => 0 + ); + $service->id = $DB->insert_record('external_services', $service); + + // Trigger the event. + $params = array( + 'objectid' => $service->id, + ); + $event = \core\event\webservice_service_deleted::create($params); + $event->add_record_snapshot('external_services', $service); + $event->trigger(); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Assert that the event contains the right information. + $this->assertEquals(context_system::instance(), $event->get_context()); + $this->assertEquals($service->id, $event->objectid); + $returnurl = $CFG->wwwroot . "/" . $CFG->admin . "/settings.php?section=externalservices"; + $expected = array(SITEID, 'webservice', 'delete', $returnurl, get_string('deleteservice', 'webservice', $service)); + $this->assertEventLegacyLogData($expected, $event); + } + + public function test_service_user_added() { + global $CFG; + + // The Web service API doesn't allow the testing of the events directly by + // calling some functions which trigger the events, so what we are going here + // is just checking that the event returns the expected information. + + $sink = $this->redirectEvents(); + + $params = array( + 'objectid' => 1, + 'relateduserid' => 2 + ); + $event = \core\event\webservice_service_user_added::create($params); + $event->trigger(); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + $this->assertEquals(context_system::instance(), $event->get_context()); + $this->assertEquals(1, $event->objectid); + $this->assertEquals(2, $event->relateduserid); + $expected = array(SITEID, 'core', 'assign', $CFG->admin . '/webservice/service_users.php?id=' . $params['objectid'], + 'add', '', $params['relateduserid']); + $this->assertEventLegacyLogData($expected, $event); + } + + public function test_service_user_removed() { + global $CFG; + + // The Web service API doesn't allow the testing of the events directly by + // calling some functions which trigger the events, so what we are going here + // is just checking that the event returns the expected information. + + $sink = $this->redirectEvents(); + + $params = array( + 'objectid' => 1, + 'relateduserid' => 2 + ); + $event = \core\event\webservice_service_user_removed::create($params); + $event->trigger(); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + $this->assertEquals(context_system::instance(), $event->get_context()); + $this->assertEquals(1, $event->objectid); + $this->assertEquals(2, $event->relateduserid); + $expected = array(SITEID, 'core', 'assign', $CFG->admin . '/webservice/service_users.php?id=' . $params['objectid'], + 'remove', '', $params['relateduserid']); + $this->assertEventLegacyLogData($expected, $event); + } + + public function test_token_created() { + // The Web service API doesn't allow the testing of the events directly by + // calling some functions which trigger the events, so what we are going here + // is just checking that the event returns the expected information. + + $sink = $this->redirectEvents(); + + $params = array( + 'objectid' => 1, + 'relateduserid' => 2, + 'other' => array( + 'auto' => true + ) + ); + $event = \core\event\webservice_token_created::create($params); + $event->trigger(); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + $this->assertEquals(context_system::instance(), $event->get_context()); + $this->assertEquals(1, $event->objectid); + $this->assertEquals(2, $event->relateduserid); + $expected = array(SITEID, 'webservice', 'automatically create user token', '' , 'User ID: ' . 2); + $this->assertEventLegacyLogData($expected, $event); + } + + public function test_token_sent() { + $user = $this->getDataGenerator()->create_user(); + $this->setUser($user); + + // The Web service API doesn't allow the testing of the events directly by + // calling some functions which trigger the events, so what we are going here + // is just checking that the event returns the expected information. + + $sink = $this->redirectEvents(); + + $params = array( + 'objectid' => 1, + 'other' => array( + 'auto' => true + ) + ); + $event = \core\event\webservice_token_sent::create($params); + $event->trigger(); + + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + $this->assertEquals(context_system::instance(), $event->get_context()); + $this->assertEquals(1, $event->objectid); + $expected = array(SITEID, 'webservice', 'sending requested user token', '' , 'User ID: ' . $user->id); + $this->assertEventLegacyLogData($expected, $event); + } +}