diff options
| author | spider@dev <spiderr@bitweaver.org> | 2017-06-30 13:33:16 -0400 |
|---|---|---|
| committer | spider@dev <spiderr@bitweaver.org> | 2017-06-30 13:33:16 -0400 |
| commit | 053aeba110edfb543b07fc58d462477b2b9c7de9 (patch) | |
| tree | 5a994f7503c1bb191103cc6b5f02c98ab06e0333 /hauth/Hybrid/thirdparty | |
| parent | b7a3419327ecdbec488aef13da6da581547942e8 (diff) | |
| download | users-053aeba110edfb543b07fc58d462477b2b9c7de9.tar.gz users-053aeba110edfb543b07fc58d462477b2b9c7de9.tar.bz2 users-053aeba110edfb543b07fc58d462477b2b9c7de9.zip | |
integrate HybridAuth signle sign on library
Diffstat (limited to 'hauth/Hybrid/thirdparty')
| -rw-r--r-- | hauth/Hybrid/thirdparty/Amazon/AmazonOAuth2Client.php | 125 | ||||
| -rw-r--r-- | hauth/Hybrid/thirdparty/OAuth/OAuth.php | 901 | ||||
| -rw-r--r-- | hauth/Hybrid/thirdparty/OAuth/OAuth1Client.php | 264 | ||||
| -rw-r--r-- | hauth/Hybrid/thirdparty/OAuth/OAuth2Client.php | 302 | ||||
| -rw-r--r-- | hauth/Hybrid/thirdparty/OpenID/LightOpenID.php | 1051 | ||||
| -rw-r--r-- | hauth/Hybrid/thirdparty/Paypal/PaypalOAuth2Client.php | 142 | ||||
| -rw-r--r-- | hauth/Hybrid/thirdparty/index.html | 10 |
7 files changed, 2795 insertions, 0 deletions
diff --git a/hauth/Hybrid/thirdparty/Amazon/AmazonOAuth2Client.php b/hauth/Hybrid/thirdparty/Amazon/AmazonOAuth2Client.php new file mode 100644 index 0000000..9c8a363 --- /dev/null +++ b/hauth/Hybrid/thirdparty/Amazon/AmazonOAuth2Client.php @@ -0,0 +1,125 @@ +<?php +/** +* HybridAuth +* http://hybridauth.sourceforge.net | http://github.com/hybridauth/hybridauth +* (c) 2009-2015, HybridAuth authors | http://hybridauth.sourceforge.net/licenses.html +*/ + +/** + * A service client for the Amazon ID OAuth 2 flow. + * + * The sole purpose of this subclass is to make sure the POST params + * for cURL are provided as an urlencoded string rather than an array. + * This is because Amazon requires COntent-Type header to be application/x-www-form-urlencoded, + * which cURL overrides to multipart/form-data when POST fields are provided as an array + * + * The only difference from Oauth2CLient in authenticate() method is http_build_query() + * wrapped around $params. request() and parseRequestResult() methods are exact copies + * from Oauth2Client. They are copied here because private scope does not allow calling them + * from subclass. + * + * @link http://stackoverflow.com/questions/5224790/curl-post-format-for-curlopt-postfields + * + */ +class AmazonOAuth2Client extends OAuth2Client { + + public function authenticate( $code ) { + + $params = array( + "client_id" => $this->client_id, + "client_secret" => $this->client_secret, + "grant_type" => 'authorization_code', + "redirect_uri" => $this->redirect_uri, + "code" => $code, + ); + + $response = $this->request( $this->token_url, http_build_query($params), $this->curl_authenticate_method ); + + $response = $this->parseRequestResult( $response ); + + if ( ! $response || ! isset( $response->access_token ) ){ + throw new Exception( "The Authorization Service has return: " . $response->error ); + } + + if( isset( $response->access_token ) ) $this->access_token = $response->access_token; + if( isset( $response->refresh_token ) ) $this->refresh_token = $response->refresh_token; + if( isset( $response->expires_in ) ) $this->access_token_expires_in = $response->expires_in; + + // calculate when the access token expire + if( isset($response->expires_in)) { + $this->access_token_expires_at = time() + $response->expires_in; + } + + return $response; + } + + private function request( $url, $params=false, $type="GET" ) + { + Hybrid_Logger::info( "Enter OAuth2Client::request( $url )" ); + Hybrid_Logger::debug( "OAuth2Client::request(). dump request params: ", serialize( $params ) ); + + if( $type == "GET" ){ + $url = $url . ( strpos( $url, '?' ) ? '&' : '?' ) . http_build_query($params, '', '&'); + } + + $this->http_info = array(); + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL , $url ); + curl_setopt($ch, CURLOPT_RETURNTRANSFER , 1 ); + curl_setopt($ch, CURLOPT_TIMEOUT , $this->curl_time_out ); + curl_setopt($ch, CURLOPT_USERAGENT , $this->curl_useragent ); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , $this->curl_connect_time_out ); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER , $this->curl_ssl_verifypeer ); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST , $this->curl_ssl_verifyhost ); + curl_setopt($ch, CURLOPT_HTTPHEADER , $this->curl_header ); + + if ($this->curl_compressed){ + curl_setopt($ch, CURLOPT_ENCODING, "gzip,deflate"); + } + + if($this->curl_proxy){ + curl_setopt( $ch, CURLOPT_PROXY , $this->curl_proxy); + } + + if( $type == "POST" ){ + curl_setopt($ch, CURLOPT_POST, 1); + if($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); + } + if( $type == "DELETE" ){ + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); + } + if( $type == "PATCH" ){ + curl_setopt($ch, CURLOPT_POST, 1); + if($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH"); + } + $response = curl_exec($ch); + if( $response === false ) { + Hybrid_Logger::error( "OAuth2Client::request(). curl_exec error: ", curl_error($ch) ); + } + Hybrid_Logger::debug( "OAuth2Client::request(). dump request info: ", serialize( curl_getinfo($ch) ) ); + Hybrid_Logger::debug( "OAuth2Client::request(). dump request result: ", serialize( $response ) ); + + $this->http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $this->http_info = array_merge($this->http_info, curl_getinfo($ch)); + + curl_close ($ch); + + return $response; + } + + private function parseRequestResult( $result ) + { + if( json_decode( $result ) ) return json_decode( $result ); + + parse_str( $result, $output ); + + $result = new StdClass(); + + foreach( $output as $k => $v ) + $result->$k = $v; + + return $result; + } +} diff --git a/hauth/Hybrid/thirdparty/OAuth/OAuth.php b/hauth/Hybrid/thirdparty/OAuth/OAuth.php new file mode 100644 index 0000000..e09d77c --- /dev/null +++ b/hauth/Hybrid/thirdparty/OAuth/OAuth.php @@ -0,0 +1,901 @@ +<?php
+// http://oauth.googlecode.com/svn/code/php/OAuth.php
+// rev 1276, July 4, 2014
+
+// vim: foldmethod=marker
+
+/* Generic exception class
+ */
+if (!class_exists('OAuthException', false)) {
+ class OAuthException extends Exception {
+ // pass
+ }
+}
+
+class OAuthConsumer {
+ public $key;
+ public $secret;
+
+ function __construct($key, $secret, $callback_url=null) {
+ $this->key = $key;
+ $this->secret = $secret;
+ $this->callback_url = $callback_url;
+ }
+
+ function __toString() {
+ return "OAuthConsumer[key=$this->key,secret=$this->secret]";
+ }
+}
+
+class OAuthToken {
+ // access tokens and request tokens
+ public $key;
+ public $secret;
+
+ /**
+ * key = the token
+ * secret = the token secret
+ */
+ function __construct($key, $secret) {
+ $this->key = $key;
+ $this->secret = $secret;
+ }
+
+ /**
+ * generates the basic string serialization of a token that a server
+ * would respond to request_token and access_token calls with
+ */
+ function to_string() {
+ return "oauth_token=" .
+ OAuthUtil::urlencode_rfc3986($this->key) .
+ "&oauth_token_secret=" .
+ OAuthUtil::urlencode_rfc3986($this->secret);
+ }
+
+ function __toString() {
+ return $this->to_string();
+ }
+}
+
+/**
+ * A class for implementing a Signature Method
+ * See section 9 ("Signing Requests") in the spec
+ */
+abstract class OAuthSignatureMethod {
+ /**
+ * Needs to return the name of the Signature Method (ie HMAC-SHA1)
+ * @return string
+ */
+ abstract public function get_name();
+
+ /**
+ * Build up the signature
+ * NOTE: The output of this function MUST NOT be urlencoded.
+ * the encoding is handled in OAuthRequest when the final
+ * request is serialized
+ * @param OAuthRequest $request
+ * @param OAuthConsumer $consumer
+ * @param OAuthToken $token
+ * @return string
+ */
+ abstract public function build_signature($request, $consumer, $token);
+
+ /**
+ * Verifies that a given signature is correct
+ * @param OAuthRequest $request
+ * @param OAuthConsumer $consumer
+ * @param OAuthToken $token
+ * @param string $signature
+ * @return bool
+ */
+ public function check_signature($request, $consumer, $token, $signature) {
+ $built = $this->build_signature($request, $consumer, $token);
+
+ // Check for zero length, although unlikely here
+ if (strlen($built) == 0 || strlen($signature) == 0) {
+ return false;
+ }
+
+ if (strlen($built) != strlen($signature)) {
+ return false;
+ }
+
+ // Avoid a timing leak with a (hopefully) time insensitive compare
+ $result = 0;
+ for ($i = 0; $i < strlen($signature); $i++) {
+ $result |= ord($built{$i}) ^ ord($signature{$i});
+ }
+
+ return $result == 0;
+ }
+}
+
+/**
+ * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
+ * where the Signature Base String is the text and the key is the concatenated values (each first
+ * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
+ * character (ASCII code 38) even if empty.
+ * - Chapter 9.2 ("HMAC-SHA1")
+ */
+class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
+ function get_name() {
+ return "HMAC-SHA1";
+ }
+
+ public function build_signature($request, $consumer, $token) {
+ $base_string = $request->get_signature_base_string();
+ $request->base_string = $base_string;
+
+ $key_parts = array(
+ $consumer->secret,
+ ($token) ? $token->secret : ""
+ );
+
+ $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
+ $key = implode('&', $key_parts);
+
+ return base64_encode(hash_hmac('sha1', $base_string, $key, true));
+ }
+}
+
+/**
+ * The PLAINTEXT method does not provide any security protection and SHOULD only be used
+ * over a secure channel such as HTTPS. It does not use the Signature Base String.
+ * - Chapter 9.4 ("PLAINTEXT")
+ */
+class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
+ public function get_name() {
+ return "PLAINTEXT";
+ }
+
+ /**
+ * oauth_signature is set to the concatenated encoded values of the Consumer Secret and
+ * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
+ * empty. The result MUST be encoded again.
+ * - Chapter 9.4.1 ("Generating Signatures")
+ *
+ * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
+ * OAuthRequest handles this!
+ */
+ public function build_signature($request, $consumer, $token) {
+ $key_parts = array(
+ $consumer->secret,
+ ($token) ? $token->secret : ""
+ );
+
+ $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
+ $key = implode('&', $key_parts);
+ $request->base_string = $key;
+
+ return $key;
+ }
+}
+
+/**
+ * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
+ * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
+ * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
+ * verified way to the Service Provider, in a manner which is beyond the scope of this
+ * specification.
+ * - Chapter 9.3 ("RSA-SHA1")
+ */
+abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
+ public function get_name() {
+ return "RSA-SHA1";
+ }
+
+ // Up to the SP to implement this lookup of keys. Possible 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
+ protected abstract function fetch_public_cert(&$request);
+
+ // Up to the SP to implement this lookup of keys. Possible 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
+ protected abstract function fetch_private_cert(&$request);
+
+ public function build_signature($request, $consumer, $token) {
+ $base_string = $request->get_signature_base_string();
+ $request->base_string = $base_string;
+
+ // 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
+ $ok = openssl_sign($base_string, $signature, $privatekeyid);
+
+ // Release the key resource
+ openssl_free_key($privatekeyid);
+
+ return base64_encode($signature);
+ }
+
+ public function check_signature($request, $consumer, $token, $signature) {
+ $decoded_sig = base64_decode($signature);
+
+ $base_string = $request->get_signature_base_string();
+
+ // 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;
+ }
+}
+
+class OAuthRequest {
+ protected $parameters;
+ protected $http_method;
+ protected $http_url;
+ // for debug purposes
+ public $base_string;
+ public static $version = '1.0';
+ public static $POST_INPUT = 'php://input';
+
+ function __construct($http_method, $http_url, $parameters=null) {
+ $parameters = ($parameters) ? $parameters : array();
+ $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
+ $this->parameters = $parameters;
+ $this->http_method = $http_method;
+ $this->http_url = $http_url;
+ }
+
+
+ /**
+ * attempt to build up a request from what was passed to the server
+ */
+ public static function from_request($http_method=null, $http_url=null, $parameters=null) {
+ $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
+ ? 'http'
+ : 'https';
+ if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
+ $scheme = $_SERVER['HTTP_X_FORWARDED_PROTO'];
+ }
+ $http_url = ($http_url) ? $http_url : $scheme .
+ '://' . $_SERVER['SERVER_NAME'] .
+ ':' .
+ $_SERVER['SERVER_PORT'] .
+ $_SERVER['REQUEST_URI'];
+ $http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD'];
+
+ // We weren't handed any parameters, so let's find the ones relevant to
+ // this request.
+ // If you run XML-RPC or similar you should use this to provide your own
+ // parsed parameter-list
+ if (!$parameters) {
+ // Find request headers
+ $request_headers = OAuthUtil::get_headers();
+
+ // Parse the query-string to find GET parameters
+ $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
+
+ // It's a POST request of the proper content-type, so parse POST
+ // parameters and add those overriding any duplicates from GET
+ if ($http_method == "POST"
+ && isset($request_headers['Content-Type'])
+ && strstr($request_headers['Content-Type'],
+ 'application/x-www-form-urlencoded')
+ ) {
+ $post_data = OAuthUtil::parse_parameters(
+ file_get_contents(self::$POST_INPUT)
+ );
+ $parameters = array_merge($parameters, $post_data);
+ }
+
+ // We have a Authorization-header with OAuth data. Parse the header
+ // and add those overriding any duplicates from GET or POST
+ if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'OAuth ') {
+ $header_parameters = OAuthUtil::split_header(
+ $request_headers['Authorization']
+ );
+ $parameters = array_merge($parameters, $header_parameters);
+ }
+
+ }
+
+ return new OAuthRequest($http_method, $http_url, $parameters);
+ }
+
+ /**
+ * pretty much a helper function to set up the request
+ */
+ public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=null) {
+ $parameters = ($parameters) ? $parameters : array();
+ $defaults = array("oauth_version" => OAuthRequest::$version,
+ "oauth_nonce" => OAuthRequest::generate_nonce(),
+ "oauth_timestamp" => OAuthRequest::generate_timestamp(),
+ "oauth_consumer_key" => $consumer->key);
+ if ($token)
+ $defaults['oauth_token'] = $token->key;
+
+ $parameters = array_merge($defaults, $parameters);
+
+ return new OAuthRequest($http_method, $http_url, $parameters);
+ }
+
+ public function set_parameter($name, $value, $allow_duplicates = true) {
+ if ($allow_duplicates && isset($this->parameters[$name])) {
+ // We have already added parameter(s) with this name, so add to the list
+ if (is_scalar($this->parameters[$name])) {
+ // This is the first duplicate, so transform scalar (string)
+ // into an array so we can add the duplicates
+ $this->parameters[$name] = array($this->parameters[$name]);
+ }
+
+ $this->parameters[$name][] = $value;
+ } else {
+ $this->parameters[$name] = $value;
+ }
+ }
+
+ public function get_parameter($name) {
+ return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
+ }
+
+ public function get_parameters() {
+ return $this->parameters;
+ }
+
+ public function unset_parameter($name) {
+ unset($this->parameters[$name]);
+ }
+
+ /**
+ * The request parameters, sorted and concatenated into a normalized string.
+ * @return string
+ */
+ public function get_signable_parameters() {
+ // Grab all parameters
+ $params = $this->parameters;
+
+ // Remove oauth_signature if present
+ // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
+ if (isset($params['oauth_signature'])) {
+ unset($params['oauth_signature']);
+ }
+
+ return OAuthUtil::build_http_query($params);
+ }
+
+ /**
+ * Returns the base string of this request
+ *
+ * The base string defined as the method, the url
+ * and the parameters (normalized), each urlencoded
+ * and the concated with &.
+ */
+ public function get_signature_base_string() {
+ $parts = array(
+ $this->get_normalized_http_method(),
+ $this->get_normalized_http_url(),
+ $this->get_signable_parameters()
+ );
+
+ $parts = OAuthUtil::urlencode_rfc3986($parts);
+
+ return implode('&', $parts);
+ }
+
+ /**
+ * just uppercases the http method
+ */
+ public function get_normalized_http_method() {
+ return strtoupper($this->http_method);
+ }
+
+ /**
+ * parses the url and rebuilds it to be
+ * scheme://host/path
+ */
+ public function get_normalized_http_url() {
+ $parts = parse_url($this->http_url);
+
+ $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
+ $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80');
+ $host = (isset($parts['host'])) ? strtolower($parts['host']) : '';
+ $path = (isset($parts['path'])) ? $parts['path'] : '';
+
+ if (($scheme == 'https' && $port != '443')
+ || ($scheme == 'http' && $port != '80')) {
+ $host = "$host:$port";
+ }
+ return "$scheme://$host$path";
+ }
+
+ /**
+ * builds a url usable for a GET request
+ */
+ public function to_url() {
+ $post_data = $this->to_postdata();
+ $out = $this->get_normalized_http_url();
+ if ($post_data) {
+ $out .= '?'.$post_data;
+ }
+ return $out;
+ }
+
+ /**
+ * builds the data one would send in a POST request
+ */
+ public function to_postdata() {
+ return OAuthUtil::build_http_query($this->parameters);
+ }
+
+ /**
+ * builds the Authorization: header
+ */
+ public function to_header($realm=null) {
+ $first = true;
+ if($realm) {
+ $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
+ $first = false;
+ } else
+ $out = 'Authorization: OAuth';
+
+ $total = array();
+ foreach ($this->parameters as $k => $v) {
+ if (substr($k, 0, 5) != "oauth") continue;
+ if (is_array($v)) {
+ throw new OAuthException('arrays not supported in headers');
+ }
+ $out .= ($first) ? ' ' : ',';
+ $out .= OAuthUtil::urlencode_rfc3986($k) .
+ '="' .
+ OAuthUtil::urlencode_rfc3986($v) .
+ '"';
+ $first = false;
+ }
+ return $out;
+ }
+
+ public function __toString() {
+ return $this->to_url();
+ }
+
+
+ public function sign_request($signature_method, $consumer, $token) {
+ $this->set_parameter(
+ "oauth_signature_method",
+ $signature_method->get_name(),
+ false
+ );
+ $signature = $this->build_signature($signature_method, $consumer, $token);
+ $this->set_parameter("oauth_signature", $signature, false);
+ }
+
+ public function build_signature($signature_method, $consumer, $token) {
+ $signature = $signature_method->build_signature($this, $consumer, $token);
+ return $signature;
+ }
+
+ /**
+ * util function: current timestamp
+ */
+ private static function generate_timestamp() {
+ return time();
+ }
+
+ /**
+ * util function: current nonce
+ */
+ private static function generate_nonce() {
+ $mt = microtime();
+ $rand = mt_rand();
+
+ return md5($mt . $rand); // md5s look nicer than numbers
+ }
+}
+
+class OAuthServer {
+ protected $timestamp_threshold = 300; // in seconds, five minutes
+ protected $version = '1.0'; // hi blaine
+ protected $signature_methods = array();
+
+ protected $data_store;
+
+ function __construct($data_store) {
+ $this->data_store = $data_store;
+ }
+
+ public function add_signature_method($signature_method) {
+ $this->signature_methods[$signature_method->get_name()] =
+ $signature_method;
+ }
+
+ // high level functions
+
+ /**
+ * process a request_token request
+ * returns the request token on success
+ */
+ public function fetch_request_token(&$request) {
+ $this->get_version($request);
+
+ $consumer = $this->get_consumer($request);
+
+ // no token required for the initial token request
+ $token = null;
+
+ $this->check_signature($request, $consumer, $token);
+
+ // Rev A change
+ $callback = $request->get_parameter('oauth_callback');
+ $new_token = $this->data_store->new_request_token($consumer, $callback);
+
+ return $new_token;
+ }
+
+ /**
+ * process an access_token request
+ * returns the access token on success
+ */
+ public function fetch_access_token(&$request) {
+ $this->get_version($request);
+
+ $consumer = $this->get_consumer($request);
+
+ // requires authorized request token
+ $token = $this->get_token($request, $consumer, "request");
+
+ $this->check_signature($request, $consumer, $token);
+
+ // Rev A change
+ $verifier = $request->get_parameter('oauth_verifier');
+ $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
+
+ return $new_token;
+ }
+
+ /**
+ * verify an api call, checks all the parameters
+ */
+ public function verify_request(&$request) {
+ $this->get_version($request);
+ $consumer = $this->get_consumer($request);
+ $token = $this->get_token($request, $consumer, "access");
+ $this->check_signature($request, $consumer, $token);
+ return array($consumer, $token);
+ }
+
+ // Internals from here
+ /**
+ * version 1
+ */
+ private function get_version(&$request) {
+ $version = $request->get_parameter("oauth_version");
+ if (!$version) {
+ // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
+ // Chapter 7.0 ("Accessing Protected Ressources")
+ $version = '1.0';
+ }
+ if ($version !== $this->version) {
+ throw new OAuthException("OAuth version '$version' not supported");
+ }
+ return $version;
+ }
+
+ /**
+ * figure out the signature with some defaults
+ */
+ private function get_signature_method($request) {
+ $signature_method = $request instanceof OAuthRequest
+ ? $request->get_parameter("oauth_signature_method")
+ : null;
+
+ if (!$signature_method) {
+ // According to chapter 7 ("Accessing Protected Ressources") the signature-method
+ // parameter is required, and we can't just fallback to PLAINTEXT
+ throw new OAuthException('No signature method parameter. This parameter is required');
+ }
+
+ if (!in_array($signature_method,
+ array_keys($this->signature_methods))) {
+ throw new OAuthException(
+ "Signature method '$signature_method' not supported " .
+ "try one of the following: " .
+ implode(", ", array_keys($this->signature_methods))
+ );
+ }
+ return $this->signature_methods[$signature_method];
+ }
+
+ /**
+ * try to find the consumer for the provided request's consumer key
+ */
+ private function get_consumer($request) {
+ $consumer_key = $request instanceof OAuthRequest
+ ? $request->get_parameter("oauth_consumer_key")
+ : null;
+
+ if (!$consumer_key) {
+ throw new OAuthException("Invalid consumer key");
+ }
+
+ $consumer = $this->data_store->lookup_consumer($consumer_key);
+ if (!$consumer) {
+ throw new OAuthException("Invalid consumer");
+ }
+
+ return $consumer;
+ }
+
+ /**
+ * try to find the token for the provided request's token key
+ */
+ private function get_token($request, $consumer, $token_type="access") {
+ $token_field = $request instanceof OAuthRequest
+ ? $request->get_parameter('oauth_token')
+ : null;
+
+ $token = $this->data_store->lookup_token(
+ $consumer, $token_type, $token_field
+ );
+ if (!$token) {
+ throw new OAuthException("Invalid $token_type token: $token_field");
+ }
+ return $token;
+ }
+
+ /**
+ * all-in-one function to check the signature on a request
+ * should guess the signature method appropriately
+ */
+ private function check_signature($request, $consumer, $token) {
+ // this should probably be in a different method
+ $timestamp = $request instanceof OAuthRequest
+ ? $request->get_parameter('oauth_timestamp')
+ : null;
+ $nonce = $request instanceof OAuthRequest
+ ? $request->get_parameter('oauth_nonce')
+ : null;
+
+ $this->check_timestamp($timestamp);
+ $this->check_nonce($consumer, $token, $nonce, $timestamp);
+
+ $signature_method = $this->get_signature_method($request);
+
+ $signature = $request->get_parameter('oauth_signature');
+ $valid_sig = $signature_method->check_signature(
+ $request,
+ $consumer,
+ $token,
+ $signature
+ );
+
+ if (!$valid_sig) {
+ throw new OAuthException("Invalid signature");
+ }
+ }
+
+ /**
+ * check that the timestamp is new enough
+ */
+ private function check_timestamp($timestamp) {
+ if( ! $timestamp )
+ throw new OAuthException(
+ 'Missing timestamp parameter. The parameter is required'
+ );
+
+ // verify that timestamp is recentish
+ $now = time();
+ if (abs($now - $timestamp) > $this->timestamp_threshold) {
+ throw new OAuthException(
+ "Expired timestamp, yours $timestamp, ours $now"
+ );
+ }
+ }
+
+ /**
+ * check that the nonce is not repeated
+ */
+ private function check_nonce($consumer, $token, $nonce, $timestamp) {
+ if( ! $nonce )
+ throw new OAuthException(
+ 'Missing nonce parameter. The parameter is required'
+ );
+
+ // verify that the nonce is uniqueish
+ $found = $this->data_store->lookup_nonce(
+ $consumer,
+ $token,
+ $nonce,
+ $timestamp
+ );
+ if ($found) {
+ throw new OAuthException("Nonce already used: $nonce");
+ }
+ }
+
+}
+
+class OAuthDataStore {
+ function lookup_consumer($consumer_key) {
+ // implement me
+ }
+
+ function lookup_token($consumer, $token_type, $token) {
+ // implement me
+ }
+
+ function lookup_nonce($consumer, $token, $nonce, $timestamp) {
+ // implement me
+ }
+
+ function new_request_token($consumer, $callback = null) {
+ // return a new token attached to this consumer
+ }
+
+ function new_access_token($token, $consumer, $verifier = null) {
+ // return a new access token attached to this consumer
+ // for the user associated with this token if the request token
+ // is authorized
+ // should also invalidate the request token
+ }
+
+}
+
+class OAuthUtil {
+ public static function urlencode_rfc3986($input) {
+ if (is_array($input)) {
+ return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input);
+ } else if (is_scalar($input)) {
+ return str_replace(
+ '+',
+ ' ',
+ str_replace('%7E', '~', rawurlencode($input))
+ );
+ } else {
+ return '';
+ }
+}
+
+
+ // This decode function isn't taking into consideration the above
+ // modifications to the encoding process. However, this method doesn't
+ // seem to be used anywhere so leaving it as is.
+ public static function urldecode_rfc3986($string) {
+ return urldecode($string);
+ }
+
+ // Utility function for turning the Authorization: header into
+ // parameters, has to do some unescaping
+ // Can filter out any non-oauth parameters if needed (default behaviour)
+ // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
+ // see http://code.google.com/p/oauth/issues/detail?id=163
+ public static function split_header($header, $only_allow_oauth_parameters = true) {
+ $params = array();
+ if (preg_match_all('/('.($only_allow_oauth_parameters ? 'oauth_' : '').'[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches)) {
+ foreach ($matches[1] as $i => $h) {
+ $params[$h] = OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]);
+ }
+ if (isset($params['realm'])) {
+ unset($params['realm']);
+ }
+ }
+ return $params;
+ }
+
+ // helper to try to sort out headers for people who aren't running apache
+ public static function get_headers() {
+ if (function_exists('apache_request_headers')) {
+ // we need this to get the actual Authorization: header
+ // because apache tends to tell us it doesn't exist
+ $headers = apache_request_headers();
+
+ // sanitize the output of apache_request_headers because
+ // we always want the keys to be Cased-Like-This and arh()
+ // returns the headers in the same case as they are in the
+ // request
+ $out = array();
+ foreach ($headers AS $key => $value) {
+ $key = str_replace(
+ " ",
+ "-",
+ ucwords(strtolower(str_replace("-", " ", $key)))
+ );
+ $out[$key] = $value;
+ }
+ } else {
+ // otherwise we don't have apache and are just going to have to hope
+ // that $_SERVER actually contains what we need
+ $out = array();
+ if( isset($_SERVER['CONTENT_TYPE']) )
+ $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
+ if( isset($_ENV['CONTENT_TYPE']) )
+ $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
+
+ foreach ($_SERVER as $key => $value) {
+ if (substr($key, 0, 5) == "HTTP_") {
+ // this is chaos, basically it is just there to capitalize the first
+ // letter of every word that is not an initial HTTP and strip HTTP
+ // code from przemek
+ $key = str_replace(
+ " ",
+ "-",
+ ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
+ );
+ $out[$key] = $value;
+ }
+ }
+ }
+ return $out;
+ }
+
+ // This function takes a input like a=b&a=c&d=e and returns the parsed
+ // parameters like this
+ // array('a' => array('b','c'), 'd' => 'e')
+ public static function parse_parameters( $input ) {
+ if (!isset($input) || !$input) return array();
+
+ $pairs = explode('&', $input);
+
+ $parsed_parameters = array();
+ foreach ($pairs as $pair) {
+ $split = explode('=', $pair, 2);
+ $parameter = OAuthUtil::urldecode_rfc3986($split[0]);
+ $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
+
+ if (isset($parsed_parameters[$parameter])) {
+ // We have already recieved parameter(s) with this name, so add to the list
+ // of parameters with this name
+
+ if (is_scalar($parsed_parameters[$parameter])) {
+ // This is the first duplicate, so transform scalar (string) into an array
+ // so we can add the duplicates
+ $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
+ }
+
+ $parsed_parameters[$parameter][] = $value;
+ } else {
+ $parsed_parameters[$parameter] = $value;
+ }
+ }
+ return $parsed_parameters;
+ }
+
+ public static function build_http_query($params) {
+ if (!$params) return '';
+
+ // Urlencode both keys and values
+ $keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
+ $values = OAuthUtil::urlencode_rfc3986(array_values($params));
+ $params = array_combine($keys, $values);
+
+ // Parameters are sorted by name, using lexicographical byte value ordering.
+ // Ref: Spec: 9.1.1 (1)
+ uksort($params, 'strcmp');
+
+ $pairs = array();
+ foreach ($params as $parameter => $value) {
+ if (is_array($value)) {
+ // If two or more parameters share the same name, they are sorted by their value
+ // Ref: Spec: 9.1.1 (1)
+ // June 12th, 2010 - changed to sort because of issue 164 by hidetaka
+ sort($value, SORT_STRING);
+ foreach ($value as $duplicate_value) {
+ $pairs[] = $parameter . '=' . $duplicate_value;
+ }
+ } else {
+ $pairs[] = $parameter . '=' . $value;
+ }
+ }
+ // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
+ // Each name-value pair is separated by an '&' character (ASCII code 38)
+ return implode('&', $pairs);
+ }
+}
diff --git a/hauth/Hybrid/thirdparty/OAuth/OAuth1Client.php b/hauth/Hybrid/thirdparty/OAuth/OAuth1Client.php new file mode 100644 index 0000000..64c03c8 --- /dev/null +++ b/hauth/Hybrid/thirdparty/OAuth/OAuth1Client.php @@ -0,0 +1,264 @@ +<?php +/** +* HybridAuth +* http://hybridauth.sourceforge.net | http://github.com/hybridauth/hybridauth +* (c) 2009-2014, HybridAuth authors | http://hybridauth.sourceforge.net/licenses.html +*/ + +// A service client for the OAuth 1/1.0a flow. +// v0.1 +class OAuth1Client{ + public $api_base_url = ""; + public $authorize_url = ""; + public $authenticate_url = ""; + public $request_token_url = ""; + public $access_token_url = ""; + + public $request_token_method = "GET"; + public $access_token_method = "GET"; + + public $redirect_uri = ""; + + public $decode_json = true; + public $curl_time_out = 30; + public $curl_connect_time_out = 30; + public $curl_ssl_verifypeer = false; + public $curl_auth_header = true; + public $curl_useragent = "OAuth/1 Simple PHP Client v0.1; HybridAuth http://hybridauth.sourceforge.net/"; + public $curl_proxy = null; + + //-- + + public $http_code = ""; + public $http_info = ""; + protected $response = null; + + /** + * OAuth client constructor + */ + function __construct( $consumer_key, $consumer_secret, $oauth_token = null, $oauth_token_secret = null ) + { + $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1(); + $this->consumer = new OAuthConsumer( $consumer_key, $consumer_secret ); + $this->token = null; + + if ( $oauth_token && $oauth_token_secret ){ + $this->token = new OAuthConsumer( $oauth_token, $oauth_token_secret ); + } + } + + /** + * Build authorize url + * + * @return string + */ + function authorizeUrl( $token, $extras =array() ) + { + if ( is_array( $token ) ){ + $token = $token['oauth_token']; + } + + $parameters = array( "oauth_token" => $token ); + + if( count($extras) ) + foreach( $extras as $k=>$v ) + $parameters[$k] = $v; + + return $this->authorize_url . "?" . http_build_query( $parameters ); + } + + /** + * Get a request_token from provider + * + * @return array a key/value array containing oauth_token and oauth_token_secret + */ + function requestToken( $callback = null ) + { + $parameters = array(); + + if ( $callback ) { + $this->redirect_uri = $parameters['oauth_callback'] = $callback; + } + + $request = $this->signedRequest( $this->request_token_url, $this->request_token_method, $parameters ); + $token = OAuthUtil::parse_parameters( $request ); + $this->token = new OAuthConsumer( $token['oauth_token'], $token['oauth_token_secret'] ); + + return $token; + } + + /** + * Exchange the request token and secret for an access token and secret, to sign API calls. + * + * @return array array('oauth_token' => the access token, 'oauth_token_secret' => the access secret) + */ + function accessToken( $oauth_verifier = false, $oauth_token = false ) + { + $parameters = array(); + + // 1.0a + if ( $oauth_verifier ) { + $parameters['oauth_verifier'] = $oauth_verifier; + } + + $request = $this->signedRequest( $this->access_token_url, $this->access_token_method, $parameters ); + $token = OAuthUtil::parse_parameters( $request ); + $this->token = new OAuthConsumer( $token['oauth_token'], $token['oauth_token_secret'] ); + + return $token; + } + + /** + * GET wrapper for provider apis request + */ + function get($url, $parameters = array(), $content_type = null) + { + return $this->api($url, 'GET', $parameters, null, $content_type); + } + + /** + * POST wrapper for provider apis request + */ + function post($url, $parameters = array(), $body = null, $content_type = null, $multipart = false) + { + return $this->api($url, 'POST', $parameters, $body, $content_type, $multipart ); + } + + /** + * Format and sign an oauth for provider api + */ + function api( $url, $method = 'GET', $parameters = array(), $body = null, $content_type = null, $multipart = false ) + { + if ( strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0 ) { + $url = $this->api_base_url . $url; + } + + $response = $this->signedRequest( $url, $method, $parameters, $body, $content_type, $multipart ); + + if( $this->decode_json ){ + $response = json_decode( $response ); + } + + return $this->response = $response; + } + + /** + * Return the response object afer the fact + * + * @return mixed + */ + public function getResponse() + { + return $this->response; + } + + /** + * Make signed request + */ + function signedRequest( $url, $method, $parameters, $body = null, $content_type = null, $multipart = false ) + { + + $signature_parameters = array(); + + // when making a multipart request, use only oauth_* keys for signature + foreach( $parameters AS $key => $value ){ + if( !$multipart || strpos( $key, 'oauth_' ) === 0 ){ + $signature_parameters[$key] = $value; + } + } + + $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $signature_parameters); + $request->sign_request($this->sha1_method, $this->consumer, $this->token); + switch ($method) { + case 'GET': return $this->request( $request->to_url(), 'GET', null, null, $content_type ); + default : + if ($body) + return $this->request( $request->to_url(), $method, $body, $request->to_header(), $content_type ); + else + return $this->request( $request->get_normalized_http_url(), $method, ($multipart ? $parameters : $request->to_postdata()), $request->to_header(), $content_type, $multipart ) ; + } + } + + /** + * Make http request + */ + function request( $url, $method, $postfields = null, $auth_header = null, $content_type = null, $multipart = false ) + { + Hybrid_Logger::info( "Enter OAuth1Client::request( $method, $url )" ); + Hybrid_Logger::debug( "OAuth1Client::request(). dump post fields: ", serialize( $postfields ) ); + + $this->http_info = array(); + $ci = curl_init(); + + /* Curl settings */ + curl_setopt( $ci, CURLOPT_USERAGENT , $this->curl_useragent ); + curl_setopt( $ci, CURLOPT_CONNECTTIMEOUT, $this->curl_connect_time_out ); + curl_setopt( $ci, CURLOPT_TIMEOUT , $this->curl_time_out ); + curl_setopt( $ci, CURLOPT_RETURNTRANSFER, true ); + curl_setopt( $ci, CURLOPT_HTTPHEADER , array('Expect:') ); + curl_setopt( $ci, CURLOPT_SSL_VERIFYPEER, $this->curl_ssl_verifypeer ); + curl_setopt( $ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader') ); + curl_setopt( $ci, CURLOPT_HEADER , false ); + + if( $multipart ){ + curl_setopt( $ci, CURLOPT_HTTPHEADER, array( 'Expect:', $auth_header ) ); + + }elseif ($content_type) + curl_setopt( $ci, CURLOPT_HTTPHEADER, array('Expect:', "Content-Type: $content_type") ); + + if($this->curl_proxy){ + curl_setopt( $ci, CURLOPT_PROXY , $this->curl_proxy); + } + + switch ($method){ + case 'POST': + curl_setopt( $ci, CURLOPT_POST, true ); + + if ( !empty($postfields) ){ + curl_setopt( $ci, CURLOPT_POSTFIELDS, $postfields ); + } + + if ( !empty($auth_header) && $this->curl_auth_header && !$multipart ){ + curl_setopt( $ci, CURLOPT_HTTPHEADER, array( 'Content-Type: application/atom+xml', $auth_header ) ); + } + break; + case 'DELETE': + curl_setopt( $ci, CURLOPT_CUSTOMREQUEST, 'DELETE' ); + if ( !empty($postfields) ){ + $url = "{$url}?{$postfields}"; + } + } + + curl_setopt($ci, CURLOPT_URL, $url); + $response = curl_exec($ci); + if( $response === false ) { + Hybrid_Logger::error( "OAuth1Client::request(). curl_exec error: ", curl_error($ci) ); + } + + + Hybrid_Logger::debug( "OAuth1Client::request(). dump request info: ", serialize( curl_getinfo($ci) ) ); + Hybrid_Logger::debug( "OAuth1Client::request(). dump request result: ", serialize( $response ) ); + + $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); + $this->http_info = array_merge($this->http_info, curl_getinfo($ci)); + + curl_close ($ci); + + return $response; + } + + /** + * Get the header info to store. + */ + function getHeader($ch, $header) { + $i = strpos($header, ':'); + + if ( !empty($i) ){ + $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); + $value = trim(substr($header, $i + 2)); + $this->http_header[$key] = $value; + } + + return strlen($header); + } +} diff --git a/hauth/Hybrid/thirdparty/OAuth/OAuth2Client.php b/hauth/Hybrid/thirdparty/OAuth/OAuth2Client.php new file mode 100644 index 0000000..0046d2c --- /dev/null +++ b/hauth/Hybrid/thirdparty/OAuth/OAuth2Client.php @@ -0,0 +1,302 @@ +<?php +/** +* HybridAuth +* http://hybridauth.sourceforge.net | http://github.com/hybridauth/hybridauth +* (c) 2009-2015, HybridAuth authors | http://hybridauth.sourceforge.net/licenses.html +*/ + +// A service client for the OAuth 2 flow. +// v0.1.1 +class OAuth2Client +{ + public $api_base_url = ""; + public $authorize_url = ""; + public $token_url = ""; + public $token_info_url = ""; + + public $client_id = "" ; + public $client_secret = "" ; + public $redirect_uri = "" ; + public $access_token = "" ; + public $refresh_token = "" ; + + public $access_token_expires_in = "" ; + public $access_token_expires_at = "" ; + + //-- + + public $sign_token_name = "access_token"; + public $curl_time_out = 30; + public $curl_connect_time_out = 30; + public $curl_ssl_verifypeer = false; + public $curl_ssl_verifyhost = false; + public $curl_header = array(); + public $curl_useragent = "OAuth/2 Simple PHP Client v0.1.1; HybridAuth http://hybridauth.sourceforge.net/"; + public $curl_authenticate_method = "POST"; + public $curl_proxy = null; + public $curl_compressed = false; + //-- + + public $http_code = ""; + public $http_info = ""; + protected $response = null; + + //-- + + public function __construct( $client_id = false, $client_secret = false, $redirect_uri='', $compressed = false ) + { + $this->client_id = $client_id; + $this->client_secret = $client_secret; + $this->redirect_uri = $redirect_uri; + $this->curl_compressed = $compressed; + } + + public function authorizeUrl( $extras = array() ) + { + $params = array( + "client_id" => $this->client_id, + "redirect_uri" => $this->redirect_uri, + "response_type" => "code" + ); + + if( count($extras) ) + foreach( $extras as $k=>$v ) + $params[$k] = $v; + + return $this->authorize_url . "?" . http_build_query($params, '', '&'); + } + + public function authenticate( $code ) + { + $params = array( + "client_id" => $this->client_id, + "client_secret" => $this->client_secret, + "grant_type" => "authorization_code", + "redirect_uri" => $this->redirect_uri, + "code" => $code + ); + + $response = $this->request( $this->token_url, $params, $this->curl_authenticate_method ); + + $response = $this->parseRequestResult( $response ); + + if( ! $response || ! isset( $response->access_token ) ){ + throw new Exception( "The Authorization Service has return: " . $response->error ); + } + + if( isset( $response->access_token ) ) $this->access_token = $response->access_token; + if( isset( $response->refresh_token ) ) $this->refresh_token = $response->refresh_token; + if( isset( $response->expires_in ) ) $this->access_token_expires_in = $response->expires_in; + + // calculate when the access token expire + if( isset($response->expires_in)) { + $this->access_token_expires_at = time() + $response->expires_in; + } + + return $response; + } + + public function authenticated() + { + if ( $this->access_token ){ + if ( $this->token_info_url && $this->refresh_token ){ + // check if this access token has expired, + $tokeninfo = $this->tokenInfo( $this->access_token ); + + // if yes, access_token has expired, then ask for a new one + if( $tokeninfo && isset( $tokeninfo->error ) ){ + $response = $this->refreshToken( $this->refresh_token ); + + // if wrong response + if( ! isset( $response->access_token ) || ! $response->access_token ){ + throw new Exception( "The Authorization Service has return an invalid response while requesting a new access token. given up!" ); + } + + // set new access_token + $this->access_token = $response->access_token; + } + } + + return true; + } + + return false; + } + + /** + * Format and sign an oauth for provider api + */ + public function api( $url, $method = "GET", $parameters = array(), $decode_json = true ) + { + if ( strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0 ) { + $url = $this->api_base_url . $url; + } + + $parameters[$this->sign_token_name] = $this->access_token; + $response = null; + + switch( $method ){ + case 'GET' : $response = $this->request( $url, $parameters, "GET" ); break; + case 'POST' : $response = $this->request( $url, $parameters, "POST" ); break; + case 'DELETE' : $response = $this->request( $url, $parameters, "DELETE" ); break; + case 'PATCH' : $response = $this->request( $url, $parameters, "PATCH" ); break; + } + + if( $response && $decode_json ){ + return $this->response = json_decode( $response ); + } + + return $this->response = $response; + } + + /** + * Return the response object afer the fact + * + * @return mixed + */ + public function getResponse() + { + return $this->response; + } + + /** + * GET wrapper for provider apis request + */ + function get( $url, $parameters = array(), $decode_json = true ) + { + return $this->api( $url, 'GET', $parameters, $decode_json ); + } + + /** + * POST wrapper for provider apis request + */ + function post( $url, $parameters = array(), $decode_json = true ) + { + return $this->api( $url, 'POST', $parameters, $decode_json ); + } + + // -- tokens + + public function tokenInfo($accesstoken) + { + $params['access_token'] = $this->access_token; + $response = $this->request( $this->token_info_url, $params ); + return $this->parseRequestResult( $response ); + } + + public function refreshToken( $parameters = array() ) + { + $params = array( + "client_id" => $this->client_id, + "client_secret" => $this->client_secret, + "grant_type" => "refresh_token" + ); + + foreach($parameters as $k=>$v ){ + $params[$k] = $v; + } + + $response = $this->request( $this->token_url, $params, "POST" ); + return $this->parseRequestResult( $response ); + } + + // -- utilities + + private function request( $url, $params=false, $type="GET" ) + { + Hybrid_Logger::info( "Enter OAuth2Client::request( $url )" ); + Hybrid_Logger::debug( "OAuth2Client::request(). dump request params: ", serialize( $params ) ); + + $urlEncodedParams = http_build_query($params, '', '&'); + + if( $type == "GET" ){ + $url = $url . ( strpos( $url, '?' ) ? '&' : '?' ) . $urlEncodedParams; + } + + $this->http_info = array(); + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL , $url ); + curl_setopt($ch, CURLOPT_RETURNTRANSFER , 1 ); + curl_setopt($ch, CURLOPT_TIMEOUT , $this->curl_time_out ); + curl_setopt($ch, CURLOPT_USERAGENT , $this->curl_useragent ); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , $this->curl_connect_time_out ); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER , $this->curl_ssl_verifypeer ); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST , $this->curl_ssl_verifyhost ); + curl_setopt($ch, CURLOPT_HTTPHEADER , $this->curl_header ); + + if ($this->curl_compressed){ + curl_setopt($ch, CURLOPT_ENCODING, "gzip,deflate"); + } + + if($this->curl_proxy){ + curl_setopt( $ch, CURLOPT_PROXY , $this->curl_proxy); + } + + if ($type == "POST") { + curl_setopt($ch, CURLOPT_POST, 1); + + // If request body exists then encode it for "application/json". + if (isset($params['body'])) { + $urlEncodedParams = json_encode($params['body']); + } + + // Using URL encoded params here instead of a more convenient array + // cURL will set a wrong HTTP Content-Type header if using an array (cf. http://www.php.net/manual/en/function.curl-setopt.php, Notes section for "CURLOPT_POSTFIELDS") + // OAuth requires application/x-www-form-urlencoded Content-Type (cf. https://tools.ietf.org/html/rfc6749#section-2.3.1) + if ($params) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $urlEncodedParams); + } + } + + if( $type == "DELETE" ){ + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); + } + if( $type == "PATCH" ){ + curl_setopt($ch, CURLOPT_POST, 1); + if($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH"); + } + $response = curl_exec($ch); + if( $response === false ) { + Hybrid_Logger::error( "OAuth2Client::request(). curl_exec error: ", curl_error($ch) ); + } + Hybrid_Logger::debug( "OAuth2Client::request(). dump request info: ", serialize( curl_getinfo($ch) ) ); + Hybrid_Logger::debug( "OAuth2Client::request(). dump request result: ", serialize( $response ) ); + + $this->http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $this->http_info = array_merge($this->http_info, curl_getinfo($ch)); + + curl_close ($ch); + + return $response; + } + + private function parseRequestResult( $result ) + { + if( json_decode( $result ) ) return json_decode( $result ); + + parse_str( $result, $output ); + + $result = new StdClass(); + + foreach( $output as $k => $v ) + $result->$k = $v; + + return $result; + } + /** + * DELETE wrapper for provider apis request + */ + function delete( $url, $parameters = array() ) + { + return $this->api( $url, 'DELETE', $parameters ); + } + /** + * PATCH wrapper for provider apis request + */ + function patch( $url, $parameters = array() ) + { + return $this->api( $url, 'PATCH', $parameters ); + } +} diff --git a/hauth/Hybrid/thirdparty/OpenID/LightOpenID.php b/hauth/Hybrid/thirdparty/OpenID/LightOpenID.php new file mode 100644 index 0000000..a257d6c --- /dev/null +++ b/hauth/Hybrid/thirdparty/OpenID/LightOpenID.php @@ -0,0 +1,1051 @@ +<?php
+/**
+ * This class provides a simple interface for OpenID 1.1/2.0 authentication.
+ *
+ * It requires PHP >= 5.1.2 with cURL or HTTP/HTTPS stream wrappers enabled.
+ *
+ * @version v1.2.0 (2014-01-14)
+ * @link https://code.google.com/p/lightopenid/ Project URL
+ * @link https://github.com/iignatov/LightOpenID GitHub Repo
+ * @author Mewp <mewp151 at gmail dot com>
+ * @copyright Copyright (c) 2013 Mewp
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ */
+class LightOpenID
+{
+ public $returnUrl
+ , $required = array()
+ , $optional = array()
+ , $verify_peer = null
+ , $capath = null
+ , $cainfo = null
+ , $cnmatch = null
+ , $data
+ , $oauth = array()
+ , $curl_time_out = 30
+ , $curl_connect_time_out = 30;
+ private $identity, $claimed_id;
+ protected $server, $version, $trustRoot, $aliases, $identifier_select = false
+ , $ax = false, $sreg = false, $setup_url = null, $headers = array()
+ , $proxy = null, $user_agent = 'LightOpenID'
+ , $xrds_override_pattern = null, $xrds_override_replacement = null;
+ static protected $ax_to_sreg = array(
+ 'namePerson/friendly' => 'nickname',
+ 'contact/email' => 'email',
+ 'namePerson' => 'fullname',
+ 'birthDate' => 'dob',
+ 'person/gender' => 'gender',
+ 'contact/postalCode/home' => 'postcode',
+ 'contact/country/home' => 'country',
+ 'pref/language' => 'language',
+ 'pref/timezone' => 'timezone',
+ );
+
+ function __construct($host, $proxy = null)
+ {
+ $this->set_realm($host);
+ $this->set_proxy($proxy);
+
+ $uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?');
+ $this->returnUrl = $this->trustRoot . $uri;
+
+ $this->data = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET;
+
+ if(!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) {
+ throw new ErrorException('You must have either https wrappers or curl enabled.');
+ }
+ }
+
+ function __isset($name)
+ {
+ return in_array($name, array('identity', 'trustRoot', 'realm', 'xrdsOverride', 'mode'));
+ }
+
+ function __set($name, $value)
+ {
+ switch ($name) {
+ case 'identity':
+ if (strlen($value = trim((String) $value))) {
+ if (preg_match('#^xri:/*#i', $value, $m)) {
+ $value = substr($value, strlen($m[0]));
+ } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) {
+ $value = "http://$value";
+ }
+ if (preg_match('#^https?://[^/]+$#i', $value, $m)) {
+ $value .= '/';
+ }
+ }
+ $this->$name = $this->claimed_id = $value;
+ break;
+ case 'trustRoot':
+ case 'realm':
+ $this->trustRoot = trim($value);
+ break;
+ case 'xrdsOverride':
+ if (is_array($value)) {
+ list($pattern, $replacement) = $value;
+ $this->xrds_override_pattern = $pattern;
+ $this->xrds_override_replacement = $replacement;
+ } else {
+ trigger_error('Invalid value specified for "xrdsOverride".', E_USER_ERROR);
+ }
+ break;
+ }
+ }
+
+ function __get($name)
+ {
+ switch ($name) {
+ case 'identity':
+ # We return claimed_id instead of identity,
+ # because the developer should see the claimed identifier,
+ # i.e. what he set as identity, not the op-local identifier (which is what we verify)
+ return $this->claimed_id;
+ case 'trustRoot':
+ case 'realm':
+ return $this->trustRoot;
+ case 'mode':
+ return empty($this->data['openid_mode']) ? null : $this->data['openid_mode'];
+ }
+ }
+
+ function set_proxy($proxy)
+ {
+ if (!empty($proxy)) {
+ // When the proxy is a string - try to parse it.
+ if (!is_array($proxy)) {
+ $proxy = parse_url($proxy);
+ }
+
+ // Check if $proxy is valid after the parsing.
+ if ($proxy && !empty($proxy['host'])) {
+ // Make sure that a valid port number is specified.
+ if (array_key_exists('port', $proxy)) {
+ if (!is_int($proxy['port'])) {
+ $proxy['port'] = is_numeric($proxy['port']) ? intval($proxy['port']) : 0;
+ }
+
+ if ($proxy['port'] <= 0) {
+ throw new ErrorException('The specified proxy port number is invalid.');
+ }
+ }
+
+ $this->proxy = $proxy;
+ }
+ }
+ }
+
+ /**
+ * Checks if the server specified in the url exists.
+ *
+ * @param $url url to check
+ * @return true, if the server exists; false otherwise
+ */
+ function hostExists($url)
+ {
+ if (strpos($url, '/') === false) {
+ $server = $url;
+ } else {
+ $server = @parse_url($url, PHP_URL_HOST);
+ }
+
+ if (!$server) {
+ return false;
+ }
+
+ return !!gethostbynamel($server);
+ }
+
+ protected function set_realm($uri)
+ {
+ $realm = '';
+
+ # Set a protocol, if not specified.
+ $realm .= (($offset = strpos($uri, '://')) === false) ? $this->get_realm_protocol() : '';
+
+ # Set the offset properly.
+ $offset = (($offset !== false) ? $offset + 3 : 0);
+
+ # Get only the root, without the path.
+ $realm .= (($end = strpos($uri, '/', $offset)) === false) ? $uri : substr($uri, 0, $end);
+
+ $this->trustRoot = $realm;
+ }
+
+ protected function get_realm_protocol()
+ {
+ if (!empty($_SERVER['HTTPS'])) {
+ $use_secure_protocol = ($_SERVER['HTTPS'] != 'off');
+ } else if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
+ $use_secure_protocol = ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https');
+ } else {
+ $use_secure_protocol = false;
+ }
+
+ return $use_secure_protocol ? 'https://' : 'http://';
+ }
+
+ protected function request_curl($url, $method='GET', $params=array(), $update_claimed_id)
+ {
+ $params = http_build_query($params, '', '&');
+ $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : ''));
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_TIMEOUT, $this->curl_time_out);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT , $this->curl_connect_time_out);
+
+
+ if ($method == 'POST') {
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
+ } else {
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*'));
+ }
+
+ if (!empty($this->proxy)) {
+ curl_setopt($curl, CURLOPT_PROXY, $this->proxy['host']);
+
+ if (!empty($this->proxy['port'])) {
+ curl_setopt($curl, CURLOPT_PROXYPORT, $this->proxy['port']);
+ }
+
+ if (!empty($this->proxy['user'])) {
+ curl_setopt($curl, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']);
+ }
+ }
+
+ if($this->verify_peer !== null) {
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer);
+ if($this->capath) {
+ curl_setopt($curl, CURLOPT_CAPATH, $this->capath);
+ }
+
+ if($this->cainfo) {
+ curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo);
+ }
+ }
+
+ if ($method == 'POST') {
+ curl_setopt($curl, CURLOPT_POST, true);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
+ } elseif ($method == 'HEAD') {
+ curl_setopt($curl, CURLOPT_HEADER, true);
+ curl_setopt($curl, CURLOPT_NOBODY, true);
+ } else {
+ curl_setopt($curl, CURLOPT_HEADER, true);
+ curl_setopt($curl, CURLOPT_HTTPGET, true);
+ }
+ $response = curl_exec($curl);
+
+ if($method == 'HEAD' && curl_getinfo($curl, CURLINFO_HTTP_CODE) == 405) {
+ curl_setopt($curl, CURLOPT_HTTPGET, true);
+ $response = curl_exec($curl);
+ $response = substr($response, 0, strpos($response, "\r\n\r\n"));
+ }
+
+ if($method == 'HEAD' || $method == 'GET') {
+ $header_response = $response;
+
+ # If it's a GET request, we want to only parse the header part.
+ if($method == 'GET') {
+ $header_response = substr($response, 0, strpos($response, "\r\n\r\n"));
+ }
+
+ $headers = array();
+ foreach(explode("\n", $header_response) as $header) {
+ $pos = strpos($header,':');
+ if ($pos !== false) {
+ $name = strtolower(trim(substr($header, 0, $pos)));
+ $headers[$name] = trim(substr($header, $pos+1));
+ }
+ }
+
+ if($update_claimed_id) {
+ # Update the claimed_id value in case of redirections.
+ $effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
+ # Ignore the fragment (some cURL versions don't handle it well).
+ if (strtok($effective_url, '#') != strtok($url, '#')) {
+ $this->identity = $this->claimed_id = $effective_url;
+ }
+ }
+
+ if($method == 'HEAD') {
+ return $headers;
+ } else {
+ $this->headers = $headers;
+ }
+ }
+
+ if (curl_errno($curl)) {
+ throw new ErrorException(curl_error($curl), curl_errno($curl));
+ }
+
+ return $response;
+ }
+
+ protected function parse_header_array($array, $update_claimed_id)
+ {
+ $headers = array();
+ foreach($array as $header) {
+ $pos = strpos($header,':');
+ if ($pos !== false) {
+ $name = strtolower(trim(substr($header, 0, $pos)));
+ $headers[$name] = trim(substr($header, $pos+1));
+
+ # Following possible redirections. The point is just to have
+ # claimed_id change with them, because the redirections
+ # are followed automatically.
+ # We ignore redirections with relative paths.
+ # If any known provider uses them, file a bug report.
+ if($name == 'location' && $update_claimed_id) {
+ if(strpos($headers[$name], 'http') === 0) {
+ $this->identity = $this->claimed_id = $headers[$name];
+ } elseif($headers[$name][0] == '/') {
+ $parsed_url = parse_url($this->claimed_id);
+ $this->identity =
+ $this->claimed_id = $parsed_url['scheme'] . '://'
+ . $parsed_url['host']
+ . $headers[$name];
+ }
+ }
+ }
+ }
+ return $headers;
+ }
+
+ protected function request_streams($url, $method='GET', $params=array(), $update_claimed_id)
+ {
+ if(!$this->hostExists($url)) {
+ throw new ErrorException("Could not connect to $url.", 404);
+ }
+
+ if (empty($this->cnmatch)) {
+ $this->cnmatch = parse_url($url, PHP_URL_HOST);
+ }
+
+ $params = http_build_query($params, '', '&');
+ switch($method) {
+ case 'GET':
+ $opts = array(
+ 'http' => array(
+ 'method' => 'GET',
+ 'header' => 'Accept: application/xrds+xml, */*',
+ 'user_agent' => $this->user_agent,
+ 'ignore_errors' => true,
+ ),
+ 'ssl' => array(
+ 'CN_match' => $this->cnmatch
+ )
+ );
+ $url = $url . ($params ? '?' . $params : '');
+ if (!empty($this->proxy)) {
+ $opts['http']['proxy'] = $this->proxy_url();
+ }
+ break;
+ case 'POST':
+ $opts = array(
+ 'http' => array(
+ 'method' => 'POST',
+ 'header' => 'Content-type: application/x-www-form-urlencoded',
+ 'user_agent' => $this->user_agent,
+ 'content' => $params,
+ 'ignore_errors' => true,
+ ),
+ 'ssl' => array(
+ 'CN_match' => $this->cnmatch
+ )
+ );
+ if (!empty($this->proxy)) {
+ $opts['http']['proxy'] = $this->proxy_url();
+ }
+ break;
+ case 'HEAD':
+ // We want to send a HEAD request, but since get_headers() doesn't
+ // accept $context parameter, we have to change the defaults.
+ $default = stream_context_get_options(stream_context_get_default());
+
+ // PHP does not reset all options. Instead, it just sets the options
+ // available in the passed array, therefore set the defaults manually.
+ $default += array(
+ 'http' => array(),
+ 'ssl' => array()
+ );
+ $default['http'] += array(
+ 'method' => 'GET',
+ 'header' => '',
+ 'user_agent' => '',
+ 'ignore_errors' => false
+ );
+ $default['ssl'] += array(
+ 'CN_match' => ''
+ );
+
+ $opts = array(
+ 'http' => array(
+ 'method' => 'HEAD',
+ 'header' => 'Accept: application/xrds+xml, */*',
+ 'user_agent' => $this->user_agent,
+ 'ignore_errors' => true,
+ ),
+ 'ssl' => array(
+ 'CN_match' => $this->cnmatch
+ )
+ );
+
+ // Enable validation of the SSL certificates.
+ if ($this->verify_peer) {
+ $default['ssl'] += array(
+ 'verify_peer' => false,
+ 'capath' => '',
+ 'cafile' => ''
+ );
+ $opts['ssl'] += array(
+ 'verify_peer' => true,
+ 'capath' => $this->capath,
+ 'cafile' => $this->cainfo
+ );
+ }
+
+ // Change the stream context options.
+ stream_context_get_default($opts);
+
+ $headers = get_headers($url . ($params ? '?' . $params : ''));
+
+ // Restore the stream context options.
+ stream_context_get_default($default);
+
+ if (!empty($headers)) {
+ if (intval(substr($headers[0], strlen('HTTP/1.1 '))) == 405) {
+ // The server doesn't support HEAD - emulate it with a GET.
+ $args = func_get_args();
+ $args[1] = 'GET';
+ call_user_func_array(array($this, 'request_streams'), $args);
+ $headers = $this->headers;
+ } else {
+ $headers = $this->parse_header_array($headers, $update_claimed_id);
+ }
+ } else {
+ $headers = array();
+ }
+
+ return $headers;
+ }
+
+ if ($this->verify_peer) {
+ $opts['ssl'] += array(
+ 'verify_peer' => true,
+ 'capath' => $this->capath,
+ 'cafile' => $this->cainfo
+ );
+ }
+
+ $context = stream_context_create ($opts);
+ $data = file_get_contents($url, false, $context);
+ # This is a hack for providers who don't support HEAD requests.
+ # It just creates the headers array for the last request in $this->headers.
+ if(isset($http_response_header)) {
+ $this->headers = $this->parse_header_array($http_response_header, $update_claimed_id);
+ }
+
+ return $data;
+ }
+
+ protected function request($url, $method='GET', $params=array(), $update_claimed_id=false)
+ {
+ $use_curl = false;
+
+ if (function_exists('curl_init')) {
+ if (!$use_curl) {
+ # When allow_url_fopen is disabled, PHP streams will not work.
+ $use_curl = !ini_get('allow_url_fopen');
+ }
+
+ if (!$use_curl) {
+ # When there is no HTTPS wrapper, PHP streams cannott be used.
+ $use_curl = !in_array('https', stream_get_wrappers());
+ }
+
+ if (!$use_curl) {
+ # With open_basedir or safe_mode set, cURL can't follow redirects.
+ $use_curl = !(ini_get('safe_mode') || ini_get('open_basedir'));
+ }
+ }
+
+ return
+ $use_curl
+ ? $this->request_curl($url, $method, $params, $update_claimed_id)
+ : $this->request_streams($url, $method, $params, $update_claimed_id);
+ }
+
+ protected function proxy_url()
+ {
+ $result = '';
+
+ if (!empty($this->proxy)) {
+ $result = $this->proxy['host'];
+
+ if (!empty($this->proxy['port'])) {
+ $result = $result . ':' . $this->proxy['port'];
+ }
+
+ if (!empty($this->proxy['user'])) {
+ $result = $this->proxy['user'] . ':' . $this->proxy['pass'] . '@' . $result;
+ }
+
+ $result = 'http://' . $result;
+ }
+
+ return $result;
+ }
+
+ protected function build_url($url, $parts)
+ {
+ if (isset($url['query'], $parts['query'])) {
+ $parts['query'] = $url['query'] . '&' . $parts['query'];
+ }
+
+ $url = $parts + $url;
+ $url = $url['scheme'] . '://'
+ . (empty($url['username'])?''
+ :(empty($url['password'])? "{$url['username']}@"
+ :"{$url['username']}:{$url['password']}@"))
+ . $url['host']
+ . (empty($url['port'])?'':":{$url['port']}")
+ . (empty($url['path'])?'':$url['path'])
+ . (empty($url['query'])?'':"?{$url['query']}")
+ . (empty($url['fragment'])?'':"#{$url['fragment']}");
+ return $url;
+ }
+
+ /**
+ * Helper function used to scan for <meta>/<link> tags and extract information
+ * from them
+ */
+ protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName)
+ {
+ preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1);
+ preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2);
+
+ $result = array_merge($matches1[1], $matches2[1]);
+ return empty($result)?false:$result[0];
+ }
+
+ /**
+ * Performs Yadis and HTML discovery. Normally not used.
+ * @param $url Identity URL.
+ * @return String OP Endpoint (i.e. OpenID provider address).
+ * @throws ErrorException
+ */
+ function discover($url)
+ {
+ if (!$url) throw new ErrorException('No identity supplied.');
+ # Use xri.net proxy to resolve i-name identities
+ if (!preg_match('#^https?:#', $url)) {
+ $url = "https://xri.net/$url";
+ }
+
+ # We save the original url in case of Yadis discovery failure.
+ # It can happen when we'll be lead to an XRDS document
+ # which does not have any OpenID2 services.
+ $originalUrl = $url;
+
+ # A flag to disable yadis discovery in case of failure in headers.
+ $yadis = true;
+
+ # Allows optional regex replacement of the URL, e.g. to use Google Apps
+ # as an OpenID provider without setting up XRDS on the domain hosting.
+ if (!is_null($this->xrds_override_pattern) && !is_null($this->xrds_override_replacement)) {
+ $url = preg_replace($this->xrds_override_pattern, $this->xrds_override_replacement, $url);
+ }
+
+ # We'll jump a maximum of 5 times, to avoid endless redirections.
+ for ($i = 0; $i < 5; $i ++) {
+ if ($yadis) {
+ $headers = $this->request($url, 'HEAD', array(), true);
+
+ $next = false;
+ if (isset($headers['x-xrds-location'])) {
+ $url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location'])));
+ $next = true;
+ }
+
+ if (isset($headers['content-type']) && $this->is_allowed_type($headers['content-type'])) {
+ # Found an XRDS document, now let's find the server, and optionally delegate.
+ $content = $this->request($url, 'GET');
+
+ preg_match_all('#<Service.*?>(.*?)</Service>#s', $content, $m);
+ foreach($m[1] as $content) {
+ $content = ' ' . $content; # The space is added, so that strpos doesn't return 0.
+
+ # OpenID 2
+ $ns = preg_quote('http://specs.openid.net/auth/2.0/', '#');
+ if(preg_match('#<Type>\s*'.$ns.'(server|signon)\s*</Type>#s', $content, $type)) {
+ if ($type[1] == 'server') $this->identifier_select = true;
+
+ preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
+ preg_match('#<(Local|Canonical)ID>(.*)</\1ID>#', $content, $delegate);
+ if (empty($server)) {
+ return false;
+ }
+ # Does the server advertise support for either AX or SREG?
+ $this->ax = (bool) strpos($content, '<Type>http://openid.net/srv/ax/1.0</Type>');
+ $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>')
+ || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
+
+ $server = $server[1];
+ if (isset($delegate[2])) $this->identity = trim($delegate[2]);
+ $this->version = 2;
+
+ $this->server = $server;
+ return $server;
+ }
+
+ # OpenID 1.1
+ $ns = preg_quote('http://openid.net/signon/1.1', '#');
+ if (preg_match('#<Type>\s*'.$ns.'\s*</Type>#s', $content)) {
+
+ preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
+ preg_match('#<.*?Delegate>(.*)</.*?Delegate>#', $content, $delegate);
+ if (empty($server)) {
+ return false;
+ }
+ # AX can be used only with OpenID 2.0, so checking only SREG
+ $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>')
+ || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
+
+ $server = $server[1];
+ if (isset($delegate[1])) $this->identity = $delegate[1];
+ $this->version = 1;
+
+ $this->server = $server;
+ return $server;
+ }
+ }
+
+ $next = true;
+ $yadis = false;
+ $url = $originalUrl;
+ $content = null;
+ break;
+ }
+ if ($next) continue;
+
+ # There are no relevant information in headers, so we search the body.
+ $content = $this->request($url, 'GET', array(), true);
+
+ if (isset($this->headers['x-xrds-location'])) {
+ $url = $this->build_url(parse_url($url), parse_url(trim($this->headers['x-xrds-location'])));
+ continue;
+ }
+
+ $location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content');
+ if ($location) {
+ $url = $this->build_url(parse_url($url), parse_url($location));
+ continue;
+ }
+ }
+
+ if (!$content) $content = $this->request($url, 'GET');
+
+ # At this point, the YADIS Discovery has failed, so we'll switch
+ # to openid2 HTML discovery, then fallback to openid 1.1 discovery.
+ $server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href');
+ $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href');
+ $this->version = 2;
+
+ if (!$server) {
+ # The same with openid 1.1
+ $server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href');
+ $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href');
+ $this->version = 1;
+ }
+
+ if ($server) {
+ # We found an OpenID2 OP Endpoint
+ if ($delegate) {
+ # We have also found an OP-Local ID.
+ $this->identity = $delegate;
+ }
+ $this->server = $server;
+ return $server;
+ }
+
+ throw new ErrorException("No OpenID Server found at $url", 404);
+ }
+ throw new ErrorException('Endless redirection!', 500);
+ }
+
+ protected function is_allowed_type($content_type) {
+ # Apparently, some providers return XRDS documents as text/html.
+ # While it is against the spec, allowing this here shouldn't break
+ # compatibility with anything.
+ $allowed_types = array('application/xrds+xml', 'text/html', 'text/xml');
+
+ foreach ($allowed_types as $type) {
+ if (strpos($content_type, $type) !== false) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected function sregParams()
+ {
+ $params = array();
+ # We always use SREG 1.1, even if the server is advertising only support for 1.0.
+ # That's because it's fully backwards compatibile with 1.0, and some providers
+ # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com
+ $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1';
+ if ($this->required) {
+ $params['openid.sreg.required'] = array();
+ foreach ($this->required as $required) {
+ if (!isset(self::$ax_to_sreg[$required])) continue;
+ $params['openid.sreg.required'][] = self::$ax_to_sreg[$required];
+ }
+ $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']);
+ }
+
+ if ($this->optional) {
+ $params['openid.sreg.optional'] = array();
+ foreach ($this->optional as $optional) {
+ if (!isset(self::$ax_to_sreg[$optional])) continue;
+ $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional];
+ }
+ $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']);
+ }
+ return $params;
+ }
+
+ protected function axParams()
+ {
+ $params = array();
+ if ($this->required || $this->optional) {
+ $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0';
+ $params['openid.ax.mode'] = 'fetch_request';
+ $this->aliases = array();
+ $counts = array();
+ $required = array();
+ $optional = array();
+ foreach (array('required','optional') as $type) {
+ foreach ($this->$type as $alias => $field) {
+ if (is_int($alias)) $alias = strtr($field, '/', '_');
+ $this->aliases[$alias] = 'http://axschema.org/' . $field;
+ if (empty($counts[$alias])) $counts[$alias] = 0;
+ $counts[$alias] += 1;
+ ${$type}[] = $alias;
+ }
+ }
+ foreach ($this->aliases as $alias => $ns) {
+ $params['openid.ax.type.' . $alias] = $ns;
+ }
+ foreach ($counts as $alias => $count) {
+ if ($count == 1) continue;
+ $params['openid.ax.count.' . $alias] = $count;
+ }
+
+ # Don't send empty ax.requied and ax.if_available.
+ # Google and possibly other providers refuse to support ax when one of these is empty.
+ if($required) {
+ $params['openid.ax.required'] = implode(',', $required);
+ }
+ if($optional) {
+ $params['openid.ax.if_available'] = implode(',', $optional);
+ }
+ }
+ return $params;
+ }
+
+ protected function authUrl_v1($immediate)
+ {
+ $returnUrl = $this->returnUrl;
+ # If we have an openid.delegate that is different from our claimed id,
+ # we need to somehow preserve the claimed id between requests.
+ # The simplest way is to just send it along with the return_to url.
+ if($this->identity != $this->claimed_id) {
+ $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id;
+ }
+
+ $params = array(
+ 'openid.return_to' => $returnUrl,
+ 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
+ 'openid.identity' => $this->identity,
+ 'openid.trust_root' => $this->trustRoot,
+ ) + $this->sregParams();
+
+ return $this->build_url(parse_url($this->server)
+ , array('query' => http_build_query($params, '', '&')));
+ }
+
+ protected function authUrl_v2($immediate)
+ {
+ $params = array(
+ 'openid.ns' => 'http://specs.openid.net/auth/2.0',
+ 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
+ 'openid.return_to' => $this->returnUrl,
+ 'openid.realm' => $this->trustRoot,
+ );
+
+ if ($this->ax) {
+ $params += $this->axParams();
+ }
+
+ if ($this->sreg) {
+ $params += $this->sregParams();
+ }
+
+ if (!$this->ax && !$this->sreg) {
+ # If OP doesn't advertise either SREG, nor AX, let's send them both
+ # in worst case we don't get anything in return.
+ $params += $this->axParams() + $this->sregParams();
+ }
+
+ if (!empty($this->oauth) && is_array($this->oauth)) {
+ $params['openid.ns.oauth'] = 'http://specs.openid.net/extensions/oauth/1.0';
+ $params['openid.oauth.consumer'] = str_replace(array('http://', 'https://'), '', $this->trustRoot);
+ $params['openid.oauth.scope'] = implode(' ', $this->oauth);
+ }
+
+ if ($this->identifier_select) {
+ $params['openid.identity'] = $params['openid.claimed_id']
+ = 'http://specs.openid.net/auth/2.0/identifier_select';
+ } else {
+ $params['openid.identity'] = $this->identity;
+ $params['openid.claimed_id'] = $this->claimed_id;
+ }
+
+ return $this->build_url(parse_url($this->server)
+ , array('query' => http_build_query($params, '', '&')));
+ }
+
+ /**
+ * Returns authentication url. Usually, you want to redirect your user to it.
+ * @return String The authentication url.
+ * @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1.
+ * @throws ErrorException
+ */
+ function authUrl($immediate = false)
+ {
+ if ($this->setup_url && !$immediate) return $this->setup_url;
+ if (!$this->server) $this->discover($this->identity);
+
+ if ($this->version == 2) {
+ return $this->authUrl_v2($immediate);
+ }
+ return $this->authUrl_v1($immediate);
+ }
+
+ /**
+ * Performs OpenID verification with the OP.
+ * @return Bool Whether the verification was successful.
+ * @throws ErrorException
+ */
+ function validate()
+ {
+ # If the request was using immediate mode, a failure may be reported
+ # by presenting user_setup_url (for 1.1) or reporting
+ # mode 'setup_needed' (for 2.0). Also catching all modes other than
+ # id_res, in order to avoid throwing errors.
+ if(isset($this->data['openid_user_setup_url'])) {
+ $this->setup_url = $this->data['openid_user_setup_url'];
+ return false;
+ }
+ if($this->mode != 'id_res') {
+ return false;
+ }
+
+ $this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity'];
+ $params = array(
+ 'openid.assoc_handle' => $this->data['openid_assoc_handle'],
+ 'openid.signed' => $this->data['openid_signed'],
+ 'openid.sig' => $this->data['openid_sig'],
+ );
+
+ if (isset($this->data['openid_ns'])) {
+ # We're dealing with an OpenID 2.0 server, so let's set an ns
+ # Even though we should know location of the endpoint,
+ # we still need to verify it by discovery, so $server is not set here
+ $params['openid.ns'] = 'http://specs.openid.net/auth/2.0';
+ } elseif (isset($this->data['openid_claimed_id'])
+ && $this->data['openid_claimed_id'] != $this->data['openid_identity']
+ ) {
+ # If it's an OpenID 1 provider, and we've got claimed_id,
+ # we have to append it to the returnUrl, like authUrl_v1 does.
+ $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?')
+ . 'openid.claimed_id=' . $this->claimed_id;
+ }
+
+ if ($this->data['openid_return_to'] != $this->returnUrl) {
+ # The return_to url must match the url of current request.
+ # I'm assuing that noone will set the returnUrl to something that doesn't make sense.
+ return false;
+ }
+
+ $server = $this->discover($this->claimed_id);
+
+ foreach (explode(',', $this->data['openid_signed']) as $item) {
+ # Checking whether magic_quotes_gpc is turned on, because
+ # the function may fail if it is. For example, when fetching
+ # AX namePerson, it might containg an apostrophe, which will be escaped.
+ # In such case, validation would fail, since we'd send different data than OP
+ # wants to verify. stripslashes() should solve that problem, but we can't
+ # use it when magic_quotes is off.
+ $value = $this->data['openid_' . str_replace('.','_',$item)];
+ $params['openid.' . $item] = function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() ? stripslashes($value) : $value;
+
+ }
+
+ $params['openid.mode'] = 'check_authentication';
+
+ $response = $this->request($server, 'POST', $params);
+
+ return preg_match('/is_valid\s*:\s*true/i', $response);
+ }
+
+ protected function getAxAttributes()
+ {
+ $result = array();
+
+ if ($alias = $this->getNamespaceAlias('http://openid.net/srv/ax/1.0', 'ax')) {
+ $prefix = 'openid_' . $alias;
+ $length = strlen('http://axschema.org/');
+
+ foreach (explode(',', $this->data['openid_signed']) as $key) {
+ $keyMatch = $alias . '.type.';
+
+ if (strncmp($key, $keyMatch, strlen($keyMatch)) !== 0) {
+ continue;
+ }
+
+ $key = substr($key, strlen($keyMatch));
+ $idv = $prefix . '_value_' . $key;
+ $idc = $prefix . '_count_' . $key;
+ $key = substr($this->getItem($prefix . '_type_' . $key), $length);
+
+ if (!empty($key)) {
+ if (($count = intval($this->getItem($idc))) > 0) {
+ $value = array();
+
+ for ($i = 1; $i <= $count; $i++) {
+ $value[] = $this->getItem($idv . '_' . $i);
+ }
+
+ $value = ($count == 1) ? reset($value) : $value;
+ } else {
+ $value = $this->getItem($idv);
+ }
+
+ if (!is_null($value)) {
+ $result[$key] = $value;
+ }
+ }
+ }
+ } else {
+ // No alias for the AX schema has been found,
+ // so there is no AX data in the OP's response.
+ }
+
+ return $result;
+ }
+
+ protected function getSregAttributes()
+ {
+ $attributes = array();
+ $sreg_to_ax = array_flip(self::$ax_to_sreg);
+ foreach (explode(',', $this->data['openid_signed']) as $key) {
+ $keyMatch = 'sreg.';
+ if (strncmp($key, $keyMatch, strlen($keyMatch)) !== 0) {
+ continue;
+ }
+ $key = substr($key, strlen($keyMatch));
+ if (!isset($sreg_to_ax[$key])) {
+ # The field name isn't part of the SREG spec, so we ignore it.
+ continue;
+ }
+ $attributes[$sreg_to_ax[$key]] = $this->data['openid_sreg_' . $key];
+ }
+ return $attributes;
+ }
+
+ /**
+ * Gets AX/SREG attributes provided by OP. should be used only after successful validaton.
+ * Note that it does not guarantee that any of the required/optional parameters will be present,
+ * or that there will be no other attributes besides those specified.
+ * In other words. OP may provide whatever information it wants to.
+ * * SREG names will be mapped to AX names.
+ * * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email'
+ * @see http://www.axschema.org/types/
+ */
+ function getAttributes()
+ {
+ if (isset($this->data['openid_ns'])
+ && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0'
+ ) { # OpenID 2.0
+ # We search for both AX and SREG attributes, with AX taking precedence.
+ return $this->getAxAttributes() + $this->getSregAttributes();
+ }
+ return $this->getSregAttributes();
+ }
+
+ /**
+ * Gets an OAuth request token if the OpenID+OAuth hybrid protocol has been used.
+ *
+ * In order to use the OpenID+OAuth hybrid protocol, you need to add at least one
+ * scope to the $openid->oauth array before you get the call to getAuthUrl(), e.g.:
+ * $openid->oauth[] = 'https://www.googleapis.com/auth/plus.me';
+ *
+ * Furthermore the registered consumer name must fit the OpenID realm.
+ * To register an OpenID consumer at Google use: https://www.google.com/accounts/ManageDomains
+ *
+ * @return string|bool OAuth request token on success, FALSE if no token was provided.
+ */
+ function getOAuthRequestToken()
+ {
+ $alias = $this->getNamespaceAlias('http://specs.openid.net/extensions/oauth/1.0');
+
+ return !empty($alias) ? $this->data['openid_' . $alias . '_request_token'] : false;
+ }
+
+ /**
+ * Gets the alias for the specified namespace, if it's present.
+ *
+ * @param string $namespace The namespace for which an alias is needed.
+ * @param string $hint Common alias of this namespace, used for optimization.
+ * @return string|null The namespace alias if found, otherwise - NULL.
+ */
+ private function getNamespaceAlias($namespace, $hint = null)
+ {
+ $result = null;
+
+ if (empty($hint) || $this->getItem('openid_ns_' . $hint) != $namespace) {
+ // The common alias is either undefined or points to
+ // some other extension - search for another alias..
+ $prefix = 'openid_ns_';
+ $length = strlen($prefix);
+
+ foreach ($this->data as $key => $val) {
+ if (strncmp($key, $prefix, $length) === 0 && $val === $namespace) {
+ $result = trim(substr($key, $length));
+ break;
+ }
+ }
+ } else {
+ $result = $hint;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Gets an item from the $data array by the specified id.
+ *
+ * @param string $id The id of the desired item.
+ * @return string|null The item if found, otherwise - NULL.
+ */
+ private function getItem($id)
+ {
+ return isset($this->data[$id]) ? $this->data[$id] : null;
+ }
+}
diff --git a/hauth/Hybrid/thirdparty/Paypal/PaypalOAuth2Client.php b/hauth/Hybrid/thirdparty/Paypal/PaypalOAuth2Client.php new file mode 100644 index 0000000..150c735 --- /dev/null +++ b/hauth/Hybrid/thirdparty/Paypal/PaypalOAuth2Client.php @@ -0,0 +1,142 @@ +<?php +/*! +* HybridAuth +* http://hybridauth.sourceforge.net | http://github.com/hybridauth/hybridauth +* (c) 2009-2012, HybridAuth authors | http://hybridauth.sourceforge.net/licenses.html +*/ + +// A service client for the OAuth 2 flow. +// v0.1 +class PaypalOAuth2Client extends OAuth2Client +{ + public $curl_header = array( + 'Accept: application/json', + 'Accept-Language: en_US', + ); + public $curl_useragent = "OAuth/2 Simple PHP Client v0.1; HybridAuth http://hybridauth.sourceforge.net/"; + public $curl_log; + + public function authenticate( $code ) + { + $params = array( + "grant_type" => "authorization_code", + "code" => $code, + "redirect_uri" => $this->redirect_uri, + ); + + $response = $this->request( $this->token_url, $params, $this->curl_authenticate_method ); + + $response = $this->parseRequestResult( $response ); + + if( ! $response || ! isset( $response->access_token ) ){ + throw new Exception( "The Authorization Service has return: " . $response->message ); + } + + if( isset( $response->access_token ) ) $this->access_token = $response->access_token; + if( isset( $response->refresh_token ) ) $this->refresh_token = $response->refresh_token; + if( isset( $response->expires_in ) ) $this->access_token_expires_in = $response->expires_in; + + // calculate when the access token expire + if( isset($response->expires_in)) { + $this->access_token_expires_at = time() + $response->expires_in; + } + + return $response; + } + + + // -- tokens + + public function tokenInfo($accesstoken) + { + $params['access_token'] = $this->access_token; + $response = $this->request( $this->token_info_url, $params, "POST" ); + return $this->parseRequestResult( $response ); + } + + public function refreshToken( $params = array() ) + { + $params = array( + "grant_type" => "refresh_token", + "refresh_token" => $this->refresh_token, + ); + $response = $this->request( $this->token_url, $params, "POST" ); + return $this->parseRequestResult( $response ); + } + + // -- utilities + + private function request( $url, $params=false, $type="GET" ) + { + $params = http_build_query($params, '', '&'); + Hybrid_Logger::info( "Enter OAuth2Client::request( $url )" ); + Hybrid_Logger::debug( "OAuth2Client::request(). dump request params: ", $params ); + + if( $type == "GET" ){ + $url = $url . ( strpos( $url, '?' ) ? '&' : '?' ) . $params; + } + + $this->http_info = array(); + $ch = curl_init(); + + $headers = $this->curl_header; + if($type == "POST" ){ + //$headers[] = 'Content-Type: application/x-www-form-urlencoded'; + } + + curl_setopt($ch, CURLOPT_URL , $url ); + curl_setopt($ch, CURLOPT_RETURNTRANSFER , 1 ); + curl_setopt($ch, CURLOPT_TIMEOUT , $this->curl_time_out ); + curl_setopt($ch, CURLOPT_USERAGENT , $this->curl_useragent ); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , $this->curl_connect_time_out ); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER , $this->curl_ssl_verifypeer ); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST , $this->curl_ssl_verifyhost ); + curl_setopt($ch, CURLOPT_HTTPHEADER , $headers ); + curl_setopt($ch, CURLOPT_USERPWD , $this->client_id.':'.$this->client_secret ); + // logging + if ($this->curl_log !== null) { + $fp = fopen($this->curl_log, 'a'); + curl_setopt($ch, CURLOPT_STDERR , $fp ); + curl_setopt($ch, CURLOPT_VERBOSE , 1 ); + } + + if($this->curl_proxy){ + curl_setopt( $ch, CURLOPT_PROXY , $this->curl_proxy); + } + + if( $type == "POST" ){ + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $params ); + } + + $response = curl_exec($ch); + if ($this->curl_log !== null) + fclose($fp); + if( $response === FALSE ) { + Hybrid_Logger::error( "OAuth2Client::request(). curl_exec error: ", curl_error($ch) ); + } + Hybrid_Logger::debug( "OAuth2Client::request(). dump request info: ", serialize( curl_getinfo($ch) ) ); + Hybrid_Logger::debug( "OAuth2Client::request(). dump request result: ", serialize( $response ) ); + + $this->http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $this->http_info = array_merge($this->http_info, curl_getinfo($ch)); + + curl_close ($ch); + + return $response; + } + + private function parseRequestResult( $result ) + { + if( json_decode( $result ) ) return json_decode( $result ); + + parse_str( $result, $output ); + + $result = new StdClass(); + + foreach( $output as $k => $v ) + $result->$k = $v; + + return $result; + } +} diff --git a/hauth/Hybrid/thirdparty/index.html b/hauth/Hybrid/thirdparty/index.html new file mode 100644 index 0000000..065d2da --- /dev/null +++ b/hauth/Hybrid/thirdparty/index.html @@ -0,0 +1,10 @@ +<html>
+<head>
+ <title>403 Forbidden</title>
+</head>
+<body>
+
+<p>Directory access is forbidden.</p>
+
+</body>
+</html>
\ No newline at end of file |
