|
|
|
@ -1,4 +1,5 @@ |
|
|
|
|
<?php |
|
|
|
|
|
|
|
|
|
/* For licensing terms, see /license.txt */ |
|
|
|
|
/** |
|
|
|
|
* OpenID login method |
|
|
|
@ -12,14 +13,23 @@ |
|
|
|
|
/** |
|
|
|
|
* Initialisation |
|
|
|
|
*/ |
|
|
|
|
require_once dirname(__FILE__).'../../inc/conf/auth.conf.php'; |
|
|
|
|
require_once api_get_path(CONFIGURATION_PATH) . 'auth.conf.php'; |
|
|
|
|
|
|
|
|
|
require_once 'openid.lib.php'; |
|
|
|
|
require_once 'xrds.lib.php'; |
|
|
|
|
|
|
|
|
|
function openid_form() |
|
|
|
|
{ |
|
|
|
|
return '<div class="menusection"><span class="menusectioncaption">'.get_lang('OpenIdAuthentication').'</span><form name="openid_login" method="post"><label for="openid_url">'.get_lang('OpenIDURL').' <a href="main/auth/openid/whatis.php" title="'.get_lang('OpenIDWhatIs').'">'.Display::return_icon('info3.gif',get_lang('Info')).'</a></label><input type="text" id="openid_url" name="openid_url" style="background: url(main/img/openid_small_logo.png) no-repeat; background-color: #fff; background-position: 0 50%; padding-left:18px;" value="http://"></input><input type="submit" name="openid_login" value="'.get_lang('Enter').'" /><br /><br /></form></div>'; |
|
|
|
|
function openid_form() { |
|
|
|
|
//get_lang('OpenIdAuthentication') |
|
|
|
|
$form = new FormValidator('openid_login', 'post', null, null, array('class' => 'form-vertical form_login')); |
|
|
|
|
$form->addElement('text', 'openid_url', array(get_lang('OpenIDURL'), Display::url(get_lang('OpenIDWhatIs'), 'main/auth/openid/whatis.php')), array('class' => 'openid_input')); |
|
|
|
|
$form->addElement('button', 'submit', get_lang('Login')); |
|
|
|
|
return $form->return_form(); |
|
|
|
|
/* |
|
|
|
|
return '<label for="openid_url">'.get_lang('OpenIDURL').' <a href="main/auth/openid/whatis.php" title="'.get_lang('OpenIDWhatIs').'">'.Display::return_icon('info3.gif',get_lang('Info')).'</a></label> |
|
|
|
|
<input type="text" id="openid_url" name="openid_url" style="background: url(main/img/openid_small_logo.png) no-repeat; background-color: #fff; background-position: 0 50%; padding-left:18px;" value="http://"></input> |
|
|
|
|
* <input type="submit" name="openid_login" value="'.get_lang('Enter').'" /><br /><br /></form></div>'; |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -32,46 +42,40 @@ function openid_form() |
|
|
|
|
* @param $claimed_id The OpenID to authenticate |
|
|
|
|
* @param $return_to The endpoint to return to from the OpenID Provider |
|
|
|
|
*/ |
|
|
|
|
function openid_begin($claimed_id, $return_to = '', $form_values = array()) |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
$claimed_id = _openid_normalize($claimed_id); |
|
|
|
|
|
|
|
|
|
$services = openid_discovery($claimed_id); |
|
|
|
|
if (count($services) == 0) { |
|
|
|
|
echo 'Sorry, that is not a valid OpenID. Please ensure you have spelled your ID correctly.'; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$op_endpoint = $services[0]['uri']; |
|
|
|
|
// Store the discovered endpoint in the session (so we don't have to rediscover). |
|
|
|
|
$_SESSION['openid_op_endpoint'] = $op_endpoint; |
|
|
|
|
// Store the claimed_id in the session (for handling delegation). |
|
|
|
|
$_SESSION['openid_claimed_id'] = $claimed_id; |
|
|
|
|
// Store the login form values so we can pass them to |
|
|
|
|
// user_exteral_login later. |
|
|
|
|
$_SESSION['openid_user_login_values'] = $form_values; |
|
|
|
|
|
|
|
|
|
// If bcmath is present, then create an association |
|
|
|
|
$assoc_handle = ''; |
|
|
|
|
if (function_exists('bcadd')) { |
|
|
|
|
$assoc_handle = openid_association($op_endpoint); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Now that there is an association created, move on |
|
|
|
|
// to request authentication from the IdP |
|
|
|
|
$identity = (!empty($services[0]['delegate'])) ? $services[0]['delegate'] : $claimed_id; |
|
|
|
|
if (isset($services[0]['types']) && is_array($services[0]['types']) && in_array(OPENID_NS_2_0 .'/server', $services[0]['types'])) { |
|
|
|
|
$identity = 'http://openid.net/identifier_select/2.0'; |
|
|
|
|
} |
|
|
|
|
$authn_request = openid_authentication_request($claimed_id, $identity, $return_to, $assoc_handle, $services[0]['version']); |
|
|
|
|
|
|
|
|
|
if ($services[0]['version'] == 2) { |
|
|
|
|
openid_redirect($op_endpoint, $authn_request); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
openid_redirect_http($op_endpoint, $authn_request); |
|
|
|
|
} |
|
|
|
|
function openid_begin($claimed_id, $return_to = '', $form_values = array()) { |
|
|
|
|
|
|
|
|
|
$claimed_id = _openid_normalize($claimed_id); |
|
|
|
|
$services = openid_discovery($claimed_id); |
|
|
|
|
if (count($services) == 0) { |
|
|
|
|
echo 'Sorry, that is not a valid OpenID. Please ensure you have spelled your ID correctly.'; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
$op_endpoint = $services[0]['uri']; |
|
|
|
|
// Store the discovered endpoint in the session (so we don't have to rediscover). |
|
|
|
|
$_SESSION['openid_op_endpoint'] = $op_endpoint; |
|
|
|
|
// Store the claimed_id in the session (for handling delegation). |
|
|
|
|
$_SESSION['openid_claimed_id'] = $claimed_id; |
|
|
|
|
// Store the login form values so we can pass them to |
|
|
|
|
// user_exteral_login later. |
|
|
|
|
$_SESSION['openid_user_login_values'] = $form_values; |
|
|
|
|
|
|
|
|
|
// If bcmath is present, then create an association |
|
|
|
|
$assoc_handle = ''; |
|
|
|
|
if (function_exists('bcadd')) { |
|
|
|
|
$assoc_handle = openid_association($op_endpoint); |
|
|
|
|
} |
|
|
|
|
// Now that there is an association created, move on |
|
|
|
|
// to request authentication from the IdP |
|
|
|
|
$identity = (!empty($services[0]['delegate'])) ? $services[0]['delegate'] : $claimed_id; |
|
|
|
|
if (isset($services[0]['types']) && is_array($services[0]['types']) && in_array(OPENID_NS_2_0 . '/server', $services[0]['types'])) { |
|
|
|
|
$identity = 'http://openid.net/identifier_select/2.0'; |
|
|
|
|
} |
|
|
|
|
$authn_request = openid_authentication_request($claimed_id, $identity, $return_to, $assoc_handle, $services[0]['version']); |
|
|
|
|
if ($services[0]['version'] == 2) { |
|
|
|
|
openid_redirect($op_endpoint, $authn_request); |
|
|
|
|
} else { |
|
|
|
|
openid_redirect_http($op_endpoint, $authn_request); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -83,29 +87,27 @@ function openid_begin($claimed_id, $return_to = '', $form_values = array()) |
|
|
|
|
* @return $response Response values for further processing with |
|
|
|
|
* $response['status'] set to one of 'success', 'failed' or 'cancel'. |
|
|
|
|
*/ |
|
|
|
|
function openid_complete($response) |
|
|
|
|
{ |
|
|
|
|
// Default to failed response |
|
|
|
|
$response['status'] = 'failed'; |
|
|
|
|
if (isset($_SESSION['openid_op_endpoint']) && isset($_SESSION['openid_claimed_id'])) { |
|
|
|
|
_openid_fix_post($response); |
|
|
|
|
$op_endpoint = $_SESSION['openid_op_endpoint']; |
|
|
|
|
$claimed_id = $_SESSION['openid_claimed_id']; |
|
|
|
|
unset($_SESSION['openid_op_endpoint']); |
|
|
|
|
unset($_SESSION['openid_claimed_id']); |
|
|
|
|
if (isset($response['openid.mode'])) { |
|
|
|
|
if ($response['openid.mode'] == 'cancel') { |
|
|
|
|
$response['status'] = 'cancel'; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
if (openid_verify_assertion($op_endpoint, $response)) { |
|
|
|
|
$response['openid.identity'] = $claimed_id; |
|
|
|
|
$response['status'] = 'success'; |
|
|
|
|
function openid_complete($response) { |
|
|
|
|
// Default to failed response |
|
|
|
|
$response['status'] = 'failed'; |
|
|
|
|
if (isset($_SESSION['openid_op_endpoint']) && isset($_SESSION['openid_claimed_id'])) { |
|
|
|
|
_openid_fix_post($response); |
|
|
|
|
$op_endpoint = $_SESSION['openid_op_endpoint']; |
|
|
|
|
$claimed_id = $_SESSION['openid_claimed_id']; |
|
|
|
|
unset($_SESSION['openid_op_endpoint']); |
|
|
|
|
unset($_SESSION['openid_claimed_id']); |
|
|
|
|
if (isset($response['openid.mode'])) { |
|
|
|
|
if ($response['openid.mode'] == 'cancel') { |
|
|
|
|
$response['status'] = 'cancel'; |
|
|
|
|
} else { |
|
|
|
|
if (openid_verify_assertion($op_endpoint, $response)) { |
|
|
|
|
$response['openid.identity'] = $claimed_id; |
|
|
|
|
$response['status'] = 'success'; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return $response; |
|
|
|
|
return $response; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -118,63 +120,61 @@ function openid_complete($response) |
|
|
|
|
*/ |
|
|
|
|
function openid_discovery($claimed_id) { |
|
|
|
|
|
|
|
|
|
$services = array(); |
|
|
|
|
$services = array(); |
|
|
|
|
|
|
|
|
|
$xrds_url = $claimed_id; |
|
|
|
|
if (_openid_is_xri($claimed_id)) { |
|
|
|
|
$xrds_url = 'http://xri.net/'. $claimed_id; |
|
|
|
|
} |
|
|
|
|
$url = @parse_url($xrds_url); |
|
|
|
|
if ($url['scheme'] == 'http' || $url['scheme'] == 'https') { |
|
|
|
|
// For regular URLs, try Yadis resolution first, then HTML-based discovery |
|
|
|
|
$headers = array('Accept' => 'application/xrds+xml'); |
|
|
|
|
//TODO |
|
|
|
|
$result = openid_http_request($xrds_url, $headers); |
|
|
|
|
|
|
|
|
|
if (!isset($result->error)) { |
|
|
|
|
if (isset($result->headers['Content-Type']) && preg_match("/application\/xrds\+xml/", $result->headers['Content-Type'])) { |
|
|
|
|
// Parse XML document to find URL |
|
|
|
|
$services = xrds_parse($result->data); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
$xrds_url = NULL; |
|
|
|
|
if (isset($result->headers['X-XRDS-Location'])) { |
|
|
|
|
$xrds_url = $result->headers['X-XRDS-Location']; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
// Look for meta http-equiv link in HTML head |
|
|
|
|
$xrds_url = _openid_meta_httpequiv('X-XRDS-Location', $result->data); |
|
|
|
|
} |
|
|
|
|
if (!empty($xrds_url)) { |
|
|
|
|
$headers = array('Accept' => 'application/xrds+xml'); |
|
|
|
|
//TODO |
|
|
|
|
$xrds_result = openid_http_request($xrds_url, $headers); |
|
|
|
|
if (!isset($xrds_result->error)) { |
|
|
|
|
$services = xrds_parse($xrds_result->data); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check for HTML delegation |
|
|
|
|
if (count($services) == 0) { |
|
|
|
|
// Look for 2.0 links |
|
|
|
|
$uri = _openid_link_href('openid2.provider', $result->data); |
|
|
|
|
$delegate = _openid_link_href('openid2.local_id', $result->data); |
|
|
|
|
$version = 2; |
|
|
|
|
|
|
|
|
|
// 1.0 links |
|
|
|
|
if (empty($uri)) { |
|
|
|
|
$uri = _openid_link_href('openid.server', $result->data); |
|
|
|
|
$delegate = _openid_link_href('openid.delegate', $result->data); |
|
|
|
|
$version = 1; |
|
|
|
|
} |
|
|
|
|
if (!empty($uri)) { |
|
|
|
|
$services[] = array('uri' => $uri, 'delegate' => $delegate, 'version' => $version); |
|
|
|
|
$xrds_url = $claimed_id; |
|
|
|
|
if (_openid_is_xri($claimed_id)) { |
|
|
|
|
$xrds_url = 'http://xri.net/' . $claimed_id; |
|
|
|
|
} |
|
|
|
|
$url = @parse_url($xrds_url); |
|
|
|
|
if ($url['scheme'] == 'http' || $url['scheme'] == 'https') { |
|
|
|
|
// For regular URLs, try Yadis resolution first, then HTML-based discovery |
|
|
|
|
$headers = array('Accept' => 'application/xrds+xml'); |
|
|
|
|
//TODO |
|
|
|
|
$result = openid_http_request($xrds_url, $headers); |
|
|
|
|
|
|
|
|
|
if (!isset($result->error)) { |
|
|
|
|
if (isset($result->headers['Content-Type']) && preg_match("/application\/xrds\+xml/", $result->headers['Content-Type'])) { |
|
|
|
|
// Parse XML document to find URL |
|
|
|
|
$services = xrds_parse($result->data); |
|
|
|
|
} else { |
|
|
|
|
$xrds_url = NULL; |
|
|
|
|
if (isset($result->headers['X-XRDS-Location'])) { |
|
|
|
|
$xrds_url = $result->headers['X-XRDS-Location']; |
|
|
|
|
} else { |
|
|
|
|
// Look for meta http-equiv link in HTML head |
|
|
|
|
$xrds_url = _openid_meta_httpequiv('X-XRDS-Location', $result->data); |
|
|
|
|
} |
|
|
|
|
if (!empty($xrds_url)) { |
|
|
|
|
$headers = array('Accept' => 'application/xrds+xml'); |
|
|
|
|
//TODO |
|
|
|
|
$xrds_result = openid_http_request($xrds_url, $headers); |
|
|
|
|
if (!isset($xrds_result->error)) { |
|
|
|
|
$services = xrds_parse($xrds_result->data); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check for HTML delegation |
|
|
|
|
if (count($services) == 0) { |
|
|
|
|
// Look for 2.0 links |
|
|
|
|
$uri = _openid_link_href('openid2.provider', $result->data); |
|
|
|
|
$delegate = _openid_link_href('openid2.local_id', $result->data); |
|
|
|
|
$version = 2; |
|
|
|
|
|
|
|
|
|
// 1.0 links |
|
|
|
|
if (empty($uri)) { |
|
|
|
|
$uri = _openid_link_href('openid.server', $result->data); |
|
|
|
|
$delegate = _openid_link_href('openid.delegate', $result->data); |
|
|
|
|
$version = 1; |
|
|
|
|
} |
|
|
|
|
if (!empty($uri)) { |
|
|
|
|
$services[] = array('uri' => $uri, 'delegate' => $delegate, 'version' => $version); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return $services; |
|
|
|
|
return $services; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -185,51 +185,50 @@ function openid_discovery($claimed_id) { |
|
|
|
|
* @return $assoc_handle The association handle. |
|
|
|
|
*/ |
|
|
|
|
function openid_association($op_endpoint) { |
|
|
|
|
//@todo Remove Old Associations: |
|
|
|
|
$openid_association = Database::get_main_table(TABLE_MAIN_OPENID_ASSOCIATION); |
|
|
|
|
$sql = "DELETE FROM $openid_association WHERE created + expires_in < '" . api_get_utc_datetime() . "'"; |
|
|
|
|
Database::query($sql); |
|
|
|
|
|
|
|
|
|
// Check to see if we have an association for this IdP already |
|
|
|
|
$op_endpoint = Database::escape_string($op_endpoint); |
|
|
|
|
$sql = "SELECT assoc_handle FROM $openid_association WHERE idp_endpoint_uri = '$op_endpoint'"; |
|
|
|
|
$assoc_handle = Database::query($sql); |
|
|
|
|
if (Database::num_rows($assoc_handle) <= 1) { |
|
|
|
|
$mod = OPENID_DH_DEFAULT_MOD; |
|
|
|
|
$gen = OPENID_DH_DEFAULT_GEN; |
|
|
|
|
$r = _openid_dh_rand($mod); |
|
|
|
|
$private = bcadd($r, 1); |
|
|
|
|
$public = bcpowmod($gen, $private, $mod); |
|
|
|
|
|
|
|
|
|
// If there is no existing association, then request one |
|
|
|
|
$assoc_request = openid_association_request($public); |
|
|
|
|
$assoc_message = _openid_encode_message(_openid_create_message($assoc_request)); |
|
|
|
|
$assoc_headers = array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'); |
|
|
|
|
//TODO |
|
|
|
|
$assoc_result = openid_http_request($op_endpoint, $assoc_headers, 'POST', $assoc_message); |
|
|
|
|
if (isset($assoc_result->error)) { |
|
|
|
|
return FALSE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Remove Old Associations: |
|
|
|
|
//TODO |
|
|
|
|
$openid_association = Database::get_main_table(TABLE_MAIN_OPENID_ASSOCIATION); |
|
|
|
|
Database::query("DELETE FROM $openid_association WHERE created + expires_in < %d", time()); |
|
|
|
|
|
|
|
|
|
// Check to see if we have an association for this IdP already |
|
|
|
|
$assoc_handle = Database::query("SELECT assoc_handle FROM $openid_association WHERE idp_endpoint_uri = '%s'", $op_endpoint); |
|
|
|
|
if (Database::num_rows($assoc_handle)<=1) { |
|
|
|
|
$mod = OPENID_DH_DEFAULT_MOD; |
|
|
|
|
$gen = OPENID_DH_DEFAULT_GEN; |
|
|
|
|
$r = _openid_dh_rand($mod); |
|
|
|
|
$private = bcadd($r, 1); |
|
|
|
|
$public = bcpowmod($gen, $private, $mod); |
|
|
|
|
|
|
|
|
|
// If there is no existing association, then request one |
|
|
|
|
$assoc_request = openid_association_request($public); |
|
|
|
|
$assoc_message = _openid_encode_message(_openid_create_message($assoc_request)); |
|
|
|
|
$assoc_headers = array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'); |
|
|
|
|
//TODO |
|
|
|
|
$assoc_result = openid_http_request($op_endpoint, $assoc_headers, 'POST', $assoc_message); |
|
|
|
|
if (isset($assoc_result->error)) { |
|
|
|
|
return FALSE; |
|
|
|
|
} |
|
|
|
|
$assoc_response = _openid_parse_message($assoc_result->data); |
|
|
|
|
if (isset($assoc_response['mode']) && $assoc_response['mode'] == 'error') { |
|
|
|
|
return FALSE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$assoc_response = _openid_parse_message($assoc_result->data); |
|
|
|
|
if (isset($assoc_response['mode']) && $assoc_response['mode'] == 'error') { |
|
|
|
|
return FALSE; |
|
|
|
|
} |
|
|
|
|
if ($assoc_response['session_type'] == 'DH-SHA1') { |
|
|
|
|
$spub = _openid_dh_base64_to_long($assoc_response['dh_server_public']); |
|
|
|
|
$enc_mac_key = base64_decode($assoc_response['enc_mac_key']); |
|
|
|
|
$shared = bcpowmod($spub, $private, $mod); |
|
|
|
|
$assoc_response['mac_key'] = base64_encode(_openid_dh_xorsecret($shared, $enc_mac_key)); |
|
|
|
|
} |
|
|
|
|
//TODO |
|
|
|
|
$openid_association = Database::get_main_table(TABLE_MAIN_OPENID_ASSOCIATION); |
|
|
|
|
Database::query(sprintf("INSERT INTO $openid_association (idp_endpoint_uri, session_type, assoc_handle, assoc_type, expires_in, mac_key, created) VALUES('%s', '%s', '%s', '%s', %d, '%s', %d)", $op_endpoint, $assoc_response['session_type'], $assoc_response['assoc_handle'], $assoc_response['assoc_type'], $assoc_response['expires_in'], $assoc_response['mac_key'], api_get_utc_datetime())); |
|
|
|
|
|
|
|
|
|
if ($assoc_response['session_type'] == 'DH-SHA1') { |
|
|
|
|
$spub = _openid_dh_base64_to_long($assoc_response['dh_server_public']); |
|
|
|
|
$enc_mac_key = base64_decode($assoc_response['enc_mac_key']); |
|
|
|
|
$shared = bcpowmod($spub, $private, $mod); |
|
|
|
|
$assoc_response['mac_key'] = base64_encode(_openid_dh_xorsecret($shared, $enc_mac_key)); |
|
|
|
|
$assoc_handle = $assoc_response['assoc_handle']; |
|
|
|
|
} |
|
|
|
|
//TODO |
|
|
|
|
$openid_association = Database::get_main_table(TABLE_MAIN_OPENID_ASSOCIATION); |
|
|
|
|
Database::query(sprintf("INSERT INTO $openid_association (idp_endpoint_uri, session_type, assoc_handle, assoc_type, expires_in, mac_key, created) VALUES('%s', '%s', '%s', '%s', %d, '%s', %d)", |
|
|
|
|
$op_endpoint, $assoc_response['session_type'], $assoc_response['assoc_handle'], $assoc_response['assoc_type'], $assoc_response['expires_in'], $assoc_response['mac_key'], time())); |
|
|
|
|
|
|
|
|
|
$assoc_handle = $assoc_response['assoc_handle']; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $assoc_handle; |
|
|
|
|
return $assoc_handle; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -256,34 +255,33 @@ function openid_association_request($public) { |
|
|
|
|
*/ |
|
|
|
|
function openid_authentication_request($claimed_id, $identity, $return_to = '', $assoc_handle = '', $version = 2) { |
|
|
|
|
|
|
|
|
|
$realm = ($return_to) ? $return_to : api_get_self(); |
|
|
|
|
|
|
|
|
|
$ns = ($version == 2) ? OPENID_NS_2_0 : OPENID_NS_1_0; |
|
|
|
|
$request = array( |
|
|
|
|
'openid.ns' => $ns, |
|
|
|
|
'openid.mode' => 'checkid_setup', |
|
|
|
|
'openid.identity' => $identity, |
|
|
|
|
'openid.claimed_id' => $claimed_id, |
|
|
|
|
'openid.assoc_handle' => $assoc_handle, |
|
|
|
|
'openid.return_to' => $return_to, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if ($version == 2) { |
|
|
|
|
$request['openid.realm'] = $realm; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
$request['openid.trust_root'] = $realm; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Simple Registration - we don't ask lastname and firstname because the only |
|
|
|
|
// available similar data is "fullname" and we would have to guess where to split |
|
|
|
|
$request['openid.sreg.required'] = 'nickname,email'; |
|
|
|
|
$request['openid.ns.sreg'] = "http://openid.net/extensions/sreg/1.1"; |
|
|
|
|
|
|
|
|
|
//$request = array_merge($request, module_invoke_all('openid', 'request', $request)); |
|
|
|
|
//$request = array_merge($request); |
|
|
|
|
|
|
|
|
|
return $request; |
|
|
|
|
$realm = ($return_to) ? $return_to : api_get_self(); |
|
|
|
|
|
|
|
|
|
$ns = ($version == 2) ? OPENID_NS_2_0 : OPENID_NS_1_0; |
|
|
|
|
$request = array( |
|
|
|
|
'openid.ns' => $ns, |
|
|
|
|
'openid.mode' => 'checkid_setup', |
|
|
|
|
'openid.identity' => $identity, |
|
|
|
|
'openid.claimed_id' => $claimed_id, |
|
|
|
|
'openid.assoc_handle' => $assoc_handle, |
|
|
|
|
'openid.return_to' => $return_to, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if ($version == 2) { |
|
|
|
|
$request['openid.realm'] = $realm; |
|
|
|
|
} else { |
|
|
|
|
$request['openid.trust_root'] = $realm; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Simple Registration - we don't ask lastname and firstname because the only |
|
|
|
|
// available similar data is "fullname" and we would have to guess where to split |
|
|
|
|
$request['openid.sreg.required'] = 'nickname,email'; |
|
|
|
|
$request['openid.ns.sreg'] = "http://openid.net/extensions/sreg/1.1"; |
|
|
|
|
|
|
|
|
|
//$request = array_merge($request, module_invoke_all('openid', 'request', $request)); |
|
|
|
|
//$request = array_merge($request); |
|
|
|
|
|
|
|
|
|
return $request; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -296,173 +294,169 @@ function openid_authentication_request($claimed_id, $identity, $return_to = '', |
|
|
|
|
*/ |
|
|
|
|
function openid_verify_assertion($op_endpoint, $response) { |
|
|
|
|
|
|
|
|
|
$valid = FALSE; |
|
|
|
|
|
|
|
|
|
//TODO |
|
|
|
|
$openid_association = Database::get_main_table(TABLE_MAIN_OPENID_ASSOCIATION); |
|
|
|
|
$sql = sprintf("SELECT * FROM $openid_association WHERE assoc_handle = '%s'", $response['openid.assoc_handle']); |
|
|
|
|
$res = Database::query($sql); |
|
|
|
|
$association = Database::fetch_object($res); |
|
|
|
|
if ($association && isset($association->session_type)) { |
|
|
|
|
$keys_to_sign = explode(',', $response['openid.signed']); |
|
|
|
|
$self_sig = _openid_signature($association, $response, $keys_to_sign); |
|
|
|
|
if ($self_sig == $response['openid.sig']) { |
|
|
|
|
$valid = TRUE; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
$valid = FALSE; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
$request = $response; |
|
|
|
|
$request['openid.mode'] = 'check_authentication'; |
|
|
|
|
$message = _openid_create_message($request); |
|
|
|
|
$headers = array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'); |
|
|
|
|
$result = openid_http_request($op_endpoint, $headers, 'POST', _openid_encode_message($message)); |
|
|
|
|
if (!isset($result->error)) { |
|
|
|
|
$response = _openid_parse_message($result->data); |
|
|
|
|
if (strtolower(trim($response['is_valid'])) == 'true') { |
|
|
|
|
$valid = TRUE; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
$valid = FALSE; |
|
|
|
|
} |
|
|
|
|
$valid = FALSE; |
|
|
|
|
|
|
|
|
|
//TODO |
|
|
|
|
$openid_association = Database::get_main_table(TABLE_MAIN_OPENID_ASSOCIATION); |
|
|
|
|
$sql = sprintf("SELECT * FROM $openid_association WHERE assoc_handle = '%s'", $response['openid.assoc_handle']); |
|
|
|
|
$res = Database::query($sql); |
|
|
|
|
$association = Database::fetch_object($res); |
|
|
|
|
if ($association && isset($association->session_type)) { |
|
|
|
|
$keys_to_sign = explode(',', $response['openid.signed']); |
|
|
|
|
$self_sig = _openid_signature($association, $response, $keys_to_sign); |
|
|
|
|
if ($self_sig == $response['openid.sig']) { |
|
|
|
|
$valid = TRUE; |
|
|
|
|
} else { |
|
|
|
|
$valid = FALSE; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
$request = $response; |
|
|
|
|
$request['openid.mode'] = 'check_authentication'; |
|
|
|
|
$message = _openid_create_message($request); |
|
|
|
|
$headers = array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'); |
|
|
|
|
$result = openid_http_request($op_endpoint, $headers, 'POST', _openid_encode_message($message)); |
|
|
|
|
if (!isset($result->error)) { |
|
|
|
|
$response = _openid_parse_message($result->data); |
|
|
|
|
if (strtolower(trim($response['is_valid'])) == 'true') { |
|
|
|
|
$valid = TRUE; |
|
|
|
|
} else { |
|
|
|
|
$valid = FALSE; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $valid; |
|
|
|
|
return $valid; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Make a HTTP request - This function has been copied straight over from Drupal 6 code (drupal_http_request) |
|
|
|
|
*/ |
|
|
|
|
function openid_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) { |
|
|
|
|
$result = new stdClass(); |
|
|
|
|
|
|
|
|
|
// Parse the URL and make sure we can handle the schema. |
|
|
|
|
$uri = parse_url($url); |
|
|
|
|
|
|
|
|
|
switch ($uri['scheme']) { |
|
|
|
|
case 'http': |
|
|
|
|
$port = isset($uri['port']) ? $uri['port'] : 80; |
|
|
|
|
$host = $uri['host'] . ($port != 80 ? ':'. $port : ''); |
|
|
|
|
$fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15); |
|
|
|
|
break; |
|
|
|
|
case 'https': |
|
|
|
|
// Note: Only works for PHP 4.3 compiled with OpenSSL. |
|
|
|
|
$port = isset($uri['port']) ? $uri['port'] : 443; |
|
|
|
|
$host = $uri['host'] . ($port != 443 ? ':'. $port : ''); |
|
|
|
|
$fp = @fsockopen('ssl://'. $uri['host'], $port, $errno, $errstr, 20); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
$result->error = 'invalid schema '. $uri['scheme']; |
|
|
|
|
return $result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Make sure the socket opened properly. |
|
|
|
|
if (!$fp) { |
|
|
|
|
// When a network error occurs, we make sure that it is a negative number so |
|
|
|
|
// it can clash with the HTTP status codes. |
|
|
|
|
$result->code = -$errno; |
|
|
|
|
$result->error = trim($errstr); |
|
|
|
|
return $result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Construct the path to act on. |
|
|
|
|
$path = isset($uri['path']) ? $uri['path'] : '/'; |
|
|
|
|
if (isset($uri['query'])) { |
|
|
|
|
$path .= '?'. $uri['query']; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Create HTTP request. |
|
|
|
|
$defaults = array( |
|
|
|
|
// RFC 2616: "non-standard ports MUST, default ports MAY be included". |
|
|
|
|
// We don't add the port to prevent from breaking rewrite rules checking the |
|
|
|
|
// host that do not take into account the port number. |
|
|
|
|
'Host' => "Host: $host", |
|
|
|
|
'User-Agent' => 'User-Agent: Dokeos (+http://dokeos.com/)', |
|
|
|
|
'Content-Length' => 'Content-Length: '. strlen($data) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// If the server url has a user then attempt to use basic authentication |
|
|
|
|
if (isset($uri['user'])) { |
|
|
|
|
$defaults['Authorization'] = 'Authorization: Basic '. base64_encode($uri['user'] . (!empty($uri['pass']) ? ":". $uri['pass'] : '')); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
foreach ($headers as $header => $value) { |
|
|
|
|
$defaults[$header] = $header .': '. $value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$request = $method .' '. $path ." HTTP/1.0\r\n"; |
|
|
|
|
$request .= implode("\r\n", $defaults); |
|
|
|
|
$request .= "\r\n\r\n"; |
|
|
|
|
if ($data) { |
|
|
|
|
$request .= $data ."\r\n"; |
|
|
|
|
} |
|
|
|
|
$result->request = $request; |
|
|
|
|
|
|
|
|
|
fwrite($fp, $request); |
|
|
|
|
|
|
|
|
|
// Fetch response. |
|
|
|
|
$response = ''; |
|
|
|
|
while (!feof($fp) && $chunk = fread($fp, 1024)) { |
|
|
|
|
$response .= $chunk; |
|
|
|
|
} |
|
|
|
|
fclose($fp); |
|
|
|
|
|
|
|
|
|
// Parse response. |
|
|
|
|
list($split, $result->data) = explode("\r\n\r\n", $response, 2); |
|
|
|
|
$split = preg_split("/\r\n|\n|\r/", $split); |
|
|
|
|
|
|
|
|
|
list($protocol, $code, $text) = explode(' ', trim(array_shift($split)), 3); |
|
|
|
|
$result->headers = array(); |
|
|
|
|
|
|
|
|
|
// Parse headers. |
|
|
|
|
while ($line = trim(array_shift($split))) { |
|
|
|
|
list($header, $value) = explode(':', $line, 2); |
|
|
|
|
if (isset($result->headers[$header]) && $header == 'Set-Cookie') { |
|
|
|
|
// RFC 2109: the Set-Cookie response header comprises the token Set- |
|
|
|
|
// Cookie:, followed by a comma-separated list of one or more cookies. |
|
|
|
|
$result->headers[$header] .= ','. trim($value); |
|
|
|
|
$result = new stdClass(); |
|
|
|
|
|
|
|
|
|
// Parse the URL and make sure we can handle the schema. |
|
|
|
|
$uri = parse_url($url); |
|
|
|
|
|
|
|
|
|
switch ($uri['scheme']) { |
|
|
|
|
case 'http': |
|
|
|
|
$port = isset($uri['port']) ? $uri['port'] : 80; |
|
|
|
|
$host = $uri['host'] . ($port != 80 ? ':' . $port : ''); |
|
|
|
|
$fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15); |
|
|
|
|
break; |
|
|
|
|
case 'https': |
|
|
|
|
// Note: Only works for PHP 4.3 compiled with OpenSSL. |
|
|
|
|
$port = isset($uri['port']) ? $uri['port'] : 443; |
|
|
|
|
$host = $uri['host'] . ($port != 443 ? ':' . $port : ''); |
|
|
|
|
$fp = @fsockopen('ssl://' . $uri['host'], $port, $errno, $errstr, 20); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
$result->error = 'invalid schema ' . $uri['scheme']; |
|
|
|
|
return $result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Make sure the socket opened properly. |
|
|
|
|
if (!$fp) { |
|
|
|
|
// When a network error occurs, we make sure that it is a negative number so |
|
|
|
|
// it can clash with the HTTP status codes. |
|
|
|
|
$result->code = -$errno; |
|
|
|
|
$result->error = trim($errstr); |
|
|
|
|
return $result; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
$result->headers[$header] = trim($value); |
|
|
|
|
|
|
|
|
|
// Construct the path to act on. |
|
|
|
|
$path = isset($uri['path']) ? $uri['path'] : '/'; |
|
|
|
|
if (isset($uri['query'])) { |
|
|
|
|
$path .= '?' . $uri['query']; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Create HTTP request. |
|
|
|
|
$defaults = array( |
|
|
|
|
// RFC 2616: "non-standard ports MUST, default ports MAY be included". |
|
|
|
|
// We don't add the port to prevent from breaking rewrite rules checking the |
|
|
|
|
// host that do not take into account the port number. |
|
|
|
|
'Host' => "Host: $host", |
|
|
|
|
'User-Agent' => 'User-Agent: Dokeos (+http://dokeos.com/)', |
|
|
|
|
'Content-Length' => 'Content-Length: ' . strlen($data) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// If the server url has a user then attempt to use basic authentication |
|
|
|
|
if (isset($uri['user'])) { |
|
|
|
|
$defaults['Authorization'] = 'Authorization: Basic ' . base64_encode($uri['user'] . (!empty($uri['pass']) ? ":" . $uri['pass'] : '')); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
foreach ($headers as $header => $value) { |
|
|
|
|
$defaults[$header] = $header . ': ' . $value; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$responses = array( |
|
|
|
|
100 => 'Continue', 101 => 'Switching Protocols', |
|
|
|
|
200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', |
|
|
|
|
300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', |
|
|
|
|
400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', |
|
|
|
|
500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' |
|
|
|
|
); |
|
|
|
|
// RFC 2616 states that all unknown HTTP codes must be treated the same as the |
|
|
|
|
// base code in their class. |
|
|
|
|
if (!isset($responses[$code])) { |
|
|
|
|
$code = floor($code / 100) * 100; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch ($code) { |
|
|
|
|
case 200: // OK |
|
|
|
|
case 304: // Not modified |
|
|
|
|
break; |
|
|
|
|
case 301: // Moved permanently |
|
|
|
|
case 302: // Moved temporarily |
|
|
|
|
case 307: // Moved temporarily |
|
|
|
|
$location = $result->headers['Location']; |
|
|
|
|
|
|
|
|
|
if ($retry) { |
|
|
|
|
$result = openid_http_request($result->headers['Location'], $headers, $method, $data, --$retry); |
|
|
|
|
$result->redirect_code = $result->code; |
|
|
|
|
} |
|
|
|
|
$result->redirect_url = $location; |
|
|
|
|
|
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
$result->error = $text; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$result->code = $code; |
|
|
|
|
return $result; |
|
|
|
|
|
|
|
|
|
$request = $method . ' ' . $path . " HTTP/1.0\r\n"; |
|
|
|
|
$request .= implode("\r\n", $defaults); |
|
|
|
|
$request .= "\r\n\r\n"; |
|
|
|
|
if ($data) { |
|
|
|
|
$request .= $data . "\r\n"; |
|
|
|
|
} |
|
|
|
|
$result->request = $request; |
|
|
|
|
|
|
|
|
|
fwrite($fp, $request); |
|
|
|
|
|
|
|
|
|
// Fetch response. |
|
|
|
|
$response = ''; |
|
|
|
|
while (!feof($fp) && $chunk = fread($fp, 1024)) { |
|
|
|
|
$response .= $chunk; |
|
|
|
|
} |
|
|
|
|
fclose($fp); |
|
|
|
|
|
|
|
|
|
// Parse response. |
|
|
|
|
list($split, $result->data) = explode("\r\n\r\n", $response, 2); |
|
|
|
|
$split = preg_split("/\r\n|\n|\r/", $split); |
|
|
|
|
|
|
|
|
|
list($protocol, $code, $text) = explode(' ', trim(array_shift($split)), 3); |
|
|
|
|
$result->headers = array(); |
|
|
|
|
|
|
|
|
|
// Parse headers. |
|
|
|
|
while ($line = trim(array_shift($split))) { |
|
|
|
|
list($header, $value) = explode(':', $line, 2); |
|
|
|
|
if (isset($result->headers[$header]) && $header == 'Set-Cookie') { |
|
|
|
|
// RFC 2109: the Set-Cookie response header comprises the token Set- |
|
|
|
|
// Cookie:, followed by a comma-separated list of one or more cookies. |
|
|
|
|
$result->headers[$header] .= ',' . trim($value); |
|
|
|
|
} else { |
|
|
|
|
$result->headers[$header] = trim($value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$responses = array( |
|
|
|
|
100 => 'Continue', 101 => 'Switching Protocols', |
|
|
|
|
200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', |
|
|
|
|
300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', |
|
|
|
|
400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', |
|
|
|
|
500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' |
|
|
|
|
); |
|
|
|
|
// RFC 2616 states that all unknown HTTP codes must be treated the same as the |
|
|
|
|
// base code in their class. |
|
|
|
|
if (!isset($responses[$code])) { |
|
|
|
|
$code = floor($code / 100) * 100; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch ($code) { |
|
|
|
|
case 200: // OK |
|
|
|
|
case 304: // Not modified |
|
|
|
|
break; |
|
|
|
|
case 301: // Moved permanently |
|
|
|
|
case 302: // Moved temporarily |
|
|
|
|
case 307: // Moved temporarily |
|
|
|
|
$location = $result->headers['Location']; |
|
|
|
|
|
|
|
|
|
if ($retry) { |
|
|
|
|
$result = openid_http_request($result->headers['Location'], $headers, $method, $data, --$retry); |
|
|
|
|
$result->redirect_code = $result->code; |
|
|
|
|
} |
|
|
|
|
$result->redirect_url = $location; |
|
|
|
|
|
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
$result->error = $text; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$result->code = $code; |
|
|
|
|
return $result; |
|
|
|
|
} |
|
|
|
|