From ff54fa54bcfc8d39d92b5e42f0111fed5cd16a12 Mon Sep 17 00:00:00 2001 From: nickw Date: Tue, 5 Oct 2010 20:16:40 +0000 Subject: Changing the name of the oAuth plugin to reflect it's library service status. Refs #2532 git-svn-id: http://code.elgg.org/elgg/trunk@7015 36083f99-b078-4883-b0ff-0f9b5a30f544 --- .../vendors/oauth/library/OAuthDiscovery.php | 226 +++ .../vendors/oauth/library/OAuthException.php | 50 + .../vendors/oauth/library/OAuthRequest.php | 801 +++++++++ .../vendors/oauth/library/OAuthRequestLogger.php | 274 +++ .../vendors/oauth/library/OAuthRequestSigner.php | 209 +++ .../vendors/oauth/library/OAuthRequestVerifier.php | 262 +++ .../vendors/oauth/library/OAuthRequester.php | 508 ++++++ .../vendors/oauth/library/OAuthServer.php | 232 +++ mod/oauth_lib/vendors/oauth/library/OAuthStore.php | 86 + .../library/body/OAuthBodyContentDisposition.php | 129 ++ .../library/body/OAuthBodyMultipartFormdata.php | 143 ++ .../vendors/oauth/library/discovery/xrds_parse.php | 304 ++++ .../vendors/oauth/library/discovery/xrds_parse.txt | 101 ++ .../OAuthSignatureMethod.class.php | 69 + .../OAuthSignatureMethod_HMAC_SHA1.php | 115 ++ .../signature_method/OAuthSignatureMethod_MD5.php | 95 + .../OAuthSignatureMethod_PLAINTEXT.php | 80 + .../OAuthSignatureMethod_RSA_SHA1.php | 136 ++ .../library/store/OAuthStoreAbstract.class.php | 149 ++ .../oauth/library/store/OAuthStoreAnyMeta.php | 265 +++ .../oauth/library/store/OAuthStoreMySQL.php | 1879 ++++++++++++++++++++ .../vendors/oauth/library/store/mysql/install.php | 32 + .../vendors/oauth/library/store/mysql/mysql.sql | 219 +++ 23 files changed, 6364 insertions(+) create mode 100644 mod/oauth_lib/vendors/oauth/library/OAuthDiscovery.php create mode 100644 mod/oauth_lib/vendors/oauth/library/OAuthException.php create mode 100644 mod/oauth_lib/vendors/oauth/library/OAuthRequest.php create mode 100644 mod/oauth_lib/vendors/oauth/library/OAuthRequestLogger.php create mode 100644 mod/oauth_lib/vendors/oauth/library/OAuthRequestSigner.php create mode 100644 mod/oauth_lib/vendors/oauth/library/OAuthRequestVerifier.php create mode 100644 mod/oauth_lib/vendors/oauth/library/OAuthRequester.php create mode 100644 mod/oauth_lib/vendors/oauth/library/OAuthServer.php create mode 100644 mod/oauth_lib/vendors/oauth/library/OAuthStore.php create mode 100644 mod/oauth_lib/vendors/oauth/library/body/OAuthBodyContentDisposition.php create mode 100644 mod/oauth_lib/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php create mode 100644 mod/oauth_lib/vendors/oauth/library/discovery/xrds_parse.php create mode 100644 mod/oauth_lib/vendors/oauth/library/discovery/xrds_parse.txt create mode 100644 mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php create mode 100644 mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php create mode 100644 mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php create mode 100644 mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php create mode 100644 mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php create mode 100644 mod/oauth_lib/vendors/oauth/library/store/OAuthStoreAbstract.class.php create mode 100644 mod/oauth_lib/vendors/oauth/library/store/OAuthStoreAnyMeta.php create mode 100644 mod/oauth_lib/vendors/oauth/library/store/OAuthStoreMySQL.php create mode 100644 mod/oauth_lib/vendors/oauth/library/store/mysql/install.php create mode 100644 mod/oauth_lib/vendors/oauth/library/store/mysql/mysql.sql (limited to 'mod/oauth_lib/vendors/oauth/library') diff --git a/mod/oauth_lib/vendors/oauth/library/OAuthDiscovery.php b/mod/oauth_lib/vendors/oauth/library/OAuthDiscovery.php new file mode 100644 index 000000000..d097756dd --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/OAuthDiscovery.php @@ -0,0 +1,226 @@ + + * @date Sep 4, 2008 5:05:19 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__).'/discovery/xrds_parse.php'; + +require_once dirname(__FILE__).'/OAuthException.php'; +require_once dirname(__FILE__).'/OAuthRequestLogger.php'; + + +class OAuthDiscovery +{ + /** + * Return a description how we can do a consumer allocation. Prefers static allocation if + * possible. If static allocation is possible + * + * See also: http://oauth.net/discovery/#consumer_identity_types + * + * @param string uri + * @return array provider description + */ + static function discover ( $uri ) + { + // See what kind of consumer allocations are available + $xrds_file = self::discoverXRDS($uri); + if (!empty($xrds_file)) + { + $xrds = xrds_parse($xrds_file); + if (empty($xrds)) + { + throw new OAuthException('Could not discover OAuth information for '.$uri); + } + } + else + { + throw new OAuthException('Could not discover XRDS file at '.$uri); + } + + // Fill an OAuthServer record for the uri found + $ps = parse_url($uri); + $host = isset($ps['host']) ? $ps['host'] : 'localhost'; + $server_uri = $ps['scheme'].'://'.$host.'/'; + + $p = array( + 'user_id' => null, + 'consumer_key' => '', + 'consumer_secret' => '', + 'signature_methods' => '', + 'server_uri' => $server_uri, + 'request_token_uri' => '', + 'authorize_uri' => '', + 'access_token_uri' => '' + ); + + + // Consumer identity (out of bounds or static) + if (isset($xrds['consumer_identity'])) + { + // Try to find a static consumer allocation, we like those :) + foreach ($xrds['consumer_identity'] as $ci) + { + if ($ci['method'] == 'static' && !empty($ci['consumer_key'])) + { + $p['consumer_key'] = $ci['consumer_key']; + $p['consumer_secret'] = ''; + } + else if ($ci['method'] == 'oob' && !empty($ci['uri'])) + { + // TODO: Keep this uri somewhere for the user? + $p['consumer_oob_uri'] = $ci['uri']; + } + } + } + + // The token uris + if (isset($xrds['request'][0]['uri'])) + { + $p['request_token_uri'] = $xrds['request'][0]['uri']; + if (!empty($xrds['request'][0]['signature_method'])) + { + $p['signature_methods'] = $xrds['request'][0]['signature_method']; + } + } + if (isset($xrds['authorize'][0]['uri'])) + { + $p['authorize_uri'] = $xrds['authorize'][0]['uri']; + if (!empty($xrds['authorize'][0]['signature_method'])) + { + $p['signature_methods'] = $xrds['authorize'][0]['signature_method']; + } + } + if (isset($xrds['access'][0]['uri'])) + { + $p['access_token_uri'] = $xrds['access'][0]['uri']; + if (!empty($xrds['access'][0]['signature_method'])) + { + $p['signature_methods'] = $xrds['access'][0]['signature_method']; + } + } + return $p; + } + + + /** + * Discover the XRDS file at the uri. This is a bit primitive, you should overrule + * this function so that the XRDS file can be cached for later referral. + * + * @param string uri + * @return string false when no XRDS file found + */ + static protected function discoverXRDS ( $uri, $recur = 0 ) + { + // Bail out when we are following redirects + if ($recur > 10) + { + return false; + } + + $data = self::curl($uri); + + // Check what we got back, could be: + // 1. The XRDS discovery file itself (check content-type) + // 2. The X-XRDS-Location header + + if (is_string($data) && !empty($data)) + { + list($head,$body) = explode("\r\n\r\n", $data); + $body = trim($body); + $m = false; + + // See if we got the XRDS file itself or we have to follow a location header + if ( preg_match('/^Content-Type:\s*application\/xrds+xml/im', $head) + || preg_match('/^<\?xml[^>]*\?>\s* \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/OAuthException.php b/mod/oauth_lib/vendors/oauth/library/OAuthException.php new file mode 100644 index 000000000..cadd1d032 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/OAuthException.php @@ -0,0 +1,50 @@ + + * @date Nov 29, 2007 5:33:54 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// TODO: something with the HTTP return code matching to the problem + +require_once dirname(__FILE__) . '/OAuthRequestLogger.php'; + +class OAuthException extends Exception +{ + function __construct ( $message ) + { + Exception::__construct($message); + OAuthRequestLogger::addNote('OAuthException: '.$message); + } + +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/OAuthRequest.php b/mod/oauth_lib/vendors/oauth/library/OAuthRequest.php new file mode 100644 index 000000000..c0d6ddbc7 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/OAuthRequest.php @@ -0,0 +1,801 @@ + + * @date Nov 16, 2007 12:20:31 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__) . '/OAuthException.php'; + +/** + * Object to parse an incoming OAuth request or prepare an outgoing OAuth request + */ +class OAuthRequest +{ + /* the realm for this request */ + protected $realm; + + /* all the parameters, RFC3986 encoded name/value pairs */ + protected $param = array(); + + /* the parsed request uri */ + protected $uri_parts; + + /* the raw request uri */ + protected $uri; + + /* the request headers */ + protected $headers; + + /* the request method */ + protected $method; + + /* the body of the OAuth request */ + protected $body; + + + /** + * Construct from the current request. Useful for checking the signature of a request. + * When not supplied with any parameters this will use the current request. + * + * @param string uri might include parameters + * @param string method GET, PUT, POST etc. + * @param string parameters additional post parameters, urlencoded (RFC1738) + * @param array headers headers for request + * @param string body optional body of the OAuth request (POST or PUT) + */ + function __construct ( $uri = null, $method = 'GET', $parameters = '', $headers = array(), $body = null ) + { + if (empty($uri)) + { + if (is_object($_SERVER)) + { + // Tainted arrays - the normal stuff in anyMeta + $method = $_SERVER->REQUEST_METHOD->getRawUnsafe(); + $uri = $_SERVER->REQUEST_URI->getRawUnsafe(); + } + else + { + // non anyMeta systems + $method = $_SERVER['REQUEST_METHOD']; + $uri = $_SERVER['REQUEST_URI']; + } + $headers = getallheaders(); + $parameters = ''; + $this->method = strtoupper($method); + + // If this is a post then also check the posted variables + if (strcasecmp($method, 'POST') == 0) + { + /* + // TODO: what to do with 'multipart/form-data'? + if ($this->getRequestContentType() == 'multipart/form-data') + { + throw new OAuthException('Unsupported POST content type, expected "application/x-www-form-urlencoded" got "'.@$_SERVER['CONTENT_TYPE'].'"'); + } + */ + if ($this->getRequestContentType() == 'application/x-www-form-urlencoded') + { + // Get the posted body (when available) + if (!isset($headers['X-OAuth-Test'])) + { + $parameters .= $this->getRequestBody(); + } + } + else + { + $body = $this->getRequestBody(); + } + } + else if (strcasecmp($method, 'PUT') == 0) + { + $body = $this->getRequestBody(); + } + } + + $this->method = strtoupper($method); + $this->headers = $headers; + // Store the values, prepare for oauth + $this->uri = $uri; + $this->body = $body; + $this->parseUri($parameters); + $this->parseHeaders(); + $this->transcodeParams(); + } + + + /** + * Return the signature base string. + * Note that we can't use rawurlencode due to specified use of RFC3986. + * + * @return string + */ + function signatureBaseString () + { + $sig = array(); + $sig[] = $this->method; + $sig[] = $this->getRequestUrl(); + $sig[] = $this->getNormalizedParams(); + + return implode('&', array_map(array($this, 'urlencode'), $sig)); + } + + + /** + * Calculate the signature of the request, using the method in oauth_signature_method. + * The signature is returned encoded in the form as used in the url. So the base64 and + * urlencoding has been done. + * + * @param string consumer_secret + * @param string token_secret + * @exception when not all parts available + * @return string + */ + function calculateSignature ( $consumer_secret, $token_secret, $token_type = 'access' ) + { + $required = array( + 'oauth_consumer_key', + 'oauth_signature_method', + 'oauth_timestamp', + 'oauth_nonce' + ); + + if ($token_type !== false) + { + $required[] = 'oauth_token'; + } + + foreach ($required as $req) + { + if (!isset($this->param[$req])) + { + throw new OAuthException('Can\'t sign request, missing parameter "'.$req.'"'); + } + } + + $this->checks(); + + $base = $this->signatureBaseString(); + $signature = $this->calculateDataSignature($base, $consumer_secret, $token_secret, $this->param['oauth_signature_method']); + return $signature; + } + + + /** + * Calculate the signature of a string. + * Uses the signature method from the current parameters. + * + * @param string data + * @param string consumer_secret + * @param string token_secret + * @param string signature_method + * @exception OAuthException thrown when the signature method is unknown + * @return string signature + */ + function calculateDataSignature ( $data, $consumer_secret, $token_secret, $signature_method ) + { + if (is_null($data)) + { + $data = ''; + } + + $sig = $this->getSignatureMethod($signature_method); + return $sig->signature($this, $data, $consumer_secret, $token_secret); + } + + + /** + * Select a signature method from the list of available methods. + * We try to check the most secure methods first. + * + * @todo Let the signature method tell us how secure it is + * @param array methods + * @exception OAuthException when we don't support any method in the list + * @return string + */ + public function selectSignatureMethod ( $methods ) + { + if (in_array('HMAC-SHA1', $methods)) + { + $method = 'HMAC-SHA1'; + } + else if (in_array('MD5', $methods)) + { + $method = 'MD5'; + } + else + { + $method = false; + foreach ($methods as $m) + { + $m = strtoupper($m); + $m = preg_replace('/[^A-Z0-9]/', '_', $m); + if (file_exists(dirname(__FILE__).'/signature_method/OAuthSignatureMethod_'.$m.'.php')) + { + $method = $m; + break; + } + } + + if (empty($method)) + { + throw new OAuthException('None of the signing methods is supported.'); + } + } + return $method; + } + + + /** + * Fetch the signature object used for calculating and checking the signature base string + * + * @param string method + * @return OAuthSignatureMethod object + */ + function getSignatureMethod ( $method ) + { + $m = strtoupper($method); + $m = preg_replace('/[^A-Z0-9]/', '_', $m); + $class = 'OAuthSignatureMethod_'.$m; + + if (file_exists(dirname(__FILE__).'/signature_method/'.$class.'.php')) + { + require_once dirname(__FILE__).'/signature_method/'.$class.'.php'; + $sig = new $class(); + } + else + { + throw new OAuthException('Unsupported signature method "'.$m.'".'); + } + return $sig; + } + + + /** + * Perform some sanity checks. + * + * @exception OAuthException thrown when sanity checks failed + */ + function checks () + { + if (isset($this->param['oauth_version'])) + { + $version = $this->urldecode($this->param['oauth_version']); + if ($version != '1.0') + { + throw new OAuthException('Expected OAuth version 1.0, got "'.$this->param['oauth_version'].'"'); + } + } + } + + + /** + * Return the request method + * + * @return string + */ + function getMethod () + { + return $this->method; + } + + /** + * Return the complete parameter string for the signature check. + * All parameters are correctly urlencoded and sorted on name and value + * + * @return string + */ + function getNormalizedParams () + { + /* + // sort by name, then by value + // (needed when we start allowing multiple values with the same name) + $keys = array_keys($this->param); + $values = array_values($this->param); + array_multisort($keys, SORT_ASC, $values, SORT_ASC); + */ + $params = $this->param; + $normalized = array(); + + ksort($params); + foreach ($params as $key => $value) + { + // all names and values are already urlencoded, exclude the oauth signature + if ($key != 'oauth_signature') + { + if (is_array($value)) + { + $value_sort = $value; + sort($value_sort); + foreach ($value_sort as $v) + { + $normalized[] = $key.'='.$v; + } + } + else + { + $normalized[] = $key.'='.$value; + } + } + } + return implode('&', $normalized); + } + + + /** + * Return the normalised url for signature checks + */ + function getRequestUrl () + { + $url = $this->uri_parts['scheme'] . '://' + . $this->uri_parts['user'] . (!empty($this->uri_parts['pass']) ? ':' : '') + . $this->uri_parts['pass'] . (!empty($this->uri_parts['user']) ? '@' : '') + . $this->uri_parts['host']; + + if ( $this->uri_parts['port'] + && $this->uri_parts['port'] != $this->defaultPortForScheme($this->uri_parts['scheme'])) + { + $url .= ':'.$this->uri_parts['port']; + } + if (!empty($this->uri_parts['path'])) + { + $url .= $this->uri_parts['path']; + } + return $url; + } + + + /** + * Get a parameter, value is always urlencoded + * + * @param string name + * @param boolean urldecode set to true to decode the value upon return + * @return string value false when not found + */ + function getParam ( $name, $urldecode = false ) + { + if (isset($this->param[$name])) + { + $s = $this->param[$name]; + } + else if (isset($this->param[$this->urlencode($name)])) + { + $s = $this->param[$this->urlencode($name)]; + } + else + { + $s = false; + } + if (!empty($s) && $urldecode) + { + if (is_array($s)) + { + $s = array_map(array($this,'urldecode'), $s); + } + else + { + $s = $this->urldecode($s); + } + } + return $s; + } + + /** + * Set a parameter + * + * @param string name + * @param string value + * @param boolean encoded set to true when the values are already encoded + */ + function setParam ( $name, $value, $encoded = false ) + { + if (!$encoded) + { + $name_encoded = $this->urlencode($name); + if (is_array($value)) + { + foreach ($value as $v) + { + $this->param[$name_encoded][] = $this->urlencode($v); + } + } + else + { + $this->param[$name_encoded] = $this->urlencode($value); + } + } + else + { + $this->param[$name] = $value; + } + } + + + /** + * Re-encode all parameters so that they are encoded using RFC3986. + * Updates the $this->param attribute. + */ + protected function transcodeParams () + { + $params = $this->param; + $this->param = array(); + + foreach ($params as $name=>$value) + { + if (is_array($value)) + { + $this->param[$this->urltranscode($name)] = array_map(array($this,'urltranscode'), $value); + } + else + { + $this->param[$this->urltranscode($name)] = $this->urltranscode($value); + } + } + } + + + + /** + * Return the body of the OAuth request. + * + * @return string null when no body + */ + function getBody () + { + return $this->body; + } + + + /** + * Return the body of the OAuth request. + * + * @return string null when no body + */ + function setBody ( $body ) + { + $this->body = $body; + } + + + /** + * Parse the uri into its parts. Fill in the missing parts. + * + * @todo check for the use of https, right now we default to http + * @todo support for multiple occurences of parameters + * @param string $parameters optional extra parameters (from eg the http post) + */ + protected function parseUri ( $parameters ) + { + $ps = parse_url($this->uri); + + // Get the current/requested method + if (empty($ps['scheme'])) + { + $ps['scheme'] = 'http'; + } + else + { + $ps['scheme'] = strtolower($ps['scheme']); + } + + // Get the current/requested host + if (empty($ps['host'])) + { + if (isset($_SERVER['HTTP_HOST'])) + { + $ps['host'] = $_SERVER['HTTP_HOST']; + } + else + { + $ps['host'] = ''; + } + } + $ps['host'] = mb_strtolower($ps['host']); + if (!preg_match('/^[a-z0-9\.\-]+$/', $ps['host'])) + { + throw new OAuthException('Unsupported characters in host name'); + } + + // Get the port we are talking on + if (empty($ps['port'])) + { + $ps['port'] = $this->defaultPortForScheme($ps['scheme']); + } + + if (empty($ps['user'])) + { + $ps['user'] = ''; + } + if (empty($ps['pass'])) + { + $ps['pass'] = ''; + } + if (empty($ps['path'])) + { + $ps['path'] = '/'; + } + if (empty($ps['query'])) + { + $ps['query'] = ''; + } + if (empty($ps['fragment'])) + { + $ps['fragment'] = ''; + } + + // Now all is complete - parse all parameters + foreach (array($ps['query'], $parameters) as $params) + { + if (strlen($params) > 0) + { + $params = explode('&', $params); + foreach ($params as $p) + { + @list($name, $value) = explode('=', $p, 2); + $this->param[$name] = $value; + } + } + } + $this->uri_parts = $ps; + } + + + /** + * Return the default port for a scheme + * + * @param string scheme + * @return int + */ + protected function defaultPortForScheme ( $scheme ) + { + switch ($scheme) + { + case 'http': return 80; + case 'https': return 43; + default: + throw new OAuthException('Unsupported scheme type, expected http or https, got "'.$scheme.'"'); + break; + } + } + + + /** + * Encode a string according to the RFC3986 + * + * @param string s + * @return string + */ + function urlencode ( $s ) + { + if ($s === false) + { + return $s; + } + else + { + return str_replace('%7E', '~', rawurlencode($s)); + } + } + + /** + * Decode a string according to RFC3986. + * Also correctly decodes RFC1738 urls. + * + * @param string s + * @return string + */ + function urldecode ( $s ) + { + if ($s === false) + { + return $s; + } + else + { + return rawurldecode($s); + } + } + + /** + * urltranscode - make sure that a value is encoded using RFC3986. + * We use a basic urldecode() function so that any use of '+' as the + * encoding of the space character is correctly handled. + * + * @param string s + * @return string + */ + function urltranscode ( $s ) + { + if ($s === false) + { + return $s; + } + else + { + return $this->urlencode(urldecode($s)); + } + } + + + /** + * Parse the oauth parameters from the request headers + * Looks for something like: + * + * Authorization: OAuth realm="http://photos.example.net/authorize", + * oauth_consumer_key="dpf43f3p2l4k3l03", + * oauth_token="nnch734d00sl2jdk", + * oauth_signature_method="HMAC-SHA1", + * oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", + * oauth_timestamp="1191242096", + * oauth_nonce="kllo9940pd9333jh", + * oauth_version="1.0" + */ + private function parseHeaders () + { +/* + $this->headers['Authorization'] = 'OAuth realm="http://photos.example.net/authorize", + oauth_consumer_key="dpf43f3p2l4k3l03", + oauth_token="nnch734d00sl2jdk", + oauth_signature_method="HMAC-SHA1", + oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", + oauth_timestamp="1191242096", + oauth_nonce="kllo9940pd9333jh", + oauth_version="1.0"'; +*/ + if (isset($this->headers['Authorization'])) + { + $auth = trim($this->headers['Authorization']); + if (strncasecmp($auth, 'OAuth', 4) == 0) + { + $vs = explode(',', substr($auth, 6)); + foreach ($vs as $v) + { + if (strpos($v, '=')) + { + $v = trim($v); + list($name,$value) = explode('=', $v, 2); + if (!empty($value) && $value{0} == '"' && substr($value, -1) == '"') + { + $value = substr(substr($value, 1), 0, -1); + } + + if (strcasecmp($name, 'realm') == 0) + { + $this->realm = $value; + } + else + { + $this->param[$name] = $value; + } + } + } + } + } + } + + + /** + * Fetch the content type of the current request + * + * @return string + */ + private function getRequestContentType () + { + $content_type = 'application/octet-stream'; + if (!empty($_SERVER) && array_key_exists('CONTENT_TYPE', $_SERVER)) + { + list($content_type) = explode(';', $_SERVER['CONTENT_TYPE']); + } + return trim($content_type); + } + + + /** + * Get the body of a POST or PUT. + * + * Used for fetching the post parameters and to calculate the body signature. + * + * @return string null when no body present (or wrong content type for body) + */ + private function getRequestBody () + { + $body = null; + if ($this->method == 'POST' || $this->method == 'PUT') + { + $body = ''; + $fh = @fopen('php://input', 'r'); + if ($fh) + { + while (!feof($fh)) + { + $s = fread($fh, 1024); + if (is_string($s)) + { + $body .= $s; + } + } + fclose($fh); + } + } + return $body; + } + + + /** + * Simple function to perform a redirect (GET). + * Redirects the User-Agent, does not return. + * + * @param string uri + * @param array params parameters, urlencoded + * @exception OAuthException when redirect uri is illegal + */ + public function redirect ( $uri, $params ) + { + if (!empty($params)) + { + $q = array(); + foreach ($params as $name=>$value) + { + $q[] = $name.'='.$value; + } + $q_s = implode('&', $q); + + if (strpos($uri, '?')) + { + $uri .= '&'.$q_s; + } + else + { + $uri .= '?'.$q_s; + } + } + + // simple security - multiline location headers can inject all kinds of extras + $uri = preg_replace('/\s/', '%20', $uri); + if (strncasecmp($uri, 'http://', 7) && strncasecmp($uri, 'https://', 8)) + { + if (strpos($uri, '://')) + { + throw new OAuthException('Illegal protocol in redirect uri '.$uri); + } + $uri = 'http://'.$uri; + } + + header('HTTP/1.1 302 Found'); + header('Location: '.$uri); + echo ''; + exit(); + } + +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/OAuthRequestLogger.php b/mod/oauth_lib/vendors/oauth/library/OAuthRequestLogger.php new file mode 100644 index 000000000..5b88e9d20 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/OAuthRequestLogger.php @@ -0,0 +1,274 @@ + + * @date Dec 7, 2007 12:22:43 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +class OAuthRequestLogger +{ + static private $logging = 0; + static private $enable_logging = null; + static private $store_log = null; + static private $note = ''; + static private $user_id = null; + static private $request_object = null; + static private $sent = null; + static private $received = null; + static private $log = array(); + + /** + * Start any logging, checks the system configuration if logging is needed. + * + * @param OAuthRequest $request_object + */ + static function start ( $request_object = null ) + { + if (defined('OAUTH_LOG_REQUEST')) + { + if (is_null(OAuthRequestLogger::$enable_logging)) + { + OAuthRequestLogger::$enable_logging = true; + } + if (is_null(OAuthRequestLogger::$store_log)) + { + OAuthRequestLogger::$store_log = true; + } + } + + if (OAuthRequestLogger::$enable_logging && !OAuthRequestLogger::$logging) + { + OAuthRequestLogger::$logging = true; + OAuthRequestLogger::$request_object = $request_object; + ob_start(); + + // Make sure we flush our log entry when we stop the request (eg on an exception) + register_shutdown_function(array('OAuthRequestLogger','flush')); + } + } + + + /** + * Force logging, needed for performing test connects independent from the debugging setting. + * + * @param boolean store_log (optional) true to store the log in the db + */ + static function enableLogging ( $store_log = null ) + { + OAuthRequestLogger::$enable_logging = true; + if (!is_null($store_log)) + { + OAuthRequestLogger::$store_log = $store_log; + } + } + + + /** + * Logs the request to the database, sends any cached output. + * Also called on shutdown, to make sure we always log the request being handled. + */ + static function flush () + { + if (OAuthRequestLogger::$logging) + { + OAuthRequestLogger::$logging = false; + + if (is_null(OAuthRequestLogger::$sent)) + { + // What has been sent to the user-agent? + $data = ob_get_contents(); + if (strlen($data) > 0) + { + ob_end_flush(); + } + elseif (ob_get_level()) + { + ob_end_clean(); + } + $hs = headers_list(); + $sent = implode("\n", $hs) . "\n\n" . $data; + } + else + { + // The request we sent + $sent = OAuthRequestLogger::$sent; + } + + if (is_null(OAuthRequestLogger::$received)) + { + // Build the request we received + $hs0 = getallheaders(); + $hs = array(); + foreach ($hs0 as $h => $v) + { + $hs[] = "$h: $v"; + } + + $data = ''; + $fh = @fopen('php://input', 'r'); + if ($fh) + { + while (!feof($fh)) + { + $s = fread($fh, 1024); + if (is_string($s)) + { + $data .= $s; + } + } + fclose($fh); + } + $received = implode("\n", $hs) . "\n\n" . $data; + } + else + { + // The answer we received + $received = OAuthRequestLogger::$received; + } + + // The request base string + if (OAuthRequestLogger::$request_object) + { + $base_string = OAuthRequestLogger::$request_object->signatureBaseString(); + } + else + { + $base_string = ''; + } + + // Figure out to what keys we want to log this request + $keys = array(); + if (OAuthRequestLogger::$request_object) + { + $consumer_key = OAuthRequestLogger::$request_object->getParam('oauth_consumer_key', true); + $token = OAuthRequestLogger::$request_object->getParam('oauth_token', true); + + switch (get_class(OAuthRequestLogger::$request_object)) + { + // tokens are access/request tokens by a consumer + case 'OAuthServer': + case 'OAuthRequestVerifier': + $keys['ocr_consumer_key'] = $consumer_key; + $keys['oct_token'] = $token; + break; + + // tokens are access/request tokens to a server + case 'OAuthRequester': + case 'OAuthRequestSigner': + $keys['osr_consumer_key'] = $consumer_key; + $keys['ost_token'] = $token; + break; + } + } + + // Log the request + if (OAuthRequestLogger::$store_log) + { + $store = elggconnect_get_oauth_store();//OAuthStore::instance(); + $store->addLog($keys, $received, $sent, $base_string, OAuthRequestLogger::$note, OAuthRequestLogger::$user_id); + } + + OAuthRequestLogger::$log[] = array( + 'keys' => $keys, + 'received' => $received, + 'sent' => $sent, + 'base_string' => $base_string, + 'note' => OAuthRequestLogger::$note + ); + } + } + + + /** + * Add a note, used by the OAuthException to log all exceptions. + * + * @param string note + */ + static function addNote ( $note ) + { + OAuthRequestLogger::$note .= $note . "\n\n"; + } + + /** + * Set the OAuth request object being used + * + * @param OAuthRequest request_object + */ + static function setRequestObject ( $request_object ) + { + OAuthRequestLogger::$request_object = $request_object; + } + + + /** + * Set the relevant user (defaults to the current user) + * + * @param int user_id + */ + static function setUser ( $user_id ) + { + OAuthRequestLogger::$user_id = $user_id; + } + + + /** + * Set the request we sent + * + * @param string request + */ + static function setSent ( $request ) + { + OAuthRequestLogger::$sent = $request; + } + + /** + * Set the reply we received + * + * @param string request + */ + static function setReceived ( $reply ) + { + OAuthRequestLogger::$received = $reply; + } + + + /** + * Get the the log till now + * + * @return array + */ + static function getLog () + { + return OAuthRequestLogger::$log; + } +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/OAuthRequestSigner.php b/mod/oauth_lib/vendors/oauth/library/OAuthRequestSigner.php new file mode 100644 index 000000000..a33d14034 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/OAuthRequestSigner.php @@ -0,0 +1,209 @@ + + * @date Nov 16, 2007 4:02:49 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__) . '/OAuthStore.php'; +require_once dirname(__FILE__) . '/OAuthRequest.php'; + + +class OAuthRequestSigner extends OAuthRequest +{ + protected $request; + protected $store; + protected $usr_id = 0; + private $signed = false; + + + /** + * Construct the request to be signed. Parses or appends the parameters in the params url. + * When you supply an params array, then the params should not be urlencoded. + * When you supply a string, then it is assumed it is of the type application/x-www-form-urlencoded + * + * @param string request url + * @param string method PUT, GET, POST etc. + * @param mixed params string (for urlencoded data, or array with name/value pairs) + * @param string body optional body for PUT and/or POST requests + */ + function __construct ( $request, $method = 'GET', $params = null, $body = null ) + { + $this->store = elggconnect_get_oauth_store();//OAuthStore::instance(); + + if (is_string($params)) + { + parent::__construct($request, $method, $params); + } + else + { + parent::__construct($request, $method); + if (is_array($params)) + { + foreach ($params as $name => $value) + { + $this->setParam($name, $value); + } + } + } + + // With put/ post we might have a body (not for application/x-www-form-urlencoded requests) + if ($method == 'PUT' || $method == 'POST') + { + $this->setBody($body); + } + } + + + /** + * Reset the 'signed' flag, so that any changes in the parameters force a recalculation + * of the signature. + */ + function setUnsigned () + { + $this->signed = false; + } + + + /** + * Sign our message in the way the server understands. + * Set the needed oauth_xxxx parameters. + * + * @param int usr_id (optional) user that wants to sign this request + * @param array secrets secrets used for signing, when empty then secrets will be fetched from the token registry + * @param string name name of the token to be used for signing + * @exception OAuthException when there is no oauth relation with the server + * @exception OAuthException when we don't support the signing methods of the server + */ + function sign ( $usr_id = 0, $secrets = null, $name = '' ) + { + $url = $this->getRequestUrl(); + if (empty($secrets)) + { + // get the access tokens for the site (on an user by user basis) + $secrets = $this->store->getSecretsForSignature($url, $usr_id, $name); + } + if (empty($secrets)) + { + throw new OAuthException('No OAuth relation with the server for at "'.$url.'"'); + } + + $signature_method = $this->selectSignatureMethod($secrets['signature_methods']); + + $token = isset($secrets['token']) ? $secrets['token'] : ''; + $token_secret = isset($secrets['token_secret']) ? $secrets['token_secret'] : ''; + + $this->setParam('oauth_signature_method',$signature_method); + $this->setParam('oauth_signature', ''); + $this->setParam('oauth_nonce', !empty($secrets['nonce']) ? $secrets['nonce'] : uniqid('')); + $this->setParam('oauth_timestamp', !empty($secrets['timestamp']) ? $secrets['timestamp'] : time()); + $this->setParam('oauth_token', $token); + $this->setParam('oauth_consumer_key', $secrets['consumer_key']); + $this->setParam('oauth_version', '1.0'); + + $body = $this->getBody(); + if (!is_null($body)) + { + // We also need to sign the body, use the default signature method + $body_signature = $this->calculateDataSignature($body, $secrets['consumer_secret'], $token_secret, $signature_method); + $this->setParam('xoauth_body_signature', $body_signature, true); + } + + $signature = $this->calculateSignature($secrets['consumer_secret'], $token_secret); + $this->setParam('oauth_signature', $signature, true); + + $this->signed = true; + $this->usr_id = $usr_id; + } + + + /** + * Builds the Authorization header for the request. + * Adds all oauth_ and xoauth_ parameters to the Authorization header. + * + * @return string + */ + function getAuthorizationHeader () + { + if (!$this->signed) + { + $this->sign($this->usr_id); + } + $h = array(); + $h[] = 'Authorization: OAuth realm=""'; + foreach ($this->param as $name => $value) + { + if (strncmp($name, 'oauth_', 6) == 0 || strncmp($name, 'xoauth_', 7) == 0) + { + $h[] = $name.'="'.$value.'"'; + } + } + $hs = implode(', ', $h); + return $hs; + } + + + /** + * Builds the application/x-www-form-urlencoded parameter string. Can be appended as + * the query part to a GET or inside the request body for a POST. + * + * @param boolean oauth_as_header (optional) set to false to include oauth parameters + * @return string + */ + function getQueryString ( $oauth_as_header = true ) + { + $parms = array(); + foreach ($this->param as $name => $value) + { + if ( !$oauth_as_header + || (strncmp($name, 'oauth_', 6) != 0 && strncmp($name, 'xoauth_', 7) != 0)) + { + if (is_array($value)) + { + foreach ($value as $v) + { + $parms[] = $name.'='.$v; + } + } + else + { + $parms[] = $name.'='.$value; + } + } + } + return implode('&', $parms); + } + +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/OAuthRequestVerifier.php b/mod/oauth_lib/vendors/oauth/library/OAuthRequestVerifier.php new file mode 100644 index 000000000..5b346b369 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/OAuthRequestVerifier.php @@ -0,0 +1,262 @@ + + * @date Nov 16, 2007 4:35:03 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthStore.php'; +require_once dirname(__FILE__) . '/OAuthRequest.php'; + + +class OAuthRequestVerifier extends OAuthRequest +{ + private $request; + private $store; + + /** + * Construct the request to be verified + * + * @param string request + * @param string method + */ + function __construct ( $uri = null, $method = 'GET' ) + { + $this->store = elggconnect_get_oauth_store();//OAuthStore::instance(); + parent::__construct($uri, $method); + + OAuthRequestLogger::start($this); + } + + + /** + * See if the current request is signed with OAuth + * + * @return boolean + */ + static public function requestIsSigned () + { + if (isset($_REQUEST['oauth_signature'])) + { + $signed = true; + } + else + { + $hs = getallheaders(); + if (isset($hs['Authorization']) && strpos($hs['Authorization'], 'oauth_signature') !== false) + { + $signed = true; + } + else + { + $signed = false; + } + } + return $signed; + } + + + /** + * Verify the request if it seemed to be signed. + * + * @param string token_type the kind of token needed, defaults to 'access' + * @exception OAuthException thrown when the request did not verify + * @return boolean true when signed, false when not signed + */ + public function verifyIfSigned ( $token_type = 'access' ) + { + if ($this->getParam('oauth_consumer_key')) + { + OAuthRequestLogger::start($this); + $this->verify($token_type); + $signed = true; + OAuthRequestLogger::flush(); + } + else + { + $signed = false; + } + return $signed; + } + + + /** + * Verify the request + * + * @param string token_type the kind of token needed, defaults to 'access' (false, 'access', 'request') + * @exception OAuthException thrown when the request did not verify + * @return int user_id associated with token (false when no user associated) + */ + public function verify ( $token_type = 'access' ) + { + $consumer_key = $this->getParam('oauth_consumer_key'); + $token = $this->getParam('oauth_token'); + $user_id = false; + + if ($consumer_key && ($token_type === false || $token)) + { + $secrets = $this->store->getSecretsForVerify( $this->urldecode($consumer_key), + $this->urldecode($token), + $token_type); + + $this->store->checkServerNonce( $this->urldecode($consumer_key), + $this->urldecode($token), + $this->getParam('oauth_timestamp', true), + $this->getParam('oauth_nonce', true)); + + $oauth_sig = $this->getParam('oauth_signature'); + if (empty($oauth_sig)) + { + throw new OAuthException('Verification of signature failed (no oauth_signature in request).'); + } + + try + { + $this->verifySignature($secrets['consumer_secret'], $secrets['token_secret'], $token_type); + } + catch (OAuthException $e) + { + throw new OAuthException('Verification of signature failed (signature base string was "'.$this->signatureBaseString().'").'); + } + + // Check the optional body signature + if ($this->getParam('xoauth_body_signature')) + { + $method = $this->getParam('xoauth_body_signature_method'); + if (empty($method)) + { + $method = $this->getParam('oauth_signature_method'); + } + + try + { + $this->verifyDataSignature($this->getBody(), $secrets['consumer_secret'], $secrets['token_secret'], $method, $this->getParam('xoauth_body_signature')); + } + catch (OAuthException $e) + { + throw new OAuthException('Verification of body signature failed.'); + } + } + + // All ok - fetch the user associated with this request + if (isset($secrets['user_id'])) + { + $user_id = $secrets['user_id']; + } + + // Check if the consumer wants us to reset the ttl of this token + $ttl = $this->getParam('xoauth_token_ttl', true); + if (is_numeric($ttl)) + { + $this->store->setConsumerAccessTokenTtl($this->urldecode($token), $ttl); + } + } + else + { + throw new OAuthException('Can\'t verify request, missing oauth_consumer_key or oauth_token'); + } + return $user_id; + } + + + + /** + * Verify the signature of the request, using the method in oauth_signature_method. + * The signature is returned encoded in the form as used in the url. So the base64 and + * urlencoding has been done. + * + * @param string consumer_secret + * @param string token_secret + * @exception OAuthException thrown when the signature method is unknown + * @exception OAuthException when not all parts available + * @exception OAuthException when signature does not match + */ + public function verifySignature ( $consumer_secret, $token_secret, $token_type = 'access' ) + { + $required = array( + 'oauth_consumer_key', + 'oauth_signature_method', + 'oauth_timestamp', + 'oauth_nonce', + 'oauth_signature' + ); + + if ($token_type !== false) + { + $required[] = 'oauth_token'; + } + + foreach ($required as $req) + { + if (!isset($this->param[$req])) + { + throw new OAuthException('Can\'t verify request signature, missing parameter "'.$req.'"'); + } + } + + $this->checks(); + + $base = $this->signatureBaseString(); + $this->verifyDataSignature($base, $consumer_secret, $token_secret, $this->param['oauth_signature_method'], $this->param['oauth_signature']); + } + + + + /** + * Verify the signature of a string. + * + * @param string data + * @param string consumer_secret + * @param string token_secret + * @param string signature_method + * @param string signature + * @exception OAuthException thrown when the signature method is unknown + * @exception OAuthException when signature does not match + */ + public function verifyDataSignature ( $data, $consumer_secret, $token_secret, $signature_method, $signature ) + { + if (is_null($data)) + { + $data = ''; + } + + $sig = $this->getSignatureMethod($signature_method); + if (!$sig->verify($this, $data, $consumer_secret, $token_secret, $signature)) + { + throw new OAuthException('Signature verification failed ('.$signature_method.')'); + } + } + +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/OAuthRequester.php b/mod/oauth_lib/vendors/oauth/library/OAuthRequester.php new file mode 100644 index 000000000..c6f56178e --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/OAuthRequester.php @@ -0,0 +1,508 @@ + + * @date Nov 20, 2007 1:41:38 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthRequestSigner.php'; +require_once dirname(__FILE__) . '/body/OAuthBodyContentDisposition.php'; + + +class OAuthRequester extends OAuthRequestSigner +{ + protected $files; + + /** + * Construct a new request signer. Perform the request with the doRequest() method below. + * + * A request can have either one file or a body, not both. + * + * The files array consists of arrays: + * - file the filename/path containing the data for the POST/PUT + * - data data for the file, omit when you have a file + * - mime content-type of the file + * - filename filename for content disposition header + * + * When OAuth (and PHP) can support multipart/form-data then we can handle more than one file. + * For now max one file, with all the params encoded in the query string. + * + * @param string request + * @param string method http method. GET, PUT, POST etc. + * @param array params name=>value array with request parameters + * @param string body optional body to send + * @param array files optional files to send (max 1 till OAuth support multipart/form-data posts) + */ + function __construct ( $request, $method = 'GET', $params = null, $body = null, $files = null ) + { + parent::__construct($request, $method, $params, $body); + + // When there are files, then we can construct a POST with a single file + if (!empty($files)) + { + $empty = true; + foreach ($files as $f) + { + $empty = $empty && empty($f['file']) && !isset($f['data']); + } + + if (!$empty) + { + if (!is_null($body)) + { + throw new OAuthException('When sending files, you can\'t send a body as well.'); + } + $this->files = $files; + } + } + } + + + /** + * Perform the request, returns the response code, headers and body. + * + * @param int usr_id optional user id for which we make the request + * @param array curl_options optional extra options for curl request + * @param array options options like name and token_ttl + * @exception OAuthException when authentication not accepted + * @exception OAuthException when signing was not possible + * @return array (code=>int, headers=>array(), body=>string) + */ + function doRequest ( $usr_id = 0, $curl_options = array(), $options = array() ) + { + $name = isset($options['name']) ? $options['name'] : ''; + if (isset($options['token_ttl'])) + { + $this->setParam('xoauth_token_ttl', intval($options['token_ttl'])); + } + + if (!empty($this->files)) + { + // At the moment OAuth does not support multipart/form-data, so try to encode + // the supplied file (or data) as the request body and add a content-disposition header. + list($extra_headers, $body) = OAuthBodyContentDisposition::encodeBody($this->files); + $this->setBody($body); + $curl_options = $this->prepareCurlOptions($curl_options, $extra_headers); + } + $this->sign($usr_id, null, $name); + $text = $this->curl_raw($curl_options); + $result = $this->curl_parse($text); + if ($result['code'] >= 400) + { + throw new OAuthException('Request failed with code ' . $result['code'] . ': ' . $result['body']); + } + + // Record the token time to live for this server access token, immediate delete iff ttl <= 0 + // Only done on a succesful request. + $token_ttl = $this->getParam('xoauth_token_ttl', false); + if (is_numeric($token_ttl)) + { + $this->store->setServerTokenTtl($this->getParam('oauth_consumer_key',true), $this->getParam('oauth_token',true), $token_ttl); + } + + return $result; + } + + + /** + * Request a request token from the site belonging to consumer_key + * + * @param string consumer_key + * @param int usr_id + * @param array params (optional) extra arguments for when requesting the request token + * @param string method (optional) change the method of the request, defaults to POST (as it should be) + * @param array options (optional) options like name and token_ttl + * @exception OAuthException when no key could be fetched + * @exception OAuthException when no server with consumer_key registered + * @return array (authorize_uri, token) + */ + static function requestRequestToken ( $consumer_key, $usr_id, $params = null, $method = 'POST', $options = array() ) + { + OAuthRequestLogger::start(); + + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $params['xoauth_token_ttl'] = intval($options['token_ttl']); + } + + $store = elggconnect_get_oauth_store();//OAuthStore::instance(); + $r = $store->getServer($consumer_key, $usr_id); + $uri = $r['request_token_uri']; + + $oauth = new OAuthRequester($uri, $method, $params); + $oauth->sign($usr_id, $r); + $text = $oauth->curl_raw(); + + if (empty($text)) + { + throw new OAuthException('No answer from the server "'.$uri.'" while requesting a request token'); + } + $data = $oauth->curl_parse($text); + if ($data['code'] != 200) + { + throw new OAuthException('Unexpected result from the server "'.$uri.'" ('.$data['code'].') while requesting a request token'); + } + $token = array(); + $params = explode('&', $data['body']); + foreach ($params as $p) + { + @list($name, $value) = explode('=', $p, 2); + $token[$name] = $oauth->urldecode($value); + } + + if (!empty($token['oauth_token']) && !empty($token['oauth_token_secret'])) + { + $opts = array(); + if (isset($options['name'])) + { + $opts['name'] = $options['name']; + } + if (isset($token['xoauth_token_ttl'])) + { + $opts['token_ttl'] = $token['xoauth_token_ttl']; + } + $store->addServerToken($consumer_key, 'request', $token['oauth_token'], $token['oauth_token_secret'], $usr_id, $opts); + } + else + { + throw new OAuthException('The server "'.$uri.'" did not return the oauth_token or the oauth_token_secret'); + } + + OAuthRequestLogger::flush(); + + // Now we can direct a browser to the authorize_uri + return array( + 'authorize_uri' => $r['authorize_uri'], + 'token' => $token['oauth_token'] + ); + } + + + /** + * Request an access token from the site belonging to consumer_key. + * Before this we got an request token, now we want to exchange it for + * an access token. + * + * @param string consumer_key + * @param string token + * @param int usr_id user requesting the access token + * @param string method (optional) change the method of the request, defaults to POST (as it should be) + * @param array options (optional) extra options for request, eg token_ttl + * @exception OAuthException when no key could be fetched + * @exception OAuthException when no server with consumer_key registered + */ + static function requestAccessToken ( $consumer_key, $token, $usr_id, $method = 'POST', $options = array() ) + { + OAuthRequestLogger::start(); + + $store = elggconnect_get_oauth_store();//OAuthStore::instance(); + $r = $store->getServerTokenSecrets($consumer_key, $token, 'request', $usr_id); + $uri = $r['access_token_uri']; + $token_name = $r['token_name']; + + // Delete the server request token, this one was for one use only + $store->deleteServerToken($consumer_key, $r['token'], 0, true); + + // Try to exchange our request token for an access token + $oauth = new OAuthRequester($uri, $method); + + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $oauth->setParam('xoauth_token_ttl', intval($options['token_ttl'])); + } + + OAuthRequestLogger::setRequestObject($oauth); + + $oauth->sign($usr_id, $r); + $text = $oauth->curl_raw(); + if (empty($text)) + { + throw new OAuthException('No answer from the server "'.$uri.'" while requesting a request token'); + } + $data = $oauth->curl_parse($text); + + if ($data['code'] != 200) + { + throw new OAuthException('Unexpected result from the server "'.$uri.'" ('.$data['code'].') while requesting a request token'); + } + + $token = array(); + $params = explode('&', $data['body']); + foreach ($params as $p) + { + @list($name, $value) = explode('=', $p, 2); + $token[$oauth->urldecode($name)] = $oauth->urldecode($value); + } + + if (!empty($token['oauth_token']) && !empty($token['oauth_token_secret'])) + { + $opts = array(); + $opts['name'] = $token_name; + if (isset($token['xoauth_token_ttl'])) + { + $opts['token_ttl'] = $token['xoauth_token_ttl']; + } + $store->addServerToken($consumer_key, 'access', $token['oauth_token'], $token['oauth_token_secret'], $usr_id, $opts); + } + else + { + throw new OAuthException('The server "'.$uri.'" did not return the oauth_token or the oauth_token_secret'); + } + + OAuthRequestLogger::flush(); + } + + + + /** + * Open and close a curl session passing all the options to the curl libs + * + * @param string url the http address to fetch + * @exception OAuthException when temporary file for PUT operation could not be created + * @return string the result of the curl action + */ + protected function curl_raw ( $opts = array() ) + { + if (isset($opts[CURLOPT_HTTPHEADER])) + { + $header = $opts[CURLOPT_HTTPHEADER]; + } + else + { + $header = array(); + } + + $ch = curl_init(); + $method = $this->getMethod(); + $url = $this->getRequestUrl(); + $header[] = $this->getAuthorizationHeader(); + $query = $this->getQueryString(); + $body = $this->getBody(); + + $has_content_type = false; + foreach ($header as $h) + { + if (strncasecmp($h, 'Content-Type:', 13) == 0) + { + $has_content_type = true; + } + } + + if (!is_null($body)) + { + if ($method == 'TRACE') + { + throw new OAuthException('A body can not be sent with a TRACE operation'); + } + + // PUT and POST allow a request body + if (!empty($query)) + { + $url .= '?'.$query; + } + + // Make sure that the content type of the request is ok + if (!$has_content_type) + { + $header[] = 'Content-Type: application/octet-stream'; + $has_content_type = true; + } + + // When PUTting, we need to use an intermediate file (because of the curl implementation) + if ($method == 'PUT') + { + /* + if (version_compare(phpversion(), '5.2.0') >= 0) + { + // Use the data wrapper to create the file expected by the put method + $put_file = fopen('data://application/octet-stream;base64,'.base64_encode($body)); + } + */ + + $put_file = @tmpfile(); + if (!$put_file) + { + throw new OAuthException('Could not create tmpfile for PUT operation'); + } + fwrite($put_file, $body); + fseek($put_file, 0); + + curl_setopt($ch, CURLOPT_PUT, true); + curl_setopt($ch, CURLOPT_INFILE, $put_file); + curl_setopt($ch, CURLOPT_INFILESIZE, strlen($body)); + } + else + { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + } + } + else + { + // a 'normal' request, no body to be send + if ($method == 'POST') + { + if (!$has_content_type) + { + $header[] = 'Content-Type: application/x-www-form-urlencoded'; + $has_content_type = true; + } + + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $query); + } + else + { + if (!empty($query)) + { + $url .= '?'.$query; + } + if ($method != 'GET') + { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + } + } + } + + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_USERAGENT, 'anyMeta/OAuth 1.0 - ($LastChangedRevision: 63 $)'); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, true); + + foreach ($opts as $k => $v) + { + if ($k != CURLOPT_HTTPHEADER) + { + curl_setopt($ch, $k, $v); + } + } + + $txt = curl_exec($ch); + curl_close($ch); + + if (!empty($put_file)) + { + fclose($put_file); + } + + // Tell the logger what we requested and what we received back + $data = $method . " $url\n".implode("\n",$header); + if (is_string($body)) + { + $data .= "\n\n".$body; + } + else if ($method == 'POST') + { + $data .= "\n\n".$query; + } + + OAuthRequestLogger::setSent($data, $body); + OAuthRequestLogger::setReceived($txt); + + return $txt; + } + + + /** + * Parse an http response + * + * @param string response the http text to parse + * @return array (code=>http-code, headers=>http-headers, body=>body) + */ + protected function curl_parse ( $response ) + { + if (empty($response)) + { + return array(); + } + + @list($headers,$body) = explode("\r\n\r\n",$response,2); + $lines = explode("\r\n",$headers); + + if (preg_match('@^HTTP/[0-9]\.[0-9] +100@', $lines[0])) + { + /* HTTP/1.x 100 Continue + * the real data is on the next line + */ + @list($headers,$body) = explode("\r\n\r\n",$body,2); + $lines = explode("\r\n",$headers); + } + + // first line of headers is the HTTP response code + $http_line = array_shift($lines); + if (preg_match('@^HTTP/[0-9]\.[0-9] +([0-9]{3})@', $http_line, $matches)) + { + $code = $matches[1]; + } + + // put the rest of the headers in an array + $headers = array(); + foreach ($lines as $l) + { + list($k, $v) = explode(': ', $l, 2); + $headers[strtolower($k)] = $v; + } + + return array( 'code' => $code, 'headers' => $headers, 'body' => $body); + } + + + /** + * Mix the given headers into the headers that were given to curl + * + * @param array curl_options + * @param array extra_headers + * @return array new curl options + */ + protected function prepareCurlOptions ( $curl_options, $extra_headers ) + { + $hs = array(); + if (!empty($curl_options[CURLOPT_HTTPHEADER]) && is_array($curl_options[CURLOPT_HTTPHEADER])) + { + foreach ($curl_options[CURLOPT_HTTPHEADER] as $h) + { + list($opt, $val) = explode(':', $h, 2); + $opt = str_replace(' ', '-', ucwords(str_replace('-', ' ', $opt))); + $hs[$opt] = $val; + } + } + + $curl_options[CURLOPT_HTTPHEADER] = array(); + $hs = array_merge($hs, $extra_headers); + foreach ($hs as $h => $v) + { + $curl_options[CURLOPT_HTTPHEADER][] = "$h: $v"; + } + return $curl_options; + } +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/OAuthServer.php b/mod/oauth_lib/vendors/oauth/library/OAuthServer.php new file mode 100644 index 000000000..d86cc5f14 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/OAuthServer.php @@ -0,0 +1,232 @@ + + * @date Nov 27, 2007 12:36:38 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once 'OAuthRequestVerifier.php'; + +class OAuthServer extends OAuthRequestVerifier +{ + /** + * Handle the request_token request. + * Returns the new request token and request token secret. + * + * TODO: add correct result code to exception + * + * @return string returned request token, false on an error + */ + public function requestToken () + { + OAuthRequestLogger::start($this); + try + { + $this->verify(false); + + $options = array(); + $ttl = $this->getParam('xoauth_token_ttl', false); + if ($ttl) + { + $options['token_ttl'] = $ttl; + } + + // Create a request token + $store = elggconnect_get_oauth_store();//OAuthStore::instance(); + $token = $store->addConsumerRequestToken($this->getParam('oauth_consumer_key', true), $options); + $result = 'oauth_token='.$this->urlencode($token['token']) + .'&oauth_token_secret='.$this->urlencode($token['token_secret']); + + if (!empty($token['token_ttl'])) + { + $result .= '&xoauth_token_ttl='.$this->urlencode($token['token_ttl']); + } + + $request_token = $token['token']; + + header('HTTP/1.1 200 OK'); + header('Content-Length: '.strlen($result)); + header('Content-Type: application/x-www-form-urlencoded'); + + echo $result; + } + catch (OAuthException $e) + { + $request_token = false; + + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/plain'); + + echo "OAuth Verification Failed: " . $e->getMessage(); + } + + OAuthRequestLogger::flush(); + return $request_token; + } + + + /** + * Verify the start of an authorization request. Verifies if the request token is valid. + * Next step is the method authorizeFinish() + * + * Nota bene: this stores the current token, consumer key and callback in the _SESSION + * + * @exception OAuthException thrown when not a valid request + * @return array token description + */ + public function authorizeVerify ( ) + { + OAuthRequestLogger::start($this); + + $store = elggconnect_get_oauth_store();//OAuthStore::instance(); + $token = $this->getParam('oauth_token', true); + $rs = $store->getConsumerRequestToken($token); + if (empty($rs)) + { + throw new OAuthException('Unknown request token "'.$token.'"'); + } + + // We need to remember the callback + if ( empty($_SESSION['verify_oauth_token']) + || strcmp($_SESSION['verify_oauth_token'], $rs['token'])) + { + $_SESSION['verify_oauth_token'] = $rs['token']; + $_SESSION['verify_oauth_consumer_key'] = $rs['consumer_key']; + $_SESSION['verify_oauth_callback'] = $this->getParam('oauth_callback', true); + } + OAuthRequestLogger::flush(); + return $rs; + } + + + /** + * Overrule this method when you want to display a nice page when + * the authorization is finished. This function does not know if the authorization was + * succesfull, you need to check the token in the database. + * + * @param boolean authorized if the current token (oauth_token param) is authorized or not + * @param int user_id user for which the token was authorized (or denied) + */ + public function authorizeFinish ( $authorized, $user_id ) + { + OAuthRequestLogger::start($this); + + $token = $this->getParam('oauth_token', true); + if ( isset($_SESSION['verify_oauth_token']) + && $_SESSION['verify_oauth_token'] == $token) + { + // Flag the token as authorized, or remove the token when not authorized + $store = elggconnect_get_oauth_store();//OAuthStore::instance(); + + // Fetch the referrer host from the oauth callback parameter + $referrer_host = ''; + $oauth_callback = false; + if (!empty($_SESSION['verify_oauth_callback'])) + { + $oauth_callback = $_SESSION['verify_oauth_callback']; + $ps = parse_url($oauth_callback); + if (isset($ps['host'])) + { + $referrer_host = $ps['host']; + } + } + + if ($authorized) + { + OAuthRequestLogger::addNote('Authorized token "'.$token.'" for user '.$user_id.' with referrer "'.$referrer_host.'"'); + $store->authorizeConsumerRequestToken($token, $user_id, $referrer_host); + } + else + { + OAuthRequestLogger::addNote('Authorization rejected for token "'.$token.'" for user '.$user_id."\nToken has been deleted"); + $store->deleteConsumerRequestToken($token); + } + + if (!empty($oauth_callback)) + { + $this->redirect($oauth_callback, array('oauth_token'=>rawurlencode($token))); + } + } + OAuthRequestLogger::flush(); + } + + + /** + * Exchange a request token for an access token. + * The exchange is only succesful iff the request token has been authorized. + * + * Never returns, calls exit() when token is exchanged or when error is returned. + */ + public function accessToken () + { + OAuthRequestLogger::start($this); + + try + { + $this->verify('request'); + + $options = array(); + $ttl = $this->getParam('xoauth_token_ttl', false); + if ($ttl) + { + $options['token_ttl'] = $ttl; + } + + $store = elggconnect_get_oauth_store();//OAuthStore::instance(); + $token = $store->exchangeConsumerRequestForAccessToken($this->getParam('oauth_token', true), $options); + $result = 'oauth_token='.$this->urlencode($token['token']) + .'&oauth_token_secret='.$this->urlencode($token['token_secret']); + + if (!empty($token['token_ttl'])) + { + $result .= '&xoauth_token_ttl='.$this->urlencode($token['token_ttl']); + } + + header('HTTP/1.1 200 OK'); + header('Content-Length: '.strlen($result)); + header('Content-Type: application/x-www-form-urlencoded'); + + echo $result; + } + catch (OAuthException $e) + { + header('HTTP/1.1 401 Access Denied'); + header('Content-Type: text/plain'); + + echo "OAuth Verification Failed: " . $e->getMessage(); + } + + OAuthRequestLogger::flush(); + exit(); + } +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/OAuthStore.php b/mod/oauth_lib/vendors/oauth/library/OAuthStore.php new file mode 100644 index 000000000..1841ab5fa --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/OAuthStore.php @@ -0,0 +1,86 @@ + + * @date Nov 16, 2007 4:03:30 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/OAuthException.php'; + +class OAuthStore +{ + static private $instance = false; + + /** + * Request an instance of the OAuthStore + */ + public static function instance ( $store = 'MySQL', $options = array() ) + { + if (!OAuthStore::$instance) + { + // Select the store you want to use + if (strpos($store, '/') === false) + { + $class = 'OAuthStore'.$store; + $file = dirname(__FILE__) . '/store/'.$class.'.php'; + } + else + { + $file = $store; + $store = basename($file, '.php'); + $class = $store; + } + + if (is_file($file)) + { + require_once $file; + + if (class_exists($class)) + { + OAuthStore::$instance = new $class($options); + } + else + { + throw new OAuthException('Could not find class '.$class.' in file '.$file); + } + } + else + { + throw new OAuthException('No OAuthStore for '.$store.' (file '.$file.')'); + } + } + return OAuthStore::$instance; + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/body/OAuthBodyContentDisposition.php b/mod/oauth_lib/vendors/oauth/library/body/OAuthBodyContentDisposition.php new file mode 100644 index 000000000..84123b6d0 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/body/OAuthBodyContentDisposition.php @@ -0,0 +1,129 @@ + + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +class OAuthBodyContentDisposition +{ + /** + * Builds the request string. + * + * The files array can be a combination of the following (either data or file): + * + * file => "path/to/file", filename=, mime=, data= + * + * @param array files (name => filedesc) (not urlencoded) + * @return array (headers, body) + */ + static function encodeBody ( $files ) + { + $headers = array(); + $body = null; + + // 1. Add all the files to the post + if (!empty($files)) + { + foreach ($files as $name => $f) + { + $data = false; + $filename = false; + + if (isset($f['filename'])) + { + $filename = $f['filename']; + } + + if (!empty($f['file'])) + { + $data = @file_get_contents($f['file']); + if ($data === false) + { + throw new OAuthException(sprintf('Could not read the file "%s" for request body', $f['file'])); + } + if (empty($filename)) + { + $filename = basename($f['file']); + } + } + else if (isset($f['data'])) + { + $data = $f['data']; + } + + // When there is data, add it as a request body, otherwise silently skip the upload + if ($data !== false) + { + if (isset($headers['Content-Disposition'])) + { + throw new OAuthException('Only a single file (or data) allowed in a signed PUT/POST request body.'); + } + + if (empty($filename)) + { + $filename = 'untitled'; + } + $mime = !empty($f['mime']) ? $f['mime'] : 'application/octet-stream'; + + $headers['Content-Disposition'] = 'attachment; filename="'.OAuthBodyContentDisposition::encodeParameterName($filename).'"'; + $headers['Content-Type'] = $mime; + + $body = $data; + } + + } + + // When we have a body, add the content-length + if (!is_null($body)) + { + $headers['Content-Length'] = strlen($body); + } + } + return array($headers, $body); + } + + + /** + * Encode a parameter's name for use in a multipart header. + * For now we do a simple filter that removes some unwanted characters. + * We might want to implement RFC1522 here. See http://tools.ietf.org/html/rfc1522 + * + * @param string name + * @return string + */ + static function encodeParameterName ( $name ) + { + return preg_replace('/[^\x20-\x7f]|"/', '-', $name); + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php b/mod/oauth_lib/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php new file mode 100644 index 000000000..048fdeb63 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php @@ -0,0 +1,143 @@ + + * @date Jan 31, 2008 12:50:05 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +class OAuthBodyMultipartFormdata +{ + /** + * Builds the request string. + * + * The files array can be a combination of the following (either data or file): + * + * file => "path/to/file", filename=, mime=, data= + * + * @param array params (name => value) (all names and values should be urlencoded) + * @param array files (name => filedesc) (not urlencoded) + * @return array (headers, body) + */ + static function encodeBody ( $params, $files ) + { + $headers = array(); + $body = ''; + $boundary = 'OAuthRequester_'.md5(uniqid('multipart') . microtime()); + $headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary; + + + // 1. Add the parameters to the post + if (!empty($params)) + { + foreach ($params as $name => $value) + { + $body .= '--'.$boundary."\r\n"; + $body .= 'Content-Disposition: form-data; name="'.OAuthBodyMultipartFormdata::encodeParameterName(rawurldecode($name)).'"'; + $body .= "\r\n\r\n"; + $body .= urldecode($value); + $body .= "\r\n"; + } + } + + // 2. Add all the files to the post + if (!empty($files)) + { + $untitled = 1; + + foreach ($files as $name => $f) + { + $data = false; + $filename = false; + + if (isset($f['filename'])) + { + $filename = $f['filename']; + } + + if (!empty($f['file'])) + { + $data = @file_get_contents($f['file']); + if ($data === false) + { + throw new OAuthException(sprintf('Could not read the file "%s" for form-data part', $f['file'])); + } + if (empty($filename)) + { + $filename = basename($f['file']); + } + } + else if (isset($f['data'])) + { + $data = $f['data']; + } + + // When there is data, add it as a form-data part, otherwise silently skip the upload + if ($data !== false) + { + if (empty($filename)) + { + $filename = sprintf('untitled-%d', $untitled++); + } + $mime = !empty($f['mime']) ? $f['mime'] : 'application/octet-stream'; + $body .= '--'.$boundary."\r\n"; + $body .= 'Content-Disposition: form-data; name="'.OAuthBodyMultipartFormdata::encodeParameterName($name).'"; filename="'.OAuthBodyMultipartFormdata::encodeParameterName($filename).'"'."\r\n"; + $body .= 'Content-Type: '.$mime; + $body .= "\r\n\r\n"; + $body .= $data; + $body .= "\r\n"; + } + + } + } + $body .= '--'.$boundary."--\r\n"; + + $headers['Content-Length'] = strlen($body); + return array($headers, $body); + } + + + /** + * Encode a parameter's name for use in a multipart header. + * For now we do a simple filter that removes some unwanted characters. + * We might want to implement RFC1522 here. See http://tools.ietf.org/html/rfc1522 + * + * @param string name + * @return string + */ + static function encodeParameterName ( $name ) + { + return preg_replace('/[^\x20-\x7f]|"/', '-', $name); + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/discovery/xrds_parse.php b/mod/oauth_lib/vendors/oauth/library/discovery/xrds_parse.php new file mode 100644 index 000000000..c9cf94997 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/discovery/xrds_parse.php @@ -0,0 +1,304 @@ + + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* example of use: + +header('content-type: text/plain'); +$file = file_get_contents('../../test/discovery/xrds-magnolia.xrds'); +$xrds = xrds_parse($file); +print_r($xrds); + + */ + +/** + * Parse the xrds file in the argument. The xrds description must have been + * fetched via curl or something else. + * + * TODO: more robust checking, support for more service documents + * TODO: support for URIs to definition instead of local xml:id + * + * @param string data contents of xrds file + * @exception Exception when the file is in an unknown format + * @return array + */ +function xrds_parse ( $data ) +{ + $oauth = array(); + $doc = @DOMDocument::loadXML($data); + if ($doc === false) + { + throw new Exception('Error in XML, can\'t load XRDS document'); + } + + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('xrds', 'xri://$xrds'); + $xpath->registerNamespace('xrd', 'xri://$XRD*($v*2.0)'); + $xpath->registerNamespace('simple', 'http://xrds-simple.net/core/1.0'); + + // Yahoo! uses this namespace, with lowercase xrd in it + $xpath->registerNamespace('xrd2', 'xri://$xrd*($v*2.0)'); + + $uris = xrds_oauth_service_uris($xpath); + + foreach ($uris as $uri) + { + // TODO: support uris referring to service documents outside this one + if ($uri{0} == '#') + { + $id = substr($uri, 1); + $oauth = xrds_xrd_oauth($xpath, $id); + if (is_array($oauth) && !empty($oauth)) + { + return $oauth; + } + } + } + + return false; +} + + +/** + * Parse a XRD definition for OAuth and return the uris etc. + * + * @param XPath xpath + * @param string id + * @return array + */ +function xrds_xrd_oauth ( $xpath, $id ) +{ + $oauth = array(); + $xrd = $xpath->query('//xrds:XRDS/xrd:XRD[@xml:id="'.$id.'"]'); + if ($xrd->length == 0) + { + // Yahoo! uses another namespace + $xrd = $xpath->query('//xrds:XRDS/xrd2:XRD[@xml:id="'.$id.'"]'); + } + + if ($xrd->length >= 1) + { + $x = $xrd->item(0); + $services = array(); + foreach ($x->childNodes as $n) + { + switch ($n->nodeName) + { + case 'Type': + if ($n->nodeValue != 'xri://$xrds*simple') + { + // Not a simple XRDS document + return false; + } + break; + case 'Expires': + $oauth['expires'] = $n->nodeValue; + break; + case 'Service': + list($type,$service) = xrds_xrd_oauth_service($n); + if ($type) + { + $services[$type][xrds_priority($n)][] = $service; + } + break; + } + } + + // Flatten the services on priority + foreach ($services as $type => $service) + { + $oauth[$type] = xrds_priority_flatten($service); + } + } + else + { + $oauth = false; + } + return $oauth; +} + + +/** + * Parse a service definition for OAuth in a simple xrd element + * + * @param DOMElement n + * @return array (type, service desc) + */ +function xrds_xrd_oauth_service ( $n ) +{ + $service = array( + 'uri' => '', + 'signature_method' => array(), + 'parameters' => array() + ); + + $type = false; + foreach ($n->childNodes as $c) + { + $name = $c->nodeName; + $value = $c->nodeValue; + + if ($name == 'URI') + { + $service['uri'] = $value; + } + else if ($name == 'Type') + { + if (strncmp($value, 'http://oauth.net/core/1.0/endpoint/', 35) == 0) + { + $type = basename($value); + } + else if (strncmp($value, 'http://oauth.net/core/1.0/signature/', 36) == 0) + { + $service['signature_method'][] = basename($value); + } + else if (strncmp($value, 'http://oauth.net/core/1.0/parameters/', 37) == 0) + { + $service['parameters'][] = basename($value); + } + else if (strncmp($value, 'http://oauth.net/discovery/1.0/consumer-identity/', 49) == 0) + { + $type = 'consumer_identity'; + $service['method'] = basename($value); + unset($service['signature_method']); + unset($service['parameters']); + } + else + { + $service['unknown'][] = $value; + } + } + else if ($name == 'LocalID') + { + $service['consumer_key'] = $value; + } + else if ($name{0} != '#') + { + $service[strtolower($name)] = $value; + } + } + return array($type, $service); +} + + +/** + * Return the OAuth service uris in order of the priority. + * + * @param XPath xpath + * @return array + */ +function xrds_oauth_service_uris ( $xpath ) +{ + $uris = array(); + $xrd_oauth = $xpath->query('//xrds:XRDS/xrd:XRD/xrd:Service/xrd:Type[.=\'http://oauth.net/discovery/1.0\']'); + if ($xrd_oauth->length > 0) + { + $service = array(); + foreach ($xrd_oauth as $xo) + { + // Find the URI of the service definition + $cs = $xo->parentNode->childNodes; + foreach ($cs as $c) + { + if ($c->nodeName == 'URI') + { + $prio = xrds_priority($xo); + $service[$prio][] = $c->nodeValue; + } + } + } + $uris = xrds_priority_flatten($service); + } + return $uris; +} + + + +/** + * Flatten an array according to the priority + * + * @param array ps buckets per prio + * @return array one dimensional array + */ +function xrds_priority_flatten ( $ps ) +{ + $prio = array(); + $null = array(); + ksort($ps); + foreach ($ps as $idx => $bucket) + { + if (!empty($bucket)) + { + if ($idx == 'null') + { + $null = $bucket; + } + else + { + $prio = array_merge($prio, $bucket); + } + } + } + $prio = array_merge($prio, $bucket); + return $prio; +} + + +/** + * Fetch the priority of a element + * + * @param DOMElement elt + * @return mixed 'null' or int + */ +function xrds_priority ( $elt ) +{ + if ($elt->hasAttribute('priority')) + { + $prio = $elt->getAttribute('priority'); + if (is_numeric($prio)) + { + $prio = intval($prio); + } + } + else + { + $prio = 'null'; + } + return $prio; +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/discovery/xrds_parse.txt b/mod/oauth_lib/vendors/oauth/library/discovery/xrds_parse.txt new file mode 100644 index 000000000..fd867ea9f --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/discovery/xrds_parse.txt @@ -0,0 +1,101 @@ +The xrds_parse.php script contains the function: + + function xrds_parse ( $data. ) + +$data Contains the contents of a XRDS XML file. +When the data is invalid XML then this will throw an exception. + +After parsing a XRDS definition it will return a datastructure much like the one below. + +Array +( + [expires] => 2008-04-13T07:34:58Z + [request] => Array + ( + [0] => Array + ( + [uri] => https://ma.gnolia.com/oauth/get_request_token + [signature_method] => Array + ( + [0] => HMAC-SHA1 + [1] => RSA-SHA1 + [2] => PLAINTEXT + ) + + [parameters] => Array + ( + [0] => auth-header + [1] => post-body + [2] => uri-query + ) + ) + ) + + [authorize] => Array + ( + [0] => Array + ( + [uri] => http://ma.gnolia.com/oauth/authorize + [signature_method] => Array + ( + ) + + [parameters] => Array + ( + [0] => auth-header + [1] => uri-query + ) + ) + ) + + [access] => Array + ( + [0] => Array + ( + [uri] => https://ma.gnolia.com/oauth/get_access_token + [signature_method] => Array + ( + [0] => HMAC-SHA1 + [1] => RSA-SHA1 + [2] => PLAINTEXT + ) + + [parameters] => Array + ( + [0] => auth-header + [1] => post-body + [2] => uri-query + ) + ) + ) + + [resource] => Array + ( + [0] => Array + ( + [uri] => + [signature_method] => Array + ( + [0] => HMAC-SHA1 + [1] => RSA-SHA1 + ) + + [parameters] => Array + ( + [0] => auth-header + [1] => post-body + [2] => uri-query + ) + ) + ) + + [consumer_identity] => Array + ( + [0] => Array + ( + [uri] => http://ma.gnolia.com/applications/new + [method] => oob + ) + ) +) + diff --git a/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php b/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php new file mode 100644 index 000000000..34ccb428c --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php @@ -0,0 +1,69 @@ + + * @date Sep 8, 2008 12:04:35 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +abstract class OAuthSignatureMethod +{ + /** + * Return the name of this signature + * + * @return string + */ + abstract public function name(); + + /** + * Return the signature for the given request + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + abstract public function signature ( $request, $base_string, $consumer_secret, $token_secret ); + + /** + * Check if the request signature corresponds to the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string data to be signed, usually the base string, can be a request body + * @param string consumer_secret + * @param string token_secret + * @param string signature from the request, still urlencoded + * @return string + */ + abstract public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ); +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php b/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php new file mode 100644 index 000000000..4bc949c10 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php @@ -0,0 +1,115 @@ + + * @date Sep 8, 2008 12:21:19 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php'; + + +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod +{ + public function name () + { + return 'HMAC-SHA1'; + } + + + /** + * Calculate the signature using HMAC-SHA1 + * This function is copyright Andy Smith, 2007. + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + function signature ( $request, $base_string, $consumer_secret, $token_secret ) + { + $key = $request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret); + if (function_exists('hash_hmac')) + { + $signature = base64_encode(hash_hmac("sha1", $base_string, $key, true)); + } + else + { + $blocksize = 64; + $hashfunc = 'sha1'; + if (strlen($key) > $blocksize) + { + $key = pack('H*', $hashfunc($key)); + } + $key = str_pad($key,$blocksize,chr(0x00)); + $ipad = str_repeat(chr(0x36),$blocksize); + $opad = str_repeat(chr(0x5c),$blocksize); + $hmac = pack( + 'H*',$hashfunc( + ($key^$opad).pack( + 'H*',$hashfunc( + ($key^$ipad).$base_string + ) + ) + ) + ); + $signature = base64_encode($hmac); + } + return $request->urlencode($signature); + } + + + /** + * Check if the request signature corresponds to the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string data to be signed, usually the base string, can be a request body + * @param string consumer_secret + * @param string token_secret + * @param string signature from the request, still urlencoded + * @return string + */ + public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ) + { + $a = $request->urldecode($signature); + $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret)); + + // We have to compare the decoded values + $valA = base64_decode($a); + $valB = base64_decode($b); + + // Crude binary comparison + return rawurlencode($a) == rawurlencode($b); + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php b/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php new file mode 100644 index 000000000..6f593a47f --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php @@ -0,0 +1,95 @@ + + * @date Sep 8, 2008 12:09:43 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php'; + + +class OAuthSignatureMethod_MD5 extends OAuthSignatureMethod +{ + public function name () + { + return 'MD5'; + } + + + /** + * Calculate the signature using MD5 + * Binary md5 digest, as distinct from PHP's built-in hexdigest. + * This function is copyright Andy Smith, 2007. + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + function signature ( $request, $base_string, $consumer_secret, $token_secret ) + { + $s .= '&'.$request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret); + $md5 = md5($base_string); + $bin = ''; + + for ($i = 0; $i < strlen($md5); $i += 2) + { + $bin .= chr(hexdec($md5{$i+1}) + hexdec($md5{$i}) * 16); + } + return $request->urlencode(base64_encode($bin)); + } + + + /** + * Check if the request signature corresponds to the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string data to be signed, usually the base string, can be a request body + * @param string consumer_secret + * @param string token_secret + * @param string signature from the request, still urlencoded + * @return string + */ + public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ) + { + $a = $request->urldecode($signature); + $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret)); + + // We have to compare the decoded values + $valA = base64_decode($a); + $valB = base64_decode($b); + + // Crude binary comparison + return rawurlencode($a) == rawurlencode($b); + } +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php b/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php new file mode 100644 index 000000000..92ef30867 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php @@ -0,0 +1,80 @@ + + * @date Sep 8, 2008 12:09:43 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php'; + + +class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod +{ + public function name () + { + return 'PLAINTEXT'; + } + + + /** + * Calculate the signature using PLAINTEXT + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + function signature ( $request, $base_string, $consumer_secret, $token_secret ) + { + return $request->urlencode($request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret)); + } + + + /** + * Check if the request signature corresponds to the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string data to be signed, usually the base string, can be a request body + * @param string consumer_secret + * @param string token_secret + * @param string signature from the request, still urlencoded + * @return string + */ + public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ) + { + $a = $request->urldecode($signature); + $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret)); + + return $request->urldecode($a) == $request->urldecode($b); + } +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php b/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php new file mode 100644 index 000000000..3bbde7d90 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php @@ -0,0 +1,136 @@ + + * @date Sep 8, 2008 12:00:14 PM + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod +{ + public function name() + { + return 'RSA-SHA1'; + } + + + /** + * Fetch the public CERT key for the signature + * + * @param OAuthRequest request + * @return string public key + */ + protected function fetch_public_cert ( $request ) + { + // not implemented yet, ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // (2) fetch via http using a url provided by the requester + // (3) some sort of specific discovery code based on request + // + // either way should return a string representation of the certificate + throw OAuthException("OAuthSignatureMethod_RSA_SHA1::fetch_public_cert not implemented"); + } + + + /** + * Fetch the private CERT key for the signature + * + * @param OAuthRequest request + * @return string private key + */ + protected function fetch_private_cert ( $request ) + { + // not implemented yet, ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // + // either way should return a string representation of the certificate + throw OAuthException("OAuthSignatureMethod_RSA_SHA1::fetch_private_cert not implemented"); + } + + + /** + * Calculate the signature using RSA-SHA1 + * This function is copyright Andy Smith, 2008. + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @return string + */ + public function signature ( $request, $base_string, $consumer_secret, $token_secret ) + { + // Fetch the private key cert based on the request + $cert = $this->fetch_private_cert($request); + + // Pull the private key ID from the certificate + $privatekeyid = openssl_get_privatekey($cert); + + // Sign using the key + $sig = false; + $ok = openssl_sign($base_string, $sig, $privatekeyid); + + // Release the key resource + openssl_free_key($privatekeyid); + + return $request->urlencode(base64_encode($sig)); + } + + + /** + * Check if the request signature is the same as the one calculated for the request. + * + * @param OAuthRequest request + * @param string base_string + * @param string consumer_secret + * @param string token_secret + * @param string signature + * @return string + */ + public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature ) + { + $decoded_sig = base64_decode($request->urldecode($signature)); + + // Fetch the public key cert based on the request + $cert = $this->fetch_public_cert($request); + + // Pull the public key ID from the certificate + $publickeyid = openssl_get_publickey($cert); + + // Check the computed signature against the one passed in the query + $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + + // Release the key resource + openssl_free_key($publickeyid); + return $ok == 1; + } + +} + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/store/OAuthStoreAbstract.class.php b/mod/oauth_lib/vendors/oauth/library/store/OAuthStoreAbstract.class.php new file mode 100644 index 000000000..e7cca981a --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/store/OAuthStoreAbstract.class.php @@ -0,0 +1,149 @@ + + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +abstract class OAuthStoreAbstract +{ + abstract public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' ); + abstract public function getSecretsForSignature ( $uri, $user_id ); + abstract public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '' ); + abstract public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() ); + + abstract public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false ); + abstract public function getServer( $consumer_key, $user_id, $user_is_admin = false ); + abstract public function getServerForUri ( $uri, $user_id ); + abstract public function listServerTokens ( $user_id ); + abstract public function countServerTokens ( $consumer_key ); + abstract public function getServerToken ( $consumer_key, $token, $user_id ); + abstract public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false ); + abstract public function listServers ( $q = '', $user_id ); + abstract public function updateServer ( $server, $user_id, $user_is_admin = false ); + + abstract public function updateConsumer ( $consumer, $user_id, $user_is_admin = false ); + abstract public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false ); + abstract public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false ); + abstract public function getConsumerStatic (); + + abstract public function addConsumerRequestToken ( $consumer_key, $options = array() ); + abstract public function getConsumerRequestToken ( $token ); + abstract public function deleteConsumerRequestToken ( $token ); + abstract public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' ); + abstract public function countConsumerAccessTokens ( $consumer_key ); + abstract public function exchangeConsumerRequestForAccessToken ( $token, $options = array() ); + abstract public function getConsumerAccessToken ( $token, $user_id ); + abstract public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false ); + abstract public function setConsumerAccessTokenTtl ( $token, $ttl ); + + abstract public function listConsumers ( $user_id ); + abstract public function listConsumerTokens ( $user_id ); + + abstract public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce ); + + abstract public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null ); + abstract public function listLog ( $options, $user_id ); + + abstract public function install (); + + /** + * Fetch the current static consumer key for this site, create it when it was not found. + * The consumer secret for the consumer key is always empty. + * + * @return string consumer key + */ + + + /* ** Some handy utility functions ** */ + + /** + * Generate a unique key + * + * @param boolean unique force the key to be unique + * @return string + */ + public function generateKey ( $unique = false ) + { + $key = md5(uniqid(rand(), true)); + if ($unique) + { + list($usec,$sec) = explode(' ',microtime()); + $key .= dechex($usec).dechex($sec); + } + return $key; + } + + /** + * Check to see if a string is valid utf8 + * + * @param string $s + * @return boolean + */ + protected function isUTF8 ( $s ) + { + return preg_match('%(?: + [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + |\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + |\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + |\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + |[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + |\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 + )+%xs', $s); + } + + + /** + * Make a string utf8, replacing all non-utf8 chars with a '.' + * + * @param string + * @return string + */ + protected function makeUTF8 ( $s ) + { + if (function_exists('iconv')) + { + do + { + $ok = true; + $text = @iconv('UTF-8', 'UTF-8//TRANSLIT', $s); + if (strlen($text) != strlen($s)) + { + // Remove the offending character... + $s = $text . '.' . substr($s, strlen($text) + 1); + $ok = false; + } + } + while (!$ok); + } + return $s; + } + +} + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/store/OAuthStoreAnyMeta.php b/mod/oauth_lib/vendors/oauth/library/store/OAuthStoreAnyMeta.php new file mode 100644 index 000000000..9c971733f --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/store/OAuthStoreAnyMeta.php @@ -0,0 +1,265 @@ + + * @date Nov 16, 2007 4:03:30 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +require_once dirname(__FILE__) . '/../../../../core/inc/any_database.inc.php'; +require_once dirname(__FILE__) . '/OAuthStoreMySQL.php'; + + +class OAuthStoreAnymeta extends OAuthStoreMySQL +{ + /** + * Construct the OAuthStoreAnymeta + * + * @param array options + */ + function __construct ( $options = array() ) + { + parent::__construct(array('conn' => any_db_conn())); + } + + + /** + * Add an entry to the log table + * + * @param array keys (osr_consumer_key, ost_token, ocr_consumer_key, oct_token) + * @param string received + * @param string sent + * @param string base_string + * @param string notes + * @param int (optional) user_id + */ + public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null ) + { + if (is_null($user_id) && isset($GLOBALS['any_auth'])) + { + $user_id = $GLOBALS['any_auth']->getUserId(); + } + parent::addLog($keys, $received, $sent, $base_string, $notes, $user_id); + } + + + /** + * Get a page of entries from the log. Returns the last 100 records + * matching the options given. + * + * @param array options + * @param int user_id current user + * @return array log records + */ + public function listLog ( $options, $user_id ) + { + $where = array(); + $args = array(); + if (empty($options)) + { + $where[] = 'olg_usa_id_ref = %d'; + $args[] = $user_id; + } + else + { + foreach ($options as $option => $value) + { + if (strlen($value) > 0) + { + switch ($option) + { + case 'osr_consumer_key': + case 'ocr_consumer_key': + case 'ost_token': + case 'oct_token': + $where[] = 'olg_'.$option.' = \'%s\''; + $args[] = $value; + break; + } + } + } + + $where[] = '(olg_usa_id_ref IS NULL OR olg_usa_id_ref = %d)'; + $args[] = $user_id; + } + + $rs = any_db_query_all_assoc(' + SELECT olg_id, + olg_osr_consumer_key AS osr_consumer_key, + olg_ost_token AS ost_token, + olg_ocr_consumer_key AS ocr_consumer_key, + olg_oct_token AS oct_token, + olg_usa_id_ref AS user_id, + olg_received AS received, + olg_sent AS sent, + olg_base_string AS base_string, + olg_notes AS notes, + olg_timestamp AS timestamp, + INET_NTOA(olg_remote_ip) AS remote_ip + FROM oauth_log + WHERE '.implode(' AND ', $where).' + ORDER BY olg_id DESC + LIMIT 0,100', $args); + + return $rs; + } + + + + /** + * Initialise the database + */ + public function install () + { + parent::install(); + + any_db_query("ALTER TABLE oauth_consumer_registry MODIFY ocr_usa_id_ref int(11) unsigned"); + any_db_query("ALTER TABLE oauth_consumer_token MODIFY oct_usa_id_ref int(11) unsigned not null"); + any_db_query("ALTER TABLE oauth_server_registry MODIFY osr_usa_id_ref int(11) unsigned"); + any_db_query("ALTER TABLE oauth_server_token MODIFY ost_usa_id_ref int(11) unsigned not null"); + any_db_query("ALTER TABLE oauth_log MODIFY olg_usa_id_ref int(11) unsigned"); + + any_db_alter_add_fk('oauth_consumer_registry', 'ocr_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete set null'); + any_db_alter_add_fk('oauth_consumer_token', 'oct_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade'); + any_db_alter_add_fk('oauth_server_registry', 'osr_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete set null'); + any_db_alter_add_fk('oauth_server_token', 'ost_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade'); + any_db_alter_add_fk('oauth_log', 'olg_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade'); + } + + + + /** Some simple helper functions for querying the mysql db **/ + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + */ + protected function query ( $sql ) + { + list($sql, $args) = $this->sql_args(func_get_args()); + any_db_query($sql, $args); + } + + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_all_assoc ( $sql ) + { + list($sql, $args) = $this->sql_args(func_get_args()); + return any_db_query_all_assoc($sql, $args); + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row_assoc ( $sql ) + { + list($sql, $args) = $this->sql_args(func_get_args()); + return any_db_query_row_assoc($sql, $args); + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row ( $sql ) + { + list($sql, $args) = $this->sql_args(func_get_args()); + return any_db_query_row($sql, $args); + } + + + /** + * Perform a query, return the first column of the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return mixed + */ + protected function query_one ( $sql ) + { + list($sql, $args) = $this->sql_args(func_get_args()); + return any_db_query_one($sql, $args); + } + + + /** + * Return the number of rows affected in the last query + * + * @return int + */ + protected function query_affected_rows () + { + return any_db_affected_rows(); + } + + + /** + * Return the id of the last inserted row + * + * @return int + */ + protected function query_insert_id () + { + return any_db_insert_id(); + } + + + private function sql_args ( $args ) + { + $sql = array_shift($args); + if (count($args) == 1 && is_array($args[0])) + { + $args = $args[0]; + } + return array($sql, $args); + } + +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/store/OAuthStoreMySQL.php b/mod/oauth_lib/vendors/oauth/library/store/OAuthStoreMySQL.php new file mode 100644 index 000000000..a1b04c5c8 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/store/OAuthStoreMySQL.php @@ -0,0 +1,1879 @@ + + * @date Nov 16, 2007 4:03:30 PM + * + * + * The MIT License + * + * Copyright (c) 2007-2008 Mediamatic Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +require_once dirname(__FILE__) . '/OAuthStoreAbstract.class.php'; + + +class OAuthStoreMySQL extends OAuthStoreAbstract +{ + /** + * The MySQL connection + */ + protected $conn; + + /** + * Maximum delta a timestamp may be off from a previous timestamp. + * Allows multiple consumers with some clock skew to work with the same token. + * Unit is seconds, default max skew is 10 minutes. + */ + protected $max_timestamp_skew = 600; + + /** + * Default ttl for request tokens + */ + protected $max_request_token_ttl = 3600; + + + /** + * Construct the OAuthStoreMySQL. + * In the options you have to supply either: + * - server, username, password and database (for a mysql_connect) + * - conn (for the connection to be used) + * + * @param array options + */ + function __construct ( $options = array() ) + { + if (isset($options['conn'])) + { + $this->conn = $options['conn']; + } + else + { + if (isset($options['server'])) + { + $server = $options['server']; + $username = $options['username']; + + if (isset($options['password'])) + { + $this->conn = mysql_connect($server, $username, $options['password']); + } + else + { + $this->conn = mysql_connect($server, $username); + } + } + else + { + // Try the default mysql connect + $this->conn = mysql_connect(); + } + + if ($this->conn === false) + { + throw new OAuthException('Could not connect to MySQL database: ' . mysql_error()); + } + + if (isset($options['database'])) + { + if (!mysql_select_db($options['database'], $this->conn)) + { + $this->sql_errcheck(); + } + } + $this->query('set character set utf8'); + } + } + + + /** + * Find stored credentials for the consumer key and token. Used by an OAuth server + * when verifying an OAuth request. + * + * @param string consumer_key + * @param string token + * @param string token_type false, 'request' or 'access' + * @exception OAuthException when no secrets where found + * @return array assoc (consumer_secret, token_secret, osr_id, ost_id, user_id) + */ + public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' ) + { + if ($token_type === false) + { + $rs = $this->query_row_assoc(' + SELECT osr_id, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret + FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND osr_enabled = 1 + ', + $consumer_key); + + if ($rs) + { + $rs['token'] = false; + $rs['token_secret'] = false; + $rs['user_id'] = false; + $rs['ost_id'] = false; + } + } + else + { + $rs = $this->query_row_assoc(' + SELECT osr_id, + ost_id, + ost_usa_id_ref as user_id, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + ost_token as token, + ost_token_secret as token_secret + FROM oauth_server_registry + JOIN oauth_server_token + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'%s\' + AND osr_consumer_key = \'%s\' + AND ost_token = \'%s\' + AND osr_enabled = 1 + AND ost_token_ttl >= NOW() + ', + $token_type, $consumer_key, $token); + } + + if (empty($rs)) + { + throw new OAuthException('The consumer_key "'.$consumer_key.'" token "'.$token.'" combination does not exist or is not enabled.'); + } + return $rs; + } + + + /** + * Find the server details for signing a request, always looks for an access token. + * The returned credentials depend on which local user is making the request. + * + * The consumer_key must belong to the user or be public (user id is null) + * + * For signing we need all of the following: + * + * consumer_key consumer key associated with the server + * consumer_secret consumer secret associated with this server + * token access token associated with this server + * token_secret secret for the access token + * signature_methods signing methods supported by the server (array) + * + * @todo filter on token type (we should know how and with what to sign this request, and there might be old access tokens) + * @param string uri uri of the server + * @param int user_id id of the logged on user + * @param string name (optional) name of the token (case sensitive) + * @exception OAuthException when no credentials found + * @return array + */ + public function getSecretsForSignature ( $uri, $user_id, $name = '' ) + { + // Find a consumer key and token for the given uri + $ps = parse_url($uri); + $host = isset($ps['host']) ? $ps['host'] : 'localhost'; + $path = isset($ps['path']) ? $ps['path'] : ''; + + if (empty($path) || substr($path, -1) != '/') + { + $path .= '/'; + } + + // The owner of the consumer_key is either the user or nobody (public consumer key) + $secrets = $this->query_row_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_token as token, + oct_token_secret as token_secret, + ocr_signature_methods as signature_methods + FROM oauth_consumer_registry + JOIN oauth_consumer_token ON oct_ocr_id_ref = ocr_id + WHERE ocr_server_uri_host = \'%s\' + AND ocr_server_uri_path = LEFT(\'%s\', LENGTH(ocr_server_uri_path)) + AND (ocr_usa_id_ref = %s OR ocr_usa_id_ref IS NULL) + AND oct_usa_id_ref = %d + AND oct_token_type = \'access\' + AND oct_name = \'%s\' + AND oct_token_ttl >= NOW() + ORDER BY ocr_usa_id_ref DESC, ocr_consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC + LIMIT 0,1 + ', $host, $path, $user_id, $user_id, $name + ); + + if (empty($secrets)) + { + throw new OAuthException('No server tokens available for '.$uri); + } + $secrets['signature_methods'] = explode(',', $secrets['signature_methods']); + return $secrets; + } + + + /** + * Get the token and token secret we obtained from a server. + * + * @param string consumer_key + * @param string token + * @param string token_type + * @param int user_id the user owning the token + * @param string name optional name for a named token + * @exception OAuthException when no credentials found + * @return array + */ + public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '' ) + { + if ($token_type != 'request' && $token_type != 'access') + { + throw new OAuthException('Unkown token type "'.$token_type.'", must be either "request" or "access"'); + } + + // Take the most recent token of the given type + $r = $this->query_row_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_token as token, + oct_token_secret as token_secret, + oct_name as token_name, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri, + IF(oct_token_ttl >= \'9999-12-31\', NULL, UNIX_TIMESTAMP(oct_token_ttl) - UNIX_TIMESTAMP(NOW())) as token_ttl + FROM oauth_consumer_registry + JOIN oauth_consumer_token + ON oct_ocr_id_ref = ocr_id + WHERE ocr_consumer_key = \'%s\' + AND oct_token_type = \'%s\' + AND oct_token = \'%s\' + AND oct_usa_id_ref = %d + AND oct_token_ttl >= NOW() + ', $consumer_key, $token_type, $token, $user_id + ); + + if (empty($r)) + { + throw new OAuthException('Could not find a "'.$token_type.'" token for consumer "'.$consumer_key.'" and user '.$user_id); + } + if (isset($r['signature_methods']) && !empty($r['signature_methods'])) + { + $r['signature_methods'] = explode(',',$r['signature_methods']); + } + else + { + $r['signature_methods'] = array(); + } + return $r; + } + + + /** + * Add a request token we obtained from a server. + * + * @todo remove old tokens for this user and this ocr_id + * @param string consumer_key key of the server in the consumer registry + * @param string token_type one of 'request' or 'access' + * @param string token + * @param string token_secret + * @param int user_id the user owning the token + * @param array options extra options, name and token_ttl + * @exception OAuthException when server is not known + * @exception OAuthException when we received a duplicate token + */ + public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() ) + { + if ($token_type != 'request' && $token_type != 'access') + { + throw new OAuthException('Unknown token type "'.$token_type.'", must be either "request" or "access"'); + } + + // Maximum time to live for this token + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $ttl = 'DATE_ADD(NOW(), INTERVAL '.intval($options['token_ttl']).' SECOND)'; + } + else if ($token == 'request') + { + $ttl = 'DATE_ADD(NOW(), INTERVAL '.$this->max_request_token_ttl.' SECOND)'; + } + else + { + $ttl = "'9999-12-31'"; + } + + $ocr_id = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + ', $consumer_key); + + if (empty($ocr_id)) + { + throw new OAuthException('No server associated with consumer_key "'.$consumer_key.'"'); + } + + // Named tokens, unique per user/consumer key + if (isset($options['name']) && $options['name'] != '') + { + $name = $options['name']; + } + else + { + $name = ''; + } + + // Delete any old tokens with the same type and name for this user/server combination + $this->query(' + DELETE FROM oauth_consumer_token + WHERE oct_ocr_id_ref = %d + AND oct_usa_id_ref = %d + AND oct_token_type = LOWER(\'%s\') + AND oct_name = \'%s\' + ', + $ocr_id, + $user_id, + $token_type, + $name); + + // Insert the new token + $this->query(' + INSERT IGNORE INTO oauth_consumer_token + SET oct_ocr_id_ref = %d, + oct_usa_id_ref = %d, + oct_name = \'%s\', + oct_token = \'%s\', + oct_token_secret= \'%s\', + oct_token_type = LOWER(\'%s\'), + oct_timestamp = NOW(), + oct_token_ttl = '.$ttl.' + ', + $ocr_id, + $user_id, + $name, + $token, + $token_secret, + $token_type); + + if (!$this->query_affected_rows()) + { + throw new OAuthException('Received duplicate token "'.$token.'" for the same consumer_key "'.$consumer_key.'"'); + } + } + + + /** + * Delete a server key. This removes access to that site. + * + * @param string consumer_key + * @param int user_id user registering this server + * @param boolean user_is_admin + */ + public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + ', $consumer_key, $user_id); + } + else + { + $this->query(' + DELETE FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND ocr_usa_id_ref = %d + ', $consumer_key, $user_id); + } + } + + + /** + * Get a server from the consumer registry using the consumer key + * + * @param string consumer_key + * @param int user_id + * @param boolean user_is_admin (optional) + * @exception OAuthException when server is not found + * @return array + */ + public function getServer ( $consumer_key, $user_id, $user_is_admin = false ) + { + $r = $this->query_row_assoc(' + SELECT ocr_id as id, + ocr_usa_id_ref as user_id, + ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + ', $consumer_key, $user_id); + + if (empty($r)) + { + throw new OAuthException('No server with consumer_key "'.$consumer_key.'" has been registered (for this user)'); + } + + if (isset($r['signature_methods']) && !empty($r['signature_methods'])) + { + $r['signature_methods'] = explode(',',$r['signature_methods']); + } + else + { + $r['signature_methods'] = array(); + } + return $r; + } + + + + /** + * Find the server details that might be used for a request + * + * The consumer_key must belong to the user or be public (user id is null) + * + * @param string uri uri of the server + * @param int user_id id of the logged on user + * @exception OAuthException when no credentials found + * @return array + */ + public function getServerForUri ( $uri, $user_id ) + { + // Find a consumer key and token for the given uri + $ps = parse_url($uri); + $host = isset($ps['host']) ? $ps['host'] : 'localhost'; + $path = isset($ps['path']) ? $ps['path'] : ''; + + if (empty($path) || substr($path, -1) != '/') + { + $path .= '/'; + } + + // The owner of the consumer_key is either the user or nobody (public consumer key) + $server = $this->query_row_assoc(' + SELECT ocr_id as id, + ocr_usa_id_ref as user_id, + ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri + FROM oauth_consumer_registry + WHERE ocr_server_uri_host = \'%s\' + AND ocr_server_uri_path = LEFT(\'%s\', LENGTH(ocr_server_uri_path)) + AND (ocr_usa_id_ref = %s OR ocr_usa_id_ref IS NULL) + ORDER BY ocr_usa_id_ref DESC, consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC + LIMIT 0,1 + ', $host, $path, $user_id + ); + + if (empty($server)) + { + throw new OAuthException('No server available for '.$uri); + } + $server['signature_methods'] = explode(',', $server['signature_methods']); + return $server; + } + + + /** + * Get a list of all server token this user has access to. + * + * @param int usr_id + * @return array + */ + public function listServerTokens ( $user_id ) + { + $ts = $this->query_all_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_id as token_id, + oct_token as token, + oct_token_secret as token_secret, + oct_usa_id_ref as user_id, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_server_uri_host as server_uri_host, + ocr_server_uri_path as server_uri_path, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri, + oct_timestamp as timestamp + FROM oauth_consumer_registry + JOIN oauth_consumer_token + ON oct_ocr_id_ref = ocr_id + WHERE oct_usa_id_ref = %d + AND oct_token_type = \'access\' + AND oct_token_ttl >= NOW() + ORDER BY ocr_server_uri_host, ocr_server_uri_path + ', $user_id); + return $ts; + } + + + /** + * Count how many tokens we have for the given server + * + * @param string consumer_key + * @return int + */ + public function countServerTokens ( $consumer_key ) + { + $count = $this->query_one(' + SELECT COUNT(oct_id) + FROM oauth_consumer_token + JOIN oauth_consumer_registry + ON oct_ocr_id_ref = ocr_id + WHERE oct_token_type = \'access\' + AND ocr_consumer_key = \'%s\' + AND oct_token_ttl >= NOW() + ', $consumer_key); + + return $count; + } + + + /** + * Get a specific server token for the given user + * + * @param string consumer_key + * @param string token + * @param int user_id + * @exception OAuthException when no such token found + * @return array + */ + public function getServerToken ( $consumer_key, $token, $user_id ) + { + $ts = $this->query_row_assoc(' + SELECT ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + oct_token as token, + oct_token_secret as token_secret, + oct_usa_id_ref as usr_id, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_server_uri_host as server_uri_host, + ocr_server_uri_path as server_uri_path, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri, + oct_timestamp as timestamp + FROM oauth_consumer_registry + JOIN oauth_consumer_token + ON oct_ocr_id_ref = ocr_id + WHERE ocr_consumer_key = \'%s\' + AND oct_usa_id_ref = %d + AND oct_token_type = \'access\' + AND oct_token = \'%s\' + AND oct_token_ttl >= NOW() + ', $consumer_key, $user_id, $token); + + if (empty($ts)) + { + throw new OAuthException('No such consumer key ('.$consumer_key.') and token ('.$token.') combination for user "'.$user_id.'"'); + } + return $ts; + } + + + /** + * Delete a token we obtained from a server. + * + * @param string consumer_key + * @param string token + * @param int user_id + * @param boolean user_is_admin + */ + public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE oauth_consumer_token + FROM oauth_consumer_token + JOIN oauth_consumer_registry + ON oct_ocr_id_ref = ocr_id + WHERE ocr_consumer_key = \'%s\' + AND oct_token = \'%s\' + ', $consumer_key, $token); + } + else + { + $this->query(' + DELETE oauth_consumer_token + FROM oauth_consumer_token + JOIN oauth_consumer_registry + ON oct_ocr_id_ref = ocr_id + WHERE ocr_consumer_key = \'%s\' + AND oct_token = \'%s\' + AND oct_usa_id_ref = %d + ', $consumer_key, $token, $user_id); + } + } + + + /** + * Set the ttl of a server access token. This is done when the + * server receives a valid request with a xoauth_token_ttl parameter in it. + * + * @param string consumer_key + * @param string token + * @param int token_ttl + */ + public function setServerTokenTtl ( $consumer_key, $token, $token_ttl ) + { + if ($token_ttl <= 0) + { + // Immediate delete when the token is past its ttl + $this->deleteServerToken($consumer_key, $token, 0, true); + } + else + { + // Set maximum time to live for this token + $this->query(' + UPDATE oauth_consumer_token, oauth_consumer_registry + SET ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND) + WHERE ocr_consumer_key = \'%s\' + AND oct_ocr_id_ref = ocr_id + AND oct_token = \'%s\' + ', $token_ttl, $consumer_key, $token); + } + } + + + /** + * Get a list of all consumers from the consumer registry. + * The consumer keys belong to the user or are public (user id is null) + * + * @param string q query term + * @param int user_id + * @return array + */ + public function listServers ( $q = '', $user_id ) + { + $q = trim(str_replace('%', '', $q)); + $args = array(); + + if (!empty($q)) + { + $where = ' WHERE ( ocr_consumer_key like \'%%%s%%\' + OR ocr_server_uri like \'%%%s%%\' + OR ocr_server_uri_host like \'%%%s%%\' + OR ocr_server_uri_path like \'%%%s%%\') + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + '; + + $args[] = $q; + $args[] = $q; + $args[] = $q; + $args[] = $q; + $args[] = $user_id; + } + else + { + $where = ' WHERE ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL'; + $args[] = $user_id; + } + + $servers = $this->query_all_assoc(' + SELECT ocr_id as id, + ocr_usa_id_ref as user_id, + ocr_consumer_key as consumer_key, + ocr_consumer_secret as consumer_secret, + ocr_signature_methods as signature_methods, + ocr_server_uri as server_uri, + ocr_server_uri_host as server_uri_host, + ocr_server_uri_path as server_uri_path, + ocr_request_token_uri as request_token_uri, + ocr_authorize_uri as authorize_uri, + ocr_access_token_uri as access_token_uri + FROM oauth_consumer_registry + '.$where.' + ORDER BY ocr_server_uri_host, ocr_server_uri_path + ', $args); + return $servers; + } + + + /** + * Register or update a server for our site (we will be the consumer) + * + * (This is the registry at the consumers, registering servers ;-) ) + * + * @param array server + * @param int user_id user registering this server + * @param boolean user_is_admin + * @exception OAuthException when fields are missing or on duplicate consumer_key + * @return consumer_key + */ + public function updateServer ( $server, $user_id, $user_is_admin = false ) + { + foreach (array('consumer_key', 'server_uri') as $f) + { + if (empty($server[$f])) + { + throw new OAuthException('The field "'.$f.'" must be set and non empty'); + } + } + + if (!empty($server['id'])) + { + $exists = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND ocr_id <> %d + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + ', $server['consumer_key'], $server['id'], $user_id); + } + else + { + $exists = $this->query_one(' + SELECT ocr_id + FROM oauth_consumer_registry + WHERE ocr_consumer_key = \'%s\' + AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL) + ', $server['consumer_key'], $user_id); + } + + if ($exists) + { + throw new OAuthException('The server with key "'.$server['consumer_key'].'" has already been registered'); + } + + $parts = parse_url($server['server_uri']); + $host = (isset($parts['host']) ? $parts['host'] : 'localhost'); + $path = (isset($parts['path']) ? $parts['path'] : '/'); + + if (isset($server['signature_methods'])) + { + if (is_array($server['signature_methods'])) + { + $server['signature_methods'] = strtoupper(implode(',', $server['signature_methods'])); + } + } + else + { + $server['signature_methods'] = ''; + } + + // When the user is an admin, then the user can update the user_id of this record + if ($user_is_admin && array_key_exists('user_id', $server)) + { + if (is_null($server['user_id'])) + { + $update_user = ', ocr_usa_id_ref = NULL'; + } + else + { + $update_user = ', ocr_usa_id_ref = '.intval($server['user_id']); + } + } + else + { + $update_user = ''; + } + + if (!empty($server['id'])) + { + // Check if the current user can update this server definition + if (!$user_is_admin) + { + $ocr_usa_id_ref = $this->query_one(' + SELECT ocr_usa_id_ref + FROM oauth_consumer_registry + WHERE ocr_id = %d + ', $server['id']); + + if ($ocr_usa_id_ref != $user_id) + { + throw new OAuthException('The user "'.$user_id.'" is not allowed to update this server'); + } + } + + // Update the consumer registration + $this->query(' + UPDATE oauth_consumer_registry + SET ocr_consumer_key = \'%s\', + ocr_consumer_secret = \'%s\', + ocr_server_uri = \'%s\', + ocr_server_uri_host = \'%s\', + ocr_server_uri_path = \'%s\', + ocr_timestamp = NOW(), + ocr_request_token_uri = \'%s\', + ocr_authorize_uri = \'%s\', + ocr_access_token_uri = \'%s\', + ocr_signature_methods = \'%s\' + '.$update_user.' + WHERE ocr_id = %d + ', + $server['consumer_key'], + $server['consumer_secret'], + $server['server_uri'], + strtolower($host), + $path, + isset($server['request_token_uri']) ? $server['request_token_uri'] : '', + isset($server['authorize_uri']) ? $server['authorize_uri'] : '', + isset($server['access_token_uri']) ? $server['access_token_uri'] : '', + $server['signature_methods'], + $server['id'] + ); + } + else + { + if (empty($update_user)) + { + // Per default the user owning the key is the user registering the key + $update_user = ', ocr_usa_id_ref = '.intval($user_id); + } + + $this->query(' + INSERT INTO oauth_consumer_registry + SET ocr_consumer_key = \'%s\', + ocr_consumer_secret = \'%s\', + ocr_server_uri = \'%s\', + ocr_server_uri_host = \'%s\', + ocr_server_uri_path = \'%s\', + ocr_timestamp = NOW(), + ocr_request_token_uri = \'%s\', + ocr_authorize_uri = \'%s\', + ocr_access_token_uri = \'%s\', + ocr_signature_methods = \'%s\' + '.$update_user, + $server['consumer_key'], + $server['consumer_secret'], + $server['server_uri'], + strtolower($host), + $path, + isset($server['request_token_uri']) ? $server['request_token_uri'] : '', + isset($server['authorize_uri']) ? $server['authorize_uri'] : '', + isset($server['access_token_uri']) ? $server['access_token_uri'] : '', + $server['signature_methods'] + ); + + $ocr_id = $this->query_insert_id(); + } + return $server['consumer_key']; + } + + + /** + * Insert/update a new consumer with this server (we will be the server) + * When this is a new consumer, then also generate the consumer key and secret. + * Never updates the consumer key and secret. + * When the id is set, then the key and secret must correspond to the entry + * being updated. + * + * (This is the registry at the server, registering consumers ;-) ) + * + * @param array consumer + * @param int user_id user registering this consumer + * @param boolean user_is_admin + * @return string consumer key + */ + public function updateConsumer ( $consumer, $user_id, $user_is_admin = false ) + { + if (!$user_is_admin) + { + foreach (array('requester_name', 'requester_email') as $f) + { + if (empty($consumer[$f])) + { + throw new OAuthException('The field "'.$f.'" must be set and non empty'); + } + } + } + + if (!empty($consumer['id'])) + { + if (empty($consumer['consumer_key'])) + { + throw new OAuthException('The field "consumer_key" must be set and non empty'); + } + if (!$user_is_admin && empty($consumer['consumer_secret'])) + { + throw new OAuthException('The field "consumer_secret" must be set and non empty'); + } + + // Check if the current user can update this server definition + if (!$user_is_admin) + { + $osr_usa_id_ref = $this->query_one(' + SELECT osr_usa_id_ref + FROM oauth_server_registry + WHERE osr_id = %d + ', $consumer['id']); + + if ($osr_usa_id_ref != $user_id) + { + throw new OAuthException('The user "'.$user_id.'" is not allowed to update this consumer'); + } + } + else + { + // User is an admin, allow a key owner to be changed or key to be shared + if (array_key_exists('user_id',$consumer)) + { + if (is_null($consumer['user_id'])) + { + $this->query(' + UPDATE oauth_server_registry + SET osr_usa_id_ref = NULL + WHERE osr_id = %d + ', $consumer['id']); + } + else + { + $this->query(' + UPDATE oauth_server_registry + SET osr_usa_id_ref = %d + WHERE osr_id = %d + ', $consumer['user_id'], $consumer['id']); + } + } + } + + $this->query(' + UPDATE oauth_server_registry + SET osr_requester_name = \'%s\', + osr_requester_email = \'%s\', + osr_callback_uri = \'%s\', + osr_application_uri = \'%s\', + osr_application_title = \'%s\', + osr_application_descr = \'%s\', + osr_application_notes = \'%s\', + osr_application_type = \'%s\', + osr_application_commercial = IF(%d,1,0), + osr_timestamp = NOW() + WHERE osr_id = %d + AND osr_consumer_key = \'%s\' + AND osr_consumer_secret = \'%s\' + ', + $consumer['requester_name'], + $consumer['requester_email'], + isset($consumer['callback_uri']) ? $consumer['callback_uri'] : '', + isset($consumer['application_uri']) ? $consumer['application_uri'] : '', + isset($consumer['application_title']) ? $consumer['application_title'] : '', + isset($consumer['application_descr']) ? $consumer['application_descr'] : '', + isset($consumer['application_notes']) ? $consumer['application_notes'] : '', + isset($consumer['application_type']) ? $consumer['application_type'] : '', + isset($consumer['application_commercial']) ? $consumer['application_commercial'] : 0, + $consumer['id'], + $consumer['consumer_key'], + $consumer['consumer_secret'] + ); + + + $consumer_key = $consumer['consumer_key']; + } + else + { + $consumer_key = $this->generateKey(true); + $consumer_secret= $this->generateKey(); + + // When the user is an admin, then the user can be forced to something else that the user + if ($user_is_admin && array_key_exists('user_id',$consumer)) + { + if (is_null($consumer['user_id'])) + { + $owner_id = 'NULL'; + } + else + { + $owner_id = intval($consumer['user_id']); + } + } + else + { + // No admin, take the user id as the owner id. + $owner_id = intval($user_id); + } + + $this->query(' + INSERT INTO oauth_server_registry + SET osr_enabled = 1, + osr_status = \'active\', + osr_usa_id_ref = %s, + osr_consumer_key = \'%s\', + osr_consumer_secret = \'%s\', + osr_requester_name = \'%s\', + osr_requester_email = \'%s\', + osr_callback_uri = \'%s\', + osr_application_uri = \'%s\', + osr_application_title = \'%s\', + osr_application_descr = \'%s\', + osr_application_notes = \'%s\', + osr_application_type = \'%s\', + osr_application_commercial = IF(%d,1,0), + osr_timestamp = NOW(), + osr_issue_date = NOW() + ', + $owner_id, + $consumer_key, + $consumer_secret, + $consumer['requester_name'], + $consumer['requester_email'], + isset($consumer['callback_uri']) ? $consumer['callback_uri'] : '', + isset($consumer['application_uri']) ? $consumer['application_uri'] : '', + isset($consumer['application_title']) ? $consumer['application_title'] : '', + isset($consumer['application_descr']) ? $consumer['application_descr'] : '', + isset($consumer['application_notes']) ? $consumer['application_notes'] : '', + isset($consumer['application_type']) ? $consumer['application_type'] : '', + isset($consumer['application_commercial']) ? $consumer['application_commercial'] : 0 + ); + } + return $consumer_key; + + } + + + + /** + * Delete a consumer key. This removes access to our site for all applications using this key. + * + * @param string consumer_key + * @param int user_id user registering this server + * @param boolean user_is_admin + */ + public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND (osr_usa_id_ref = %d OR osr_usa_id_ref IS NULL) + ', $consumer_key, $user_id); + } + else + { + $this->query(' + DELETE FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND osr_usa_id_ref = %d + ', $consumer_key, $user_id); + } + } + + + + /** + * Fetch a consumer of this server, by consumer_key. + * + * @param string consumer_key + * @param int user_id + * @param boolean user_is_admin (optional) + * @exception OAuthException when consumer not found + * @return array + */ + public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false ) + { + $consumer = $this->query_row_assoc(' + SELECT * + FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + ', $consumer_key); + + if (!is_array($consumer)) + { + throw new OAuthException('No consumer with consumer_key "'.$consumer_key.'"'); + } + + $c = array(); + foreach ($consumer as $key => $value) + { + $c[substr($key, 4)] = $value; + } + $c['user_id'] = $c['usa_id_ref']; + + if (!$user_is_admin && !empty($c['user_id']) && $c['user_id'] != $user_id) + { + throw new OAuthException('No access to the consumer information for consumer_key "'.$consumer_key.'"'); + } + return $c; + } + + + /** + * Fetch the static consumer key for this provider. The user for the static consumer + * key is NULL (no user, shared key). If the key did not exist then the key is created. + * + * @return string + */ + public function getConsumerStatic () + { + $consumer = $this->query_one(' + SELECT osr_consumer_key + FROM oauth_server_registry + WHERE osr_consumer_key LIKE \'sc-%%\' + AND osr_usa_id_ref IS NULL + '); + + if (empty($consumer)) + { + $consumer_key = 'sc-'.$this->generateKey(true); + $this->query(' + INSERT INTO oauth_server_registry + SET osr_enabled = 1, + osr_status = \'active\', + osr_usa_id_ref = NULL, + osr_consumer_key = \'%s\', + osr_consumer_secret = \'\', + osr_requester_name = \'\', + osr_requester_email = \'\', + osr_callback_uri = \'\', + osr_application_uri = \'\', + osr_application_title = \'Static shared consumer key\', + osr_application_descr = \'\', + osr_application_notes = \'Static shared consumer key\', + osr_application_type = \'\', + osr_application_commercial = 0, + osr_timestamp = NOW(), + osr_issue_date = NOW() + ', + $consumer_key + ); + + // Just make sure that if the consumer key is truncated that we get the truncated string + $consumer = $this->getConsumerStatic(); + } + return $consumer; + } + + + /** + * Add an unautorized request token to our server. + * + * @param string consumer_key + * @param array options (eg. token_ttl) + * @return array (token, token_secret) + */ + public function addConsumerRequestToken ( $consumer_key, $options = array() ) + { + $token = $this->generateKey(true); + $secret = $this->generateKey(); + $osr_id = $this->query_one(' + SELECT osr_id + FROM oauth_server_registry + WHERE osr_consumer_key = \'%s\' + AND osr_enabled = 1 + ', $consumer_key); + + if (!$osr_id) + { + throw new OAuthException('No server with consumer_key "'.$consumer_key.'" or consumer_key is disabled'); + } + + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $ttl = intval($options['token_ttl']); + } + else + { + $ttl = $this->max_request_token_ttl; + } + + $this->query(' + INSERT INTO oauth_server_token + SET ost_osr_id_ref = %d, + ost_usa_id_ref = 1, + ost_token = \'%s\', + ost_token_secret = \'%s\', + ost_token_type = \'request\', + ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND) + ON DUPLICATE KEY UPDATE + ost_osr_id_ref = VALUES(ost_osr_id_ref), + ost_usa_id_ref = VALUES(ost_usa_id_ref), + ost_token = VALUES(ost_token), + ost_token_secret = VALUES(ost_token_secret), + ost_token_type = VALUES(ost_token_type), + ost_token_ttl = VALUES(ost_token_ttl), + ost_timestamp = NOW() + ', $osr_id, $token, $secret, $ttl); + + return array('token'=>$token, 'token_secret'=>$secret, 'token_ttl'=>$ttl); + } + + + /** + * Fetch the consumer request token, by request token. + * + * @param string token + * @return array token and consumer details + */ + public function getConsumerRequestToken ( $token ) + { + $rs = $this->query_row_assoc(' + SELECT ost_token as token, + ost_token_secret as token_secret, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + ost_token_type as token_type + FROM oauth_server_token + JOIN oauth_server_registry + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'request\' + AND ost_token = \'%s\' + AND ost_token_ttl >= NOW() + ', $token); + + return $rs; + } + + + /** + * Delete a consumer token. The token must be a request or authorized token. + * + * @param string token + */ + public function deleteConsumerRequestToken ( $token ) + { + $this->query(' + DELETE FROM oauth_server_token + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + ', $token); + } + + + /** + * Upgrade a request token to be an authorized request token. + * + * @param string token + * @param int user_id user authorizing the token + * @param string referrer_host used to set the referrer host for this token, for user feedback + */ + public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' ) + { + $this->query(' + UPDATE oauth_server_token + SET ost_authorized = 1, + ost_usa_id_ref = %d, + ost_timestamp = NOW(), + ost_referrer_host = \'%s\' + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + ', $user_id, $referrer_host, $token); + } + + + /** + * Count the consumer access tokens for the given consumer. + * + * @param string consumer_key + * @return int + */ + public function countConsumerAccessTokens ( $consumer_key ) + { + $count = $this->query_one(' + SELECT COUNT(ost_id) + FROM oauth_server_token + JOIN oauth_server_registry + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'access\' + AND osr_consumer_key = \'%s\' + AND ost_token_ttl >= NOW() + ', $consumer_key); + + return $count; + } + + + /** + * Exchange an authorized request token for new access token. + * + * @param string token + * @param array options options for the token, token_ttl + * @exception OAuthException when token could not be exchanged + * @return array (token, token_secret) + */ + public function exchangeConsumerRequestForAccessToken ( $token, $options = array() ) + { + $new_token = $this->generateKey(true); + $new_secret = $this->generateKey(); + + // Maximum time to live for this token + if (isset($options['token_ttl']) && is_numeric($options['token_ttl'])) + { + $ttl_sql = 'DATE_ADD(NOW(), INTERVAL '.intval($options['token_ttl']).' SECOND)'; + } + else + { + $ttl_sql = "'9999-12-31'"; + } + + $this->query(' + UPDATE oauth_server_token + SET ost_token = \'%s\', + ost_token_secret = \'%s\', + ost_token_type = \'access\', + ost_timestamp = NOW(), + ost_token_ttl = '.$ttl_sql.' + WHERE ost_token = \'%s\' + AND ost_token_type = \'request\' + AND ost_authorized = 1 + AND ost_token_ttl >= NOW() + ', $new_token, $new_secret, $token); + + if ($this->query_affected_rows() != 1) + { + throw new OAuthException('Can\'t exchange request token "'.$token.'" for access token. No such token or not authorized'); + } + + $ret = array('token' => $new_token, 'token_secret' => $new_secret); + $ttl = $this->query_one(' + SELECT IF(ost_token_ttl >= \'9999-12-31\', NULL, UNIX_TIMESTAMP(ost_token_ttl) - UNIX_TIMESTAMP(NOW())) as token_ttl + FROM oauth_server_token + WHERE ost_token = \'%s\'', $new_token); + + if (is_numeric($ttl)) + { + $ret['token_ttl'] = intval($ttl); + } + return $ret; + } + + + /** + * Fetch the consumer access token, by access token. + * + * @param string token + * @param int user_id + * @exception OAuthException when token is not found + * @return array token and consumer details + */ + public function getConsumerAccessToken ( $token, $user_id ) + { + $rs = $this->query_row_assoc(' + SELECT ost_token as token, + ost_token_secret as token_secret, + ost_referrer_host as token_referrer_host, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr + FROM oauth_server_token + JOIN oauth_server_registry + ON ost_osr_id_ref = osr_id + WHERE ost_token_type = \'access\' + AND ost_token = \'%s\' + AND ost_usa_id_ref = %d + AND ost_token_ttl >= NOW() + ', $token, $user_id); + + if (empty($rs)) + { + throw new OAuthException('No server_token "'.$token.'" for user "'.$user_id.'"'); + } + return $rs; + } + + + /** + * Delete a consumer access token. + * + * @param string token + * @param int user_id + * @param boolean user_is_admin + */ + public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false ) + { + if ($user_is_admin) + { + $this->query(' + DELETE FROM oauth_server_token + WHERE ost_token = \'%s\' + AND ost_token_type = \'access\' + ', $token); + } + else + { + $this->query(' + DELETE FROM oauth_server_token + WHERE ost_token = \'%s\' + AND ost_token_type = \'access\' + AND ost_usa_id_ref = %d + ', $token, $user_id); + } + } + + + /** + * Set the ttl of a consumer access token. This is done when the + * server receives a valid request with a xoauth_token_ttl parameter in it. + * + * @param string token + * @param int ttl + */ + public function setConsumerAccessTokenTtl ( $token, $token_ttl ) + { + if ($token_ttl <= 0) + { + // Immediate delete when the token is past its ttl + $this->deleteConsumerAccessToken($token, 0, true); + } + else + { + // Set maximum time to live for this token + $this->query(' + UPDATE oauth_server_token + SET ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND) + WHERE ost_token = \'%s\' + AND ost_token_type = \'access\' + ', $token_ttl, $token); + } + } + + + /** + * Fetch a list of all consumer keys, secrets etc. + * Returns the public (user_id is null) and the keys owned by the user + * + * @param int user_id + * @return array + */ + public function listConsumers ( $user_id ) + { + $rs = $this->query_all_assoc(' + SELECT osr_id as id, + osr_usa_id_ref as user_id, + osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + osr_enabled as enabled, + osr_status as status, + osr_issue_date as issue_date, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr, + osr_requester_name as requester_name, + osr_requester_email as requester_email + FROM oauth_server_registry + WHERE (osr_usa_id_ref = %d OR osr_usa_id_ref IS NULL) + ORDER BY osr_application_title + ', $user_id); + return $rs; + } + + + /** + * Fetch a list of all consumer tokens accessing the account of the given user. + * + * @param int user_id + * @return array + */ + public function listConsumerTokens ( $user_id ) + { + $rs = $this->query_all_assoc(' + SELECT osr_consumer_key as consumer_key, + osr_consumer_secret as consumer_secret, + osr_enabled as enabled, + osr_status as status, + osr_application_uri as application_uri, + osr_application_title as application_title, + osr_application_descr as application_descr, + ost_timestamp as timestamp, + ost_token as token, + ost_token_secret as token_secret, + ost_referrer_host as token_referrer_host + FROM oauth_server_registry + JOIN oauth_server_token + ON ost_osr_id_ref = osr_id + WHERE ost_usa_id_ref = %d + AND ost_token_type = \'access\' + AND ost_token_ttl >= NOW() + ORDER BY osr_application_title + ', $user_id); + return $rs; + } + + + /** + * Check an nonce/timestamp combination. Clears any nonce combinations + * that are older than the one received. + * + * @param string consumer_key + * @param string token + * @param int timestamp + * @param string nonce + * @exception OAuthException thrown when the timestamp is not in sequence or nonce is not unique + */ + public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce ) + { + $r = $this->query_row(' + SELECT MAX(osn_timestamp), MAX(osn_timestamp) > %d + %d + FROM oauth_server_nonce + WHERE osn_consumer_key = \'%s\' + AND osn_token = \'%s\' + ', $timestamp, $this->max_timestamp_skew, $consumer_key, $token); + + if (!empty($r) && $r[1]) + { + throw new OAuthException('Timestamp is out of sequence. Request rejected. Got '.$timestamp.' last max is '.$r[0].' allowed skew is '.$this->max_timestamp_skew); + } + + // Insert the new combination + $this->query(' + INSERT IGNORE INTO oauth_server_nonce + SET osn_consumer_key = \'%s\', + osn_token = \'%s\', + osn_timestamp = %d, + osn_nonce = \'%s\' + ', $consumer_key, $token, $timestamp, $nonce); + + if ($this->query_affected_rows() == 0) + { + throw new OAuthException('Duplicate timestamp/nonce combination, possible replay attack. Request rejected.'); + } + + // Clean up all timestamps older than the one we just received + $this->query(' + DELETE FROM oauth_server_nonce + WHERE osn_consumer_key = \'%s\' + AND osn_token = \'%s\' + AND osn_timestamp < %d - %d + ', $consumer_key, $token, $timestamp, $this->max_timestamp_skew); + } + + + /** + * Add an entry to the log table + * + * @param array keys (osr_consumer_key, ost_token, ocr_consumer_key, oct_token) + * @param string received + * @param string sent + * @param string base_string + * @param string notes + * @param int (optional) user_id + */ + public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null ) + { + $args = array(); + $ps = array(); + foreach ($keys as $key => $value) + { + $args[] = $value; + $ps[] = "olg_$key = '%s'"; + } + + if (!empty($_SERVER['REMOTE_ADDR'])) + { + $remote_ip = $_SERVER['REMOTE_ADDR']; + } + else if (!empty($_SERVER['REMOTE_IP'])) + { + $remote_ip = $_SERVER['REMOTE_IP']; + } + else + { + $remote_ip = '0.0.0.0'; + } + + // Build the SQL + $ps[] = "olg_received = '%s'"; $args[] = $this->makeUTF8($received); + $ps[] = "olg_sent = '%s'"; $args[] = $this->makeUTF8($sent); + $ps[] = "olg_base_string= '%s'"; $args[] = $base_string; + $ps[] = "olg_notes = '%s'"; $args[] = $this->makeUTF8($notes); + $ps[] = "olg_usa_id_ref = NULLIF(%d,0)"; $args[] = $user_id; + $ps[] = "olg_remote_ip = IFNULL(INET_ATON('%s'),0)"; $args[] = $remote_ip; + + $this->query('INSERT INTO oauth_log SET '.implode(',', $ps), $args); + } + + + /** + * Get a page of entries from the log. Returns the last 100 records + * matching the options given. + * + * @param array options + * @param int user_id current user + * @return array log records + */ + public function listLog ( $options, $user_id ) + { + $where = array(); + $args = array(); + if (empty($options)) + { + $where[] = 'olg_usa_id_ref = %d'; + $args[] = $user_id; + } + else + { + foreach ($options as $option => $value) + { + if (strlen($value) > 0) + { + switch ($option) + { + case 'osr_consumer_key': + case 'ocr_consumer_key': + case 'ost_token': + case 'oct_token': + $where[] = 'olg_'.$option.' = \'%s\''; + $args[] = $value; + break; + } + } + } + + $where[] = '(olg_usa_id_ref IS NULL OR olg_usa_id_ref = %d)'; + $args[] = $user_id; + } + + $rs = $this->query_all_assoc(' + SELECT olg_id, + olg_osr_consumer_key AS osr_consumer_key, + olg_ost_token AS ost_token, + olg_ocr_consumer_key AS ocr_consumer_key, + olg_oct_token AS oct_token, + olg_usa_id_ref AS user_id, + olg_received AS received, + olg_sent AS sent, + olg_base_string AS base_string, + olg_notes AS notes, + olg_timestamp AS timestamp, + INET_NTOA(olg_remote_ip) AS remote_ip + FROM oauth_log + WHERE '.implode(' AND ', $where).' + ORDER BY olg_id DESC + LIMIT 0,100', $args); + + return $rs; + } + + + + /** + * Initialise the database + */ + public function install () + { + require_once dirname(__FILE__) . '/mysql/install.php'; + } + + + /* ** Some simple helper functions for querying the mysql db ** */ + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + */ + protected function query ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysql_query($sql, $this->conn))) + { + $this->sql_errcheck($sql); + } + if (is_resource($res)) + { + mysql_free_result($res); + } + } + + + /** + * Perform a query, ignore the results + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_all_assoc ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysql_query($sql, $this->conn))) + { + $this->sql_errcheck($sql); + } + $rs = array(); + while ($row = mysql_fetch_assoc($res)) + { + $rs[] = $row; + } + mysql_free_result($res); + return $rs; + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row_assoc ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysql_query($sql, $this->conn))) + { + $this->sql_errcheck($sql); + } + if ($row = mysql_fetch_assoc($res)) + { + $rs = $row; + } + else + { + $rs = false; + } + mysql_free_result($res); + return $rs; + } + + + /** + * Perform a query, return the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return array + */ + protected function query_row ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysql_query($sql, $this->conn))) + { + $this->sql_errcheck($sql); + } + if ($row = mysql_fetch_array($res)) + { + $rs = $row; + } + else + { + $rs = false; + } + mysql_free_result($res); + return $rs; + } + + + /** + * Perform a query, return the first column of the first row + * + * @param string sql + * @param vararg arguments (for sprintf) + * @return mixed + */ + protected function query_one ( $sql ) + { + $sql = $this->sql_printf(func_get_args()); + if (!($res = mysql_query($sql, $this->conn))) + { + $this->sql_errcheck($sql); + } + $val = @mysql_result($res, 0, 0); + mysql_free_result($res); + return $val; + } + + + /** + * Return the number of rows affected in the last query + */ + protected function query_affected_rows () + { + return mysql_affected_rows($this->conn); + } + + + /** + * Return the id of the last inserted row + * + * @return int + */ + protected function query_insert_id () + { + return mysql_insert_id($this->conn); + } + + + protected function sql_printf ( $args ) + { + $sql = array_shift($args); + if (count($args) == 1 && is_array($args[0])) + { + $args = $args[0]; + } + $args = array_map(array($this, 'sql_escape_string'), $args); + return vsprintf($sql, $args); + } + + + protected function sql_escape_string ( $s ) + { + if (is_string($s)) + { + return mysql_real_escape_string($s, $this->conn); + } + else if (is_null($s)) + { + return NULL; + } + else if (is_bool($s)) + { + return intval($s); + } + else if (is_int($s) || is_float($s)) + { + return $s; + } + else + { + return mysql_real_escape_string(strval($s), $this->conn); + } + } + + + protected function sql_errcheck ( $sql ) + { + if (mysql_errno($this->conn)) + { + $msg = "SQL Error in OAuthStoreMySQL: ".mysql_error($this->conn)."\n\n" . $sql; + throw new OAuthException($msg); + } + } +} + + +/* vi:set ts=4 sts=4 sw=4 binary noeol: */ + +?> \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/store/mysql/install.php b/mod/oauth_lib/vendors/oauth/library/store/mysql/install.php new file mode 100644 index 000000000..0015da5e3 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/store/mysql/install.php @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/mod/oauth_lib/vendors/oauth/library/store/mysql/mysql.sql b/mod/oauth_lib/vendors/oauth/library/store/mysql/mysql.sql new file mode 100644 index 000000000..d652a1c99 --- /dev/null +++ b/mod/oauth_lib/vendors/oauth/library/store/mysql/mysql.sql @@ -0,0 +1,219 @@ +# Datamodel for OAuthStoreMySQL +# +# You need to add the foreign key constraints for the user ids your are using. +# I have commented the constraints out, just look for 'usa_id_ref' to enable them. +# +# The --SPLIT-- markers are used by the install.php script +# +# @version $Id: mysql.sql 51 2008-10-15 15:15:47Z marcw@pobox.com $ +# @author Marc Worrell +# + +# Changes: +# +# 2008-10-15 (on r48) Added ttl to consumer and server tokens, added named server tokens +# +# ALTER TABLE oauth_server_token +# ADD ost_token_ttl datetime not null default '9999-12-31', +# ADD KEY (ost_token_ttl); +# +# ALTER TABLE oauth_consumer_token +# ADD oct_name varchar(64) binary not null default '', +# ADD oct_token_ttl datetime not null default '9999-12-31', +# DROP KEY oct_usa_id_ref, +# ADD UNIQUE KEY (oct_usa_id_ref, oct_ocr_id_ref, oct_token_type, oct_name), +# ADD KEY (oct_token_ttl); +# +# 2008-09-09 (on r5) Added referrer host to server access token +# +# ALTER TABLE oauth_server_token ADD ost_referrer_host VARCHAR(128) NOT NULL; +# + + +# +# Log table to hold all OAuth request when you enabled logging +# + +CREATE TABLE IF NOT EXISTS oauth_log ( + olg_id int(11) not null auto_increment, + olg_osr_consumer_key varchar(64) binary, + olg_ost_token varchar(64) binary, + olg_ocr_consumer_key varchar(64) binary, + olg_oct_token varchar(64) binary, + olg_usa_id_ref int(11), + olg_received text not null, + olg_sent text not null, + olg_base_string text not null, + olg_notes text not null, + olg_timestamp timestamp not null default current_timestamp, + olg_remote_ip bigint not null, + + primary key (olg_id), + key (olg_osr_consumer_key, olg_id), + key (olg_ost_token, olg_id), + key (olg_ocr_consumer_key, olg_id), + key (olg_oct_token, olg_id), + key (olg_usa_id_ref, olg_id) + +# , foreign key (olg_usa_id_ref) references any_user_auth (usa_id_ref) +# on update cascade +# on delete cascade +) engine=InnoDB default charset=utf8; + +#--SPLIT-- + +# +# /////////////////// CONSUMER SIDE /////////////////// +# + +# This is a registry of all consumer codes we got from other servers +# The consumer_key/secret is obtained from the server +# We also register the server uri, so that we can find the consumer key and secret +# for a certain server. From that server we can check if we have a token for a +# particular user. + +CREATE TABLE IF NOT EXISTS oauth_consumer_registry ( + ocr_id int(11) not null auto_increment, + ocr_usa_id_ref int(11), + ocr_consumer_key varchar(64) binary not null, + ocr_consumer_secret varchar(64) binary not null, + ocr_signature_methods varchar(255) not null default 'HMAC-SHA1,PLAINTEXT', + ocr_server_uri varchar(255) not null, + ocr_server_uri_host varchar(128) not null, + ocr_server_uri_path varchar(128) binary not null, + + ocr_request_token_uri varchar(255) not null, + ocr_authorize_uri varchar(255) not null, + ocr_access_token_uri varchar(255) not null, + ocr_timestamp timestamp not null default current_timestamp, + + primary key (ocr_id), + unique key (ocr_consumer_key, ocr_usa_id_ref), + key (ocr_server_uri), + key (ocr_server_uri_host, ocr_server_uri_path), + key (ocr_usa_id_ref) + +# , foreign key (ocr_usa_id_ref) references any_user_auth(usa_id_ref) +# on update cascade +# on delete set null +) engine=InnoDB default charset=utf8; + +#--SPLIT-- + +# Table used to sign requests for sending to a server by the consumer +# The key is defined for a particular user. Only one single named +# key is allowed per user/server combination + +CREATE TABLE IF NOT EXISTS oauth_consumer_token ( + oct_id int(11) not null auto_increment, + oct_ocr_id_ref int(11) not null, + oct_usa_id_ref int(11) not null, + oct_name varchar(64) binary not null default '', + oct_token varchar(64) binary not null, + oct_token_secret varchar(64) binary not null, + oct_token_type enum('request','authorized','access'), + oct_token_ttl datetime not null default '9999-12-31', + oct_timestamp timestamp not null default current_timestamp, + + primary key (oct_id), + unique key (oct_ocr_id_ref, oct_token), + unique key (oct_usa_id_ref, oct_ocr_id_ref, oct_token_type, oct_name), + key (oct_token_ttl), + + foreign key (oct_ocr_id_ref) references oauth_consumer_registry (ocr_id) + on update cascade + on delete cascade + +# , foreign key (oct_usa_id_ref) references any_user_auth (usa_id_ref) +# on update cascade +# on delete cascade +) engine=InnoDB default charset=utf8; + +#--SPLIT-- + + +# +# ////////////////// SERVER SIDE ///////////////// +# + +# Table holding consumer key/secret combos an user issued to consumers. +# Used for verification of incoming requests. + +CREATE TABLE IF NOT EXISTS oauth_server_registry ( + osr_id int(11) not null auto_increment, + osr_usa_id_ref int(11), + osr_consumer_key varchar(64) binary not null, + osr_consumer_secret varchar(64) binary not null, + osr_enabled tinyint(1) not null default '1', + osr_status varchar(16) not null, + osr_requester_name varchar(64) not null, + osr_requester_email varchar(64) not null, + osr_callback_uri varchar(255) not null, + osr_application_uri varchar(255) not null, + osr_application_title varchar(80) not null, + osr_application_descr text not null, + osr_application_notes text not null, + osr_application_type varchar(20) not null, + osr_application_commercial tinyint(1) not null default '0', + osr_issue_date datetime not null, + osr_timestamp timestamp not null default current_timestamp, + + primary key (osr_id), + unique key (osr_consumer_key), + key (osr_usa_id_ref) + +# , foreign key (osr_usa_id_ref) references any_user_auth(usa_id_ref) +# on update cascade +# on delete set null +) engine=InnoDB default charset=utf8; + +#--SPLIT-- + +# Nonce used by a certain consumer, every used nonce should be unique, this prevents +# replaying attacks. We need to store all timestamp/nonce combinations for the +# maximum timestamp received. + +CREATE TABLE IF NOT EXISTS oauth_server_nonce ( + osn_id int(11) not null auto_increment, + osn_consumer_key varchar(64) binary not null, + osn_token varchar(64) binary not null, + osn_timestamp bigint not null, + osn_nonce varchar(80) binary not null, + + primary key (osn_id), + unique key (osn_consumer_key, osn_token, osn_timestamp, osn_nonce) +) engine=InnoDB default charset=utf8; + +#--SPLIT-- + +# Table used to verify signed requests sent to a server by the consumer +# When the verification is succesful then the associated user id is returned. + +CREATE TABLE IF NOT EXISTS oauth_server_token ( + ost_id int(11) not null auto_increment, + ost_osr_id_ref int(11) not null, + ost_usa_id_ref int(11) not null, + ost_token varchar(64) binary not null, + ost_token_secret varchar(64) binary not null, + ost_token_type enum('request','access'), + ost_authorized tinyint(1) not null default '0', + ost_referrer_host varchar(128) not null, + ost_token_ttl datetime not null default '9999-12-31', + ost_timestamp timestamp not null default current_timestamp, + + primary key (ost_id), + unique key (ost_token), + key (ost_osr_id_ref), + key (ost_token_ttl), + + foreign key (ost_osr_id_ref) references oauth_server_registry (osr_id) + on update cascade + on delete cascade + +# , foreign key (ost_usa_id_ref) references any_user_auth (usa_id_ref) +# on update cascade +# on delete cascade +) engine=InnoDB default charset=utf8; + + + -- cgit v1.2.3