$count2 = 0; foreach ($list as $str) { if ($str != "" && substr_count($str, ":")) { $tmp = explode(": ", $str); $tmp_obj->{$tmp}[0] = trim($tmp[1]); } else { if ($tmp_obj->Event && $tmp_obj->Event == "PeerEntry") { $obj[$count] = $tmp_obj; unset($tmp_obj); $count++; } } } foreach ($obj as $item) { $peername = $item->ObjectName; $data = get_peer($peername); $data = explode("\r\n", $data); foreach ($data as $str) { if ($str != "" && substr_count($str, ":")) { $tmp = explode(": ", $str); if ($tmp[0] == "ChanVariable") { $ttt = explode("=", $tmp[1]); $var = $tmp[0] . "_" . $ttt[0]; $tmp_obj->{$var} = $ttt[1]; } else { $tmp_obj->{$tmp}[0] = trim($tmp[1]); } } else { if ($tmp_obj->ChanObjectType && $tmp_obj->ChanObjectType == "peer") { $obj2[$count2] = $tmp_obj; unset($tmp_obj);
/** * Gets data about users who have been online in the last while. * * The time is configured by setting the 'accessidletimeout' configuration * option. * * Limits the number of users to display based on the 'onlineuserssideblockmaxusers' * site configuration option and the Institution specific 'showonlineusers' setting. * If the user belongs to no institution (other than the standard 'mahara' one) then * the decision will be to show ALL users by default. * */ function onlineusers_sideblock() { global $USER; // Determine what level of users to show // 0 = none, 1 = institution/s only, 2 = all users $showusers = 2; $institutions = $USER->institutions; if (!empty($institutions)) { $showusers = 0; foreach ($institutions as $i) { if ($i->showonlineusers == 2) { $showusers = 2; break; } if ($i->showonlineusers == 1) { $showusers = 1; } } } $maxonlineusers = get_config('onlineuserssideblockmaxusers'); switch ($showusers) { case 0: // show none return array('users' => array(), 'count' => 0, 'lastminutes' => floor(get_config('accessidletimeout') / 60)); case 1: // show institution only $sql = 'SELECT DISTINCT u.* FROM {usr} u JOIN {usr_institution} i ON u.id = i.usr WHERE i.institution IN (' . join(',', array_map('db_quote', array_keys($institutions))) . ') AND lastaccess > ? AND deleted = 0 ORDER BY lastaccess DESC'; break; case 2: // show all $sql = 'SELECT * FROM {usr} WHERE lastaccess > ? AND deleted = 0 ORDER BY lastaccess DESC'; break; } $onlineusers = get_records_sql_array($sql, array(db_format_timestamp(time() - get_config('accessidletimeout'))), 0, $maxonlineusers); if ($onlineusers) { foreach ($onlineusers as &$user) { $user->profileiconurl = profile_icon_url($user, 20, 20); // If the user is an MNET user, show where they've come from $authobj = AuthFactory::create($user->authinstance); if ($authobj->authname == 'xmlrpc') { $peer = get_peer($authobj->wwwroot); $user->loggedinfrom = $peer->name; } } } else { $onlineusers = array(); } return array('users' => $onlineusers, 'count' => count($onlineusers), 'lastminutes' => floor(get_config('accessidletimeout') / 60)); }
/** * Overrides the default logout mechanism to do proper single singout */ public function logout() { global $USER, $SESSION; $sessionlogouttime = $USER->get('logout_time'); if (get_config('usersuniquebyusername')) { // The auth_remote_user will have a row for the institution in // which the user SSOed into first. However, they could have // been coming from somewhere else this time, which is why we // can't use auth_remote_user for the lookup. Their username // won't change for their Mahara account anyway, so just grab // it out of the usr table. $remoteusername = get_field('usr', 'username', 'id', $USER->get('id')); } else { // Check the auth_remote_user table for what the remote // application thinks the username is $remoteusername = get_field('auth_remote_user', 'remoteusername', 'localusr', $USER->get('id'), 'authinstance', $this->instanceid); if (!$remoteusername && $this->parent) { $remoteusername = get_field('auth_remote_user', 'remoteusername', 'localusr', $USER->get('id'), 'authinstance', $this->parent); } } $USER->logout(); // Unset locked fields $SESSION->clear('lockedfields'); if (isset($_GET['logout'])) { // Explicit logout request $this->kill_parent($remoteusername); redirect($this->wwwroot); } elseif (!$this->parent) { $this->kill_parent($remoteusername); if ($sessionlogouttime == 0 || $sessionlogouttime > time()) { // Redirect back to their IDP if they don't have a parent auth method set // (aka: they can't log in at Mahara's log in form) $peer = get_peer($this->wwwroot); // TODO: This should be stored in the application config table $jumpurl = str_replace('land', 'jump', $peer->application->ssolandurl); redirect($this->wwwroot . $jumpurl . '?hostwwwroot=' . dropslash(get_config('wwwroot')) . '&wantsurl=' . urlencode($_SERVER['REQUEST_URI'])); } } // Anything else is a session timeout $SESSION->set('mnetuser', null); }
/** * Get user records for online users page * * @param integer $limit * @param integer $offset * * @returns array Total number of users, along with $limit or fewer user records. */ function get_onlineusers($limit = 10, $offset = 0, $orderby = 'firstname,lastname') { global $USER; // Determine what level of users to show // 0 = none, 1 = institution/s only, 2 = all users $showusers = 2; $institutions = $USER->institutions; if (!empty($institutions)) { $showusers = 0; foreach ($institutions as $i) { if ($i->showonlineusers == 2) { $showusers = 2; break; } if ($i->showonlineusers == 1) { $showusers = 1; } } } $result = array('count' => 0, 'limit' => $limit, 'offset' => $offset, 'data' => false); switch ($showusers) { case 0: // show none return $result; case 1: // show institution only $sql = "SELECT DISTINCT u.* FROM {usr} u JOIN {usr_institution} i ON id = i.usr\n WHERE deleted = 0 AND lastaccess > ? AND i.institution IN (" . join(',', array_map('db_quote', array_keys($institutions))) . ")\n ORDER BY {$orderby}"; $countsql = 'SELECT count(DISTINCT id) FROM {usr} JOIN {usr_institution} i ON id = i.usr WHERE deleted = 0 AND lastaccess > ? AND i.institution IN (' . join(',', array_map('db_quote', array_keys($institutions))) . ')'; break; case 2: // show all $sql = "SELECT * FROM {usr} WHERE deleted = 0 AND lastaccess > ? ORDER BY {$orderby}"; $countsql = 'SELECT count(id) FROM {usr} WHERE deleted = 0 AND lastaccess > ?'; break; } $lastaccess = db_format_timestamp(time() - get_config('accessidletimeout')); if (!($result['count'] = count_records_sql($countsql, array($lastaccess)))) { return $result; } $onlineusers = get_records_sql_array($sql, array($lastaccess), $offset, $limit); if ($onlineusers) { foreach ($onlineusers as &$user) { $user->profileiconurl = profile_icon_url($user, 20, 20); // If the user is an MNET user, show where they've come from $authobj = AuthFactory::create($user->authinstance); if ($authobj->authname == 'xmlrpc') { $peer = get_peer($authobj->wwwroot); $user->loggedinfrom = $peer->name; } } } else { $onlineusers = array(); } $result['data'] = array_map(create_function('$a', 'return $a->id;'), $onlineusers); return $result; }
/** * Check that the signature has been signed by the remote host. */ function xmldsig_envelope_strip(&$xml) { $signature = base64_decode($xml->Signature->SignatureValue); $payload = base64_decode($xml->object); $wwwroot = (string) $xml->wwwroot; $timestamp = $xml->timestamp; $peer = get_peer($wwwroot); // Does the signature match the data and the public cert? $signature_verified = openssl_verify($payload, $signature, $peer->certificate); if ($signature_verified == 0) { // Maybe the remote host is using a new key? // Make a dummy request so we'll be given a new key log_info("Signature verification for message from {$wwwroot} failed, checking to see if they have a new signature for us"); require_once get_config('docroot') . 'api/xmlrpc/client.php'; $client = new Client(); $client->set_method('system/listServices')->send($wwwroot); // Now use the new key and re-try verification $peer = get_peer($wwwroot, false); $signature_verified = openssl_verify($payload, $signature, $peer->certificate); } if ($signature_verified == 1) { // Parse the XML try { $xml = new SimpleXMLElement($payload); return $payload; } catch (Exception $e) { throw new MaharaException('Signed payload is not a valid XML document', 6007); } } throw new MaharaException('An error occurred while trying to verify your message signature', 6004); }
function send($wwwroot, $use_cached_peer = true) { $this->peer = get_peer($wwwroot, $use_cached_peer); $this->response = ''; $URL = $this->peer->wwwroot . $this->peer->application->xmlrpcserverurl; $this->requesttext = xmlrpc_encode_request($this->method, $this->params, array("encoding" => "utf-8")); $this->signedrequest = xmldsig_envelope($this->requesttext); $this->encryptedrequest = xmlenc_envelope($this->signedrequest, $this->peer->certificate); $config = array(CURLOPT_URL => $URL, CURLOPT_TIMEOUT => $this->timeout, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_USERAGENT => 'Mahara', CURLOPT_POSTFIELDS => $this->encryptedrequest, CURLOPT_HTTPHEADER => array("Content-Type: text/xml charset=UTF-8", 'Expect: '), CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 0); $result = mahara_http_request($config); $timestamp_send = time(); $this->rawresponse = $result->data; $response_code = $result->info['http_code']; $response_code_prefix = substr($response_code, 0, 1); if ('2' != $response_code_prefix) { if ('4' == $response_code_prefix) { throw new XmlrpcClientException('Client error code: ' . $response_code); } else { if ('5' == $response_code_prefix) { throw new XmlrpcClientException('An error occurred at the remote server. Code: ' . $response_code); } } } $timestamp_receive = time(); $remote_timestamp = null; $curl_errno = $result->errno; if ($curl_errno || $this->rawresponse == false) { throw new XmlrpcClientException('Curl error: ' . $curl_errno . ': ' . $result->error); return false; } try { $xml = new SimpleXMLElement(trim($this->rawresponse)); } catch (Exception $e) { log_debug($this->rawresponse); throw new XmlrpcClientException('Payload is not a valid XML document (payload is above)', 6001); } try { if ($xml->getName() == 'encryptedMessage') { $payload_encrypted = true; $wwwroot = (string) $xml->wwwroot; // Strip encryption, using an older code is OK, because we're the client. // The server is able to respond with the correct key, be we're not $payload = xmlenc_envelope_strip($xml, true); } if ($xml->getName() == 'signedMessage') { $payload_signed = true; $remote_timestamp = $xml->timestamp; $payload = xmldsig_envelope_strip($xml); } } catch (CryptException $e) { throw new XmlrpcClientException("An error occurred while decrypting a message sent by {$wwwroot}. Unable to authenticate the user."); } if ($xml->getName() == 'methodResponse') { $this->response = xmlrpc_decode($payload); // Margin of error is the time it took the request to complete. $margin_of_error = $timestamp_receive - $timestamp_send; // Guess the time gap between sending the request and the remote machine // executing the time() function. Marginally better than nothing. $hysteresis = $margin_of_error / 2; if (!empty($remote_timestamp)) { $remote_timestamp = $remote_timestamp - $hysteresis; $time_offset = $remote_timestamp - $timestamp_send; if ($time_offset > self::get_max_server_time_difference()) { throw new XmlrpcClientException('Time drift (' . $margin_of_error . ', ' . $time_offset . ') is too large.'); } } if (is_array($this->response) && array_key_exists('faultCode', $this->response)) { if ($this->response['faultCode'] == 7025) { log_info('Remote application has sent us a new public key'); // The remote application sent back a new public key, the // old one must have expired if (array_key_exists('faultString', $this->response)) { $details = openssl_x509_parse($this->response['faultString']); if (isset($details['validTo_time_t'])) { $updateobj = (object) array('publickey' => $this->response['faultString'], 'publickeyexpires' => $details['validTo_time_t']); $whereobj = (object) array('wwwroot' => $wwwroot); update_record('host', $updateobj, $whereobj); log_info('New key has been imported. Valid until ' . date('Y/m/d h:i:s', $details['validTo_time_t'])); // Send request again. But don't use the cached // peer, look it up again now we've changed the // public key $this->send($wwwroot, false); } else { throw new XmlrpcClientException('Could not parse new public key'); } } else { throw new XmlrpcClientException('Remote site claims to have sent a public key, but they LIE'); } } throw new XmlrpcClientException('Unknown error occurred: ' . $this->response['faultCode'] . ': ' . $this->response['faultString']); } // Clean up so object can be re-used. $this->requesttext = ''; $this->signedrequest = ''; $this->encryptedrequest = ''; $this->params = array(); $this->method = ''; return true; } else { throw new XmlrpcClientException('Unrecognized XML document form: ' . $payload); } }
xmlrpc_error($e->getMessage(), $e->getCode()); $response = ob_get_contents(); ob_end_clean(); // Sign and encrypt our response, even though we don't know if the // request was signed and encrypted $response = xmldsig_envelope($response); $peer = get_peer($REMOTEWWWROOT); $response = xmlenc_envelope($response, $peer->certificate); echo $response; exit; } } if ($xml->getName() == 'methodCall') { // $payload ? if (empty($xml->methodName)) { throw new XmlrpcServerException('Payload is not an XML-RPC document', 6008); } $Dispatcher = new Dispatcher($payload, $payload_signed, $payload_encrypted); if ($payload_signed) { $response = xmldsig_envelope($Dispatcher->response); } else { $response = $Dispatcher->response; } if ($payload_encrypted) { $peer = get_peer($REMOTEWWWROOT); $response = xmlenc_envelope($response, $peer->certificate); } echo $response; } else { throw new XmlrpcServerException('Unrecognized XML document form: ' . var_export($xml, 1), 6009); }
/** * Gets data about users who have been online in the last while. * * The time is configured by setting the 'accessidletimeout' configuration * option. * * NOTE: currently returns all online users, this might not be desirable on a * really busy site. */ function onlineusers_sideblock() { global $USER; $onlineusers = get_records_select_array('usr', 'deleted = 0 AND lastaccess > ?', array(db_format_timestamp(time() - get_config('accessidletimeout'))), 'lastaccess DESC'); if ($onlineusers) { foreach ($onlineusers as &$user) { // Use 'profileiconbyid' for the current user, just in case they change their profile icon if ($user->id == $USER->get('id')) { $user->profileiconurl = get_config('wwwroot') . 'thumb.php?type=profileiconbyid&id=' . (int) $user->profileicon . '&size=20x20'; } else { $user->profileiconurl = get_config('wwwroot') . 'thumb.php?type=profileicon&id=' . $user->id . '&size=20x20'; } // If the user is an MNET user, show where they've come from $authobj = AuthFactory::create($user->authinstance); if ($authobj->authname == 'xmlrpc') { $peer = get_peer($authobj->wwwroot); $user->loggedinfrom = $peer->name; } } } else { $onlineusers = array(); } return array('users' => $onlineusers, 'count' => count($onlineusers), 'lastminutes' => floor(get_config('accessidletimeout') / 60)); }