$fs = get_file_storage();
$fs->cron();
+ //cleanup old session linked tokens
+ //deletes the session linked tokens that are over a day old.
+ mtrace("Deleting session linked tokens more than one day old...", '');
+ $DB->delete_records_select('external_tokens', 'lastaccess < {onedayago} AND tokentype = {tokentype}',
+ array('onedayago' => time() - DAYSECS, 'tokentype' => EXTERNAL_TOKEN_EMBEDDED));
+
// run any customized cronjobs, if any
if ($locals = get_plugin_list('local')) {
mtrace('Processing customized cron scripts ...', '');
$string['targetdatabasenotempty'] = 'The target database is not empty. Transfer aborted for safety reasons.';
$string['themenotinstall'] = 'This theme is not installed!';
$string['TODO'] = 'TODO';
-$string['tokenalreadyexist'] = 'The generated token already exists, try again.';
+$string['tokengenerationfailed'] = 'Cannot generate a new token.';
$string['transactionvoid'] = 'Transaction cannot be voided because it has already been voided';
$string['unenrolerror'] = 'An error occurred while trying to unenrol that person';
$string['unicodeupgradeerror'] = 'Sorry, but your database is not already in Unicode, and this version of Moodle is not able to migrate your database to Unicode. Please upgrade to Moodle 1.7.x first and perform the Unicode migration from the Admin page. After that is done you should be able to migrate to Moodle {$a}';
$string['invalidiptoken'] = 'Invalid token - your IP is not supported';
$string['invalidtimedtoken'] = 'Invalid token - token expired';
$string['invalidtoken'] = 'Invalid token - token not found';
+$string['invalidtokensession'] = 'Invalid session based token - session not found or expired';
$string['iprestriction'] = 'IP restriction';
$string['key'] = 'Key';
$string['keyshelp'] = 'The keys are used to access your Moodle account from external applications.';
}
$newtoken->tokentype = $tokentype;
$newtoken->userid = $userid;
+ if ($tokentype == EXTERNAL_TOKEN_EMBEDDED){
+ $newtoken->sid = session_id();
+ }
$newtoken->contextid = $context->id;
$newtoken->creatorid = $USER->id;
}
$DB->insert_record('external_tokens', $newtoken);
return $newtoken->token;
+}
+/**
+ * Create and return a session linked token. Token to be used for html embedded client apps that want to communicate
+ * with the Moodle server through web services. The token is linked to the current session for the current page request.
+ * It is expected this will be called in the script generating the html page that is embedding the client app and that the
+ * returned token will be somehow passed into the client app being embedded in the page.
+ * @param string $servicename name of the web service. Service name as defined in db/services.php
+ * @param int $context context within which the web service can operate.
+ * @return int returns token id.
+ */
+function external_create_service_token($servicename, $context){
+ global $USER, $DB;
+ $service = $DB->get_record('external_services', array('name'=>$servicename), '*', MUST_EXIST);
+ return external_generate_token(EXTERNAL_TOKEN_EMBEDDED, $service, $USER->id, $context, 0);
}
\ No newline at end of file
* @return void
*/
public function write_close();
+
+ /**
+ * Check for existing session with id $sid
+ * @param unknown_type $sid
+ * @return boolean true if session found.
+ */
+ public function session_exists($sid);
}
/**
* Terminates active moodle session
*/
public function terminate_current() {
- global $CFG, $SESSION, $USER;
+ global $CFG, $SESSION, $USER, $DB;
+ $DB->delete_records('external_tokens', array('sid'=>session_id(), 'tokentype'=>EXTERNAL_TOKEN_EMBEDDED));
+
if (NO_MOODLE_COOKIES) {
return;
}
}
ini_set('session.save_path', $CFG->dataroot .'/sessions');
}
+ /**
+ * Check for existing session with id $sid
+ * @param unknown_type $sid
+ * @return boolean true if session found.
+ */
+ public function session_exists($sid){
+ $sid = clean_param($sid, PARAM_FILE);
+ $sessionfile = clean_param("$CFG->dataroot/sessions/sess_$sid", PARAM_FILE);
+ return file_exists($sessionfile);
+ }
+
+
}
/**
}
}
}
-
+
+ public function session_exists($sid){
+ global $CFG;
+ try {
+ $sql = "SELECT * FROM {sessions} WHERE timemodified < ? AND sid=? AND state=?";
+ $params = array(time() + $CFG->sessiontimeout, $sid, 0);
+
+ return $this->database->record_exists_sql($sql, $params);
+ } catch (dml_exception $ex) {
+ error_log('Error checking existance of database session');
+ return false;
+ }
+ }
+
protected function init_session_storage() {
global $CFG;
class webservice_amf_server extends webservice_zend_server {
/**
* Contructor
- * @param bool $simple use simple authentication
+ * @param integer $authmethod authentication method - one of WEBSERVICE_AUTHMETHOD_*
*/
- public function __construct($simple) {
+ public function __construct($authmethod) {
require_once 'Zend/Amf/Server.php';
- parent::__construct($simple, 'Zend_Amf_Server');
+ parent::__construct($authmethod, 'Zend_Amf_Server');
$this->wsname = 'amf';
}
protected function init_service_class(){
- parent::init_service_class();
+ parent::init_service_class();
//allow access to data about methods available.
$this->zend_server->setClass( "MethodDescriptor" );
MethodDescriptor::$classnametointrospect = $this->service_class;
}
protected function service_class_method_body($function, $params){
- $params = "webservice_amf_server::cast_objects_to_array($params)";
- $externallibcall = $function->classname.'::'.$function->methodname.'('.$params.')';
- $descriptionmethod = $function->methodname.'_returns()';
- $callforreturnvaluedesc = $function->classname.'::'.$descriptionmethod;
- return
+ if ($params){
+ $params = "webservice_amf_server::cast_objects_to_array($params)";
+ }
+ $externallibcall = $function->classname.'::'.$function->methodname.'('.$params.')';
+ $descriptionmethod = $function->methodname.'_returns()';
+ $callforreturnvaluedesc = $function->classname.'::'.$descriptionmethod;
+ return
' return webservice_amf_server::validate_and_cast_values('.$callforreturnvaluedesc.', '.$externallibcall.');';
}
/**
* @return mixed params with added defaults for optional items, invalid_parameters_exception thrown if any problem found
*/
public static function validate_and_cast_values($description, $value) {
- if (is_null($description)){
- return;
- }
+ if (is_null($description)){
+ return;
+ }
if ($description instanceof external_value) {
if (is_array($value) or is_object($value)) {
throw new invalid_return_value_exception('Scalar type expected, array or object received.');
throw new invalid_return_value_exception('Invalid external api description.');
}
}
- /**
- * Recursive function to recurse down into a complex variable and convert all
- * objects to arrays. Doesn't recurse down into objects or cast objects other than stdClass
- * which is represented in Flash / Flex as an object.
- * @param mixed $params value to cast
- * @return mixed Cast value
- */
- public static function cast_objects_to_array($params){
- if ($params instanceof stdClass){
- $params = (array)$params;
- }
- if (is_array($params)){
- $toreturn = array();
- foreach ($params as $key=> $param){
- $toreturn[$key] = self::cast_objects_to_array($param);
- }
- return $toreturn;
- } else {
- return $params;
- }
- }
+ /**
+ * Recursive function to recurse down into a complex variable and convert all
+ * objects to arrays. Doesn't recurse down into objects or cast objects other than stdClass
+ * which is represented in Flash / Flex as an object.
+ * @param mixed $params value to cast
+ * @return mixed Cast value
+ */
+ public static function cast_objects_to_array($params){
+ if ($params instanceof stdClass){
+ $params = (array)$params;
+ }
+ if (is_array($params)){
+ $toreturn = array();
+ foreach ($params as $key=> $param){
+ $toreturn[$key] = self::cast_objects_to_array($param);
+ }
+ return $toreturn;
+ } else {
+ return $params;
+ }
+ }
/**
* Set up zend service class
* @return void
die;
}
-$server = new webservice_amf_server(false);
+$server = new webservice_amf_server(WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN);
$server->run();
die;
die;
}
-$server = new webservice_amf_server(true);
+$server = new webservice_amf_server(WEBSERVICE_AUTHMETHOD_USERNAME);
$server->run();
die;
require_once($CFG->libdir.'/externallib.php');
+define('WEBSERVICE_AUTHMETHOD_USERNAME', 0);
+define('WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN', 1);
+define('WEBSERVICE_AUTHMETHOD_SESSION_TOKEN', 2);
+
/**
* Exception indicating access control problem in web service call
* @author Petr Skoda (skodak)
/** @property int $userid the local user */
protected $userid = null;
- /** @property bool $simple true if simple auth used */
- protected $simple;
+ /** @property integer $authmethod authentication method one of WEBSERVICE_AUTHMETHOD_* */
+ protected $authmethod;
/** @property string $token authentication token*/
protected $token = null;
/** @property int restrict call to one service id*/
protected $restricted_serviceid = null;
+ /**
+ * Contructor
+ * @param integer $authmethod authentication method one of WEBSERVICE_AUTHMETHOD_*
+ */
+ public function __construct($authmethod) {
+ $this->authmethod = $authmethod;
+ }
+
+
/**
* Authenticate user using username+password or token.
* This function sets up $USER global.
throw new coding_exception('Cookies must be disabled in WS servers!');
}
- if ($this->simple) {
+ if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME) {
//we check that authentication plugin is enabled
//it is only required by simple authentication
$user = $DB->get_record('user', array('username'=>$this->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0), '*', MUST_EXIST);
+ } else if ($this->authmethod == WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN){
+ $user = $this->authenticate_by_token(EXTERNAL_TOKEN_PERMANENT);
} else {
- if (!$token = $DB->get_record('external_tokens', array('token'=>$this->token, 'tokentype'=>EXTERNAL_TOKEN_PERMANENT))) {
- // log failed login attempts
- add_to_log(1, 'webservice', get_string('tokenauthlog', 'webservice'), '' , get_string('failedtolog', 'webservice').": ".$this->token. " - ".getremoteaddr() , 0);
- throw new webservice_access_exception(get_string('invalidtoken', 'webservice'));
- }
-
- if ($token->validuntil and $token->validuntil < time()) {
- throw new webservice_access_exception(get_string('invalidtimedtoken', 'webservice'));
- }
-
- if ($token->iprestriction and !address_in_subnet(getremoteaddr(), $token->iprestriction)) {
- add_to_log(1, 'webservice', get_string('tokenauthlog', 'webservice'), '' , get_string('failedtolog', 'webservice').": ".getremoteaddr() , 0);
- throw new webservice_access_exception(get_string('invalidiptoken', 'webservice'));
- }
-
- $this->restricted_context = get_context_instance_by_id($token->contextid);
- $this->restricted_serviceid = $token->externalserviceid;
-
- $user = $DB->get_record('user', array('id'=>$token->userid, 'deleted'=>0), '*', MUST_EXIST);
-
- // log token access
- $DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id));
+ $user = $this->authenticate_by_token(EXTERNAL_TOKEN_EMBEDDED);
}
-
+
// now fake user login, the session is completely empty too
session_set_user($user);
$this->userid = $user->id;
- if (!has_capability("webservice/$this->wsname:use", $this->restricted_context)) {
+ if ($this->authmethod != EXTERNAL_TOKEN_EMBEDDED && !has_capability("webservice/$this->wsname:use", $this->restricted_context)) {
throw new webservice_access_exception(get_string('accessnotallowed', 'webservice'));
}
external_api::set_context_restriction($this->restricted_context);
}
+
+ protected function authenticate_by_token($tokentype){
+ global $DB;
+ if (!$token = $DB->get_record('external_tokens', array('token'=>$this->token, 'tokentype'=>$tokentype))) {
+ // log failed login attempts
+ add_to_log(1, 'webservice', get_string('tokenauthlog', 'webservice'), '' , get_string('failedtolog', 'webservice').": ".$this->token. " - ".getremoteaddr() , 0);
+ throw new webservice_access_exception(get_string('invalidtoken', 'webservice'));
+ }
+
+ if ($token->validuntil and $token->validuntil < time()) {
+ $DB->delete_records('external_tokens', array('token'=>$this->token, 'tokentype'=>$tokentype));
+ throw new webservice_access_exception(get_string('invalidtimedtoken', 'webservice'));
+ }
+
+ if ($token->sid){//assumes that if sid is set then there must be a valid associated session no matter the token type
+ $session = session_get_instance();
+ if (!$session->session_exists($token->sid)){
+ $DB->delete_records('external_tokens', array('sid'=>$token->sid));
+ throw new webservice_access_exception(get_string('invalidtokensession', 'webservice'));
+ }
+ }
+
+ if ($token->iprestriction and !address_in_subnet(getremoteaddr(), $token->iprestriction)) {
+ add_to_log(1, 'webservice', get_string('tokenauthlog', 'webservice'), '' , get_string('failedtolog', 'webservice').": ".getremoteaddr() , 0);
+ throw new webservice_access_exception(get_string('invalidiptoken', 'webservice'));
+ }
+
+ $this->restricted_context = get_context_instance_by_id($token->contextid);
+ $this->restricted_serviceid = $token->externalserviceid;
+
+ $user = $DB->get_record('user', array('id'=>$token->userid, 'deleted'=>0), '*', MUST_EXIST);
+
+ // log token access
+ $DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id));
+
+ return $user;
+
+ }
}
/**
/**
* Contructor
- * @param bool $simple use simple authentication
+ * @param integer $authmethod authentication method - one of WEBSERVICE_AUTHMETHOD_*
*/
- public function __construct($simple, $zend_class) {
- $this->simple = $simple;
+ public function __construct($authmethod, $zend_class) {
+ parent::__construct($authmethod);
$this->zend_class = $zend_class;
}
*/
protected function service_class_method_body($function, $params){
$descriptionmethod = $function->methodname.'_returns()';
- $callforreturnvaluedesc = $function->classname.'::'.$descriptionmethod;
+ $callforreturnvaluedesc = $function->classname.'::'.$descriptionmethod;
return ' if ('.$callforreturnvaluedesc.' == null) {
return null;
}
* @return void
*/
protected function parse_request() {
- if ($this->simple) {
+ if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME) {
//note: some clients have problems with entity encoding :-(
if (isset($_REQUEST['wsusername'])) {
$this->username = $_REQUEST['wsusername'];
* @return void
*/
protected function session_cleanup($exception=null) {
- if ($this->simple) {
+ if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME) {
// nothing needs to be done, there is no persistent session
} else {
// close emulated session if used
/** @property mixed $returns function return value */
protected $returns = null;
- /**
- * Contructor
- * @param bool $simple use simple authentication
- */
- public function __construct($simple) {
- $this->simple = $simple;
- }
-
/**
* This method parses the request input, it needs to get:
* 1/ user authentication - username+password or token
/**
* Contructor
*/
- public function __construct($simple) {
- parent::__construct($simple);
+ public function __construct($authmethod) {
+ parent::__construct($authmethod);
$this->wsname = 'rest';
}
die;
}
-$server = new webservice_rest_server(false);
+$server = new webservice_rest_server(WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN);
$server->run();
die;
die;
}
-$server = new webservice_rest_server(true);
+$server = new webservice_rest_server(WEBSERVICE_AUTHMETHOD_USERNAME);
$server->run();
die;
* Contructor
* @param bool $simple use simple authentication
*/
- public function __construct($simple) {
+ public function __construct($authmethod) {
// must not cache wsdl - the list of functions is created on the fly
ini_set('soap.wsdl_cache_enabled', '0');
require_once 'Zend/Soap/Server.php';
require_once 'Zend/Soap/AutoDiscover.php';
if (optional_param('wsdl', 0, PARAM_BOOL)) {
- parent::__construct($simple, 'Zend_Soap_AutoDiscover');
+ parent::__construct($authmethod, 'Zend_Soap_AutoDiscover');
} else {
- parent::__construct($simple, 'Zend_Soap_Server');
+ parent::__construct($authmethod, 'Zend_Soap_Server');
}
$this->wsname = 'soap';
}
die;
}
-$server = new webservice_soap_server(false);
+$server = new webservice_soap_server(WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN);
$server->run();
die;
die;
}
-$server = new webservice_soap_server(true);
+$server = new webservice_soap_server(WEBSERVICE_AUTHMETHOD_USERNAME);
$server->run();
die;
class webservice_xmlrpc_server extends webservice_zend_server {
/**
* Contructor
- * @param bool $simple use simple authentication
+ * @param integer $authmethod authentication method one of WEBSERVICE_AUTHMETHOD_*
*/
- public function __construct($simple) {
+ public function __construct($authmethod) {
require_once 'Zend/XmlRpc/Server.php';
- parent::__construct($simple, 'Zend_XmlRpc_Server');
+ parent::__construct($authmethod, 'Zend_XmlRpc_Server');
$this->wsname = 'xmlrpc';
}
die;
}
-$server = new webservice_xmlrpc_server(false);
+$server = new webservice_xmlrpc_server(WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN);
$server->run();
die;
die;
}
-$server = new webservice_xmlrpc_server(true);
+$server = new webservice_xmlrpc_server(WEBSERVICE_AUTHMETHOD_USERNAME);
$server->run();
die;