New file |
0,0 → 1,1193 |
<?php |
/** |
* La classe OpenIdClient est une implémentation d'un client OpenId, depuis une classe Zend. |
* Elle permet d'établir une connexion avec un serveur, en fonction d'un identifiant OpenId. |
* Elle permet de communiquer de manière sécurisée avec ce serveur, et doit aboutir a une |
* identification centralisée. |
* */ |
|
class OpenIdClient { |
|
//OpenID 2.0 namespace. Tous les messages OpenID 2.0 DOIVENT contenir la variable openid.ns et sa valeur |
const NS_2_0 = 'http://specs.openid.net/auth/2.0'; |
|
|
// TODO : remplacer _storage par une gestion par cache ? |
/** |
* Variable permettant le stockage d'informations, notammenent à propos des clés DiffieHellmann |
* @var Storage $_storage |
*/ |
protected $_storage = null; |
|
/** |
* Tableau "cache" interne permettant d'éviter des accès inutiles au fichier storage |
* @var array $_cache |
*/ |
protected $_cache = array(); |
|
// Client pour les requetes. |
private $client; |
|
/** |
* Constructeur de l'application |
* */ |
function __construct() { |
$this->client = new Client(); |
$this->_storage = new StorageFile(); |
} |
|
/** |
* Fonction login |
* |
* Return true ou false |
* > Ne retourne rien si true car redirige vers l'adresse du serveur OID |
* */ |
//FIXME : le paramètre immediate ? |
// A vérifier mais ça doit permettre de passer directement le mot de passe. Il reste plus qu'à trouver le nom de la variable mot de passe. |
|
function login($id, $immediate = false) { |
|
// L'original retourne la fonction checkId, avec le parametre immediate = true |
// Je ne comprends pas l'utilité, je fusionne les deux pour l'instant |
// FIXME : si pas de comportement étrange, valider. |
|
|
//Tests et arrêt si non validé : |
//Normaliser (traite si XRI ou URL, normalize URL) |
//FIXME : voir avec JP pour équivalent dans framework |
if (!$this->normalize($id)) { |
return false; |
} |
|
//Discovery |
// Récupérer les informations sur le serveur OPEN ID |
/* |
FIXME : ca change la valeur de l'id !!! |
if (!$this->_discovery($id, $server, $version)) { |
trigger_error('Discovery failed'); |
return false; |
}*/ |
|
$retour_url = $this->client->consulter($id); |
//Le retour contient les balises suivantes : |
/* |
* |
* <link rel="openid.server" href="http://www.myopenid.com/server" /> |
* <link rel="openid2.provider" href="http://www.myopenid.com/server" /> |
*/ |
$metaServeur = $this->verifierVersion($retour_url); |
|
//TODO : Voir avec JP : la classe client ne permet pas de vérifer le statut ?? |
|
if ($retour_url === false) { |
trigger_error('L\'adresse $id est inacessible', E_USER_ERROR); |
return false; |
} |
if ($metaServeur === false) { |
return false; |
} |
if (!$this->_associate($metaServeur['serveur'], $metaServeur['version'])) { |
trigger_error('Impossible de s\'associer avec le serveur'); |
} |
|
|
/*TODO : fonctionnement différent |
if (!$this->_getAssociation( |
$server, |
$handle, |
$macFunc, |
$secret, |
$expires)) { |
/* Use dumb mode * |
unset($handle); |
unset($macFunc); |
unset($secret); |
unset($expires);* |
}*/ |
|
//on a la version, l'adresse du serveur et le realId si c'est une 2.0 dans metaServeur |
if (isset($metaServeur['realId'])) { |
$id = $metaServeur['realId']; |
} |
|
//Associate |
//getAssociation |
|
//Organisation des paramètres : |
$params = array(); |
|
|
if ($metaServeur['version'] >= 2.0) { |
$params['openid.ns'] = self::NS_2_0; |
} |
|
$params['openid.mode'] = $immediate ? |
'checkid_immediate' : 'checkid_setup'; |
|
$params['openid.identity'] = $id; |
|
//FIXME : Ex : $params['openid.claimed_id'] = $claimedId; > jvois pas l'intéret |
$params['openid.claimed_id'] = $id; |
/* |
* TODO : gérer les sessions et namespace |
* if ($metaServeur['version'] <= 2.0) { |
if ($this->_session !== null) { |
$this->_session->identity = $id; |
$this->_session->claimed_id = $claimedId; |
} else if (defined('SID')) { |
$_SESSION["zend_openid"] = array( |
"identity" => $id, |
"claimed_id" => $claimedId); |
} else { |
require_once "Zend/Session/Namespace.php"; |
$this->_session = new Zend_Session_Namespace("zend_openid"); |
$this->_session->identity = $id; |
$this->_session->claimed_id = $claimedId; |
} |
}*/ |
|
if (isset($handle)) { |
$params['openid.assoc_handle'] = $handle; |
} |
|
//FIXME : $params['openid.return_to'] = $this->absoluteUrl($returnTo); |
$params['openid.return_to'] = $this->absoluteUrl(null); |
|
if (empty($root)) { |
$root = $this->selfUrl(); |
if ($root[strlen($root)-1] != '/') { |
$root = dirname($root); |
} |
} |
if ($metaServeur['version'] >= 2.0) { |
$params['openid.realm'] = $root; |
} else { |
$params['openid.trust_root'] = $root; |
} |
|
/*FIXME :: |
|
if (!Zend_OpenId_Extension::forAll($extensions, 'prepareRequest', $params)) { |
$this->_setError("Extension::prepareRequest failure"); |
return false; |
}*/ |
|
$this->redirect($metaServeur['serveur'], $params); |
return true; |
//Renvoyer vers l'url |
} |
|
/** |
* Verifies authentication response from OpenID server. |
* |
* This is the second step of OpenID authentication process. |
* The function returns true on successful authentication and false on |
* failure. |
* |
* @param array $params HTTP query data from OpenID server |
* @param string &$identity this argument is set to end-user's claimed |
* identifier or OpenID provider local identifier. |
* @param mixed $extensions extension object or array of extensions objects |
* @return bool |
*/ |
public function verify($params, &$identity = "", $extensions = null) |
{ |
|
if (isset($params['openid_ns']) && |
$params['openid_ns'] == $this->NS_2_0) { |
$version = 2.0; |
} |
|
if (isset($params["openid_claimed_id"])) { |
$identity = $params["openid_claimed_id"]; |
} else if (isset($params["openid_identity"])){ |
$identity = $params["openid_identity"]; |
} else { |
$identity = ""; |
} |
|
if ($version < 2.0 && !isset($params["openid_claimed_id"])) { |
if ($this->_session !== null) { |
if ($this->_session->identity === $identity) { |
$identity = $this->_session->claimed_id; |
} |
} else if (defined('SID')) { |
if (isset($_SESSION["zend_openid"]["identity"]) && |
isset($_SESSION["zend_openid"]["claimed_id"]) && |
$_SESSION["zend_openid"]["identity"] === $identity) { |
$identity = $_SESSION["zend_openid"]["claimed_id"]; |
} |
} else { |
require_once "Zend/Session/Namespace.php"; |
$this->_session = new Zend_Session_Namespace("zend_openid"); |
if ($this->_session->identity === $identity) { |
$identity = $this->_session->claimed_id; |
} |
} |
} |
|
if (empty($params['openid_mode'])) { |
$this->_setError("Missing openid.mode"); |
return false; |
} |
if (empty($params['openid_return_to'])) { |
$this->_setError("Missing openid.return_to"); |
return false; |
} |
if (empty($params['openid_signed'])) { |
$this->_setError("Missing openid.signed"); |
return false; |
} |
if (empty($params['openid_sig'])) { |
$this->_setError("Missing openid.sig"); |
return false; |
} |
if ($params['openid_mode'] != 'id_res') { |
$this->_setError("Wrong openid.mode '".$params['openid_mode']."' != 'id_res'"); |
return false; |
} |
if (empty($params['openid_assoc_handle'])) { |
$this->_setError("Missing openid.assoc_handle"); |
return false; |
} |
|
if ($params['openid_return_to'] != $this->selfUrl()) { |
/* Ignore query part in openid.return_to */ |
$pos = strpos($params['openid_return_to'], '?'); |
if ($pos === false || |
SUBSTR($params['openid_return_to'], 0 , $pos) != $this->selfUrl()) { |
|
/*$this->_setError("Wrong openid.return_to '". |
$params['openid_return_to']."' != '" . $this->selfUrl() ."'");*/ |
trigger_error('Wrong openid.return_to', E_USER_ERROR); |
|
return false; |
} |
} |
|
if ($version >= 2.0) { |
if (empty($params['openid_response_nonce'])) { |
trigger_error('Missing openid.response_nonce', E_USER_ERROR); |
return false; |
} |
if (empty($params['openid_op_endpoint'])) { |
trigger_error('Missing openid.op_endpoint', E_USER_ERROR); |
return false; |
/* OpenID 2.0 (11.3) Checking the Nonce */ |
} else if (!$this->_storage->isUniqueNonce($params['openid_op_endpoint'], $params['openid_response_nonce'])) { |
trigger_error('Duplicate openid.response_nonce', E_USER_ERROR); |
return false; |
} |
} |
|
if (!empty($params['openid_invalidate_handle'])) { |
if ($this->_storage->getAssociationByHandle( |
$params['openid_invalidate_handle'], |
$url, |
$macFunc, |
$secret, |
$expires)) { |
$this->_storage->delAssociation($url); |
} |
} |
|
if ($this->_storage->getAssociationByHandle( |
$params['openid_assoc_handle'], |
$url, |
$macFunc, |
$secret, |
$expires)) { |
$signed = explode(',', $params['openid_signed']); |
$data = ''; |
foreach ($signed as $key) { |
$data .= $key . ':' . $params['openid_' . strtr($key,'.','_')] . "\n"; |
} |
if (base64_decode($params['openid_sig']) == |
Zend_OpenId::hashHmac($macFunc, $data, $secret)) { |
/* |
* FIXME dépendance je sais pas pour quoi : a voir : |
* if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) { |
$this->_setError("Extension::parseResponse failure"); |
return false; |
}*/ |
/* OpenID 2.0 (11.2) Verifying Discovered Information */ |
if (isset($params['openid_claimed_id'])) { |
$id = $params['openid_claimed_id']; |
if (!$this->normalize($id)) { |
$this->_setError("Normalization failed"); |
return false; |
} else if (!$this->_discovery($id, $discovered_server, $discovered_version)) { |
$this->_setError("Discovery failed: " . $this->getError()); |
return false; |
} else if ((!empty($params['openid_identity']) && |
$params["openid_identity"] != $id) || |
(!empty($params['openid_op_endpoint']) && |
$params['openid_op_endpoint'] != $discovered_server) || |
$discovered_version != $version) { |
$this->_setError("Discovery information verification failed"); |
return false; |
} |
} |
return true; |
} |
$this->_storage->delAssociation($url); |
$this->_setError("Signature check failed"); |
return false; |
} |
else |
{ |
/* Use dumb mode */ |
if (isset($params['openid_claimed_id'])) { |
$id = $params['openid_claimed_id']; |
} else if (isset($params['openid_identity'])) { |
$id = $params['openid_identity']; |
} else { |
$this->_setError("Missing openid.claimed_id and openid.identity"); |
return false; |
} |
|
if (!$this->normalize($id)) { |
trigger_error('Normalization failed', E_USER_ERROR); |
return false; |
} else if (!$this->_discovery($id, $server, $discovered_version)) { |
trigger_error('Discovery failed', E_USER_ERROR); |
return false; |
} |
|
/* OpenID 2.0 (11.2) Verifying Discovered Information */ |
if ((isset($params['openid_identity']) && |
$params["openid_identity"] != $id) || |
(isset($params['openid_op_endpoint']) && |
$params['openid_op_endpoint'] != $server) || |
$discovered_version != $version) { |
trigger_error('Discovery information verification failed', E_USER_ERROR); |
return false; |
} |
|
$params2 = array(); |
foreach ($params as $key => $val) { |
if (strpos($key, 'openid_ns_') === 0) { |
$key = 'openid.ns.' . substr($key, strlen('openid_ns_')); |
} else if (strpos($key, 'openid_sreg_') === 0) { |
$key = 'openid.sreg.' . substr($key, strlen('openid_sreg_')); |
} else if (strpos($key, 'openid_') === 0) { |
$key = 'openid.' . substr($key, strlen('openid_')); |
} |
$params2[$key] = $val; |
} |
$params2['openid.mode'] = 'check_authentication'; |
$ret = $this->client->modifier($serveur, $params2); |
|
//_httpRequest($server, 'POST', $params2, $status); |
if ($ret === false) { |
trigger_error("'Dumb' signature verification HTTP request failed", E_USER_ERROR); |
return false; |
} |
$r = array(); |
if (is_string($ret)) { |
foreach(explode("\n", $ret) as $line) { |
$line = trim($line); |
if (!empty($line)) { |
$x = explode(':', $line, 2); |
if (is_array($x) && count($x) == 2) { |
list($key, $value) = $x; |
$r[trim($key)] = trim($value); |
} |
} |
} |
} |
$ret = $r; |
if (!empty($ret['invalidate_handle'])) { |
if ($this->_storage->getAssociationByHandle( |
$ret['invalidate_handle'], |
$url, |
$macFunc, |
$secret, |
$expires)) { |
$this->_storage->delAssociation($url); |
} |
} |
if (isset($ret['is_valid']) && $ret['is_valid'] == 'true') { |
if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) { |
$this->_setError("Extension::parseResponse failure"); |
return false; |
} |
return true; |
} |
$this->_setError("'Dumb' signature verification failed"); |
return false; |
} |
} |
|
|
/** |
* Performs discovery of identity and finds OpenID URL, OpenID server URL |
* and OpenID protocol version. Returns true on succees and false on |
* failure. |
* |
* @param string &$id OpenID identity URL |
* @param string &$server OpenID server URL |
* @param float &$version OpenID protocol version |
* @return bool |
* @todo OpenID 2.0 (7.3) XRI and Yadis discovery |
*/ |
protected function _discovery(&$id, &$server, &$version) |
{ |
$realId = $id; |
if ($this->_storage->getDiscoveryInfo( |
$id, |
$realId, |
$server, |
$version, |
$expire)) { |
$id = $realId; |
return true; |
} |
|
/* TODO: OpenID 2.0 (7.3) XRI and Yadis discovery */ |
|
/* HTML-based discovery */ |
$clientDiscovery = new Client(); |
|
//TODO : rajouter un test sur le statut de la réponse |
// Nécessite la prise en compte des entetes dans le framework |
|
/*if ($status != 200 || !is_string($response)) { |
return false; |
}*/ |
$reponse = $clientDiscovery->consulter($id); |
$metaServeur = $this->verifierVersion($reponse); |
if (!isset($metaServeur) || empty($reponse)) { |
trigger_error('Aucune donnée OpenId', E_USER_ERROR); |
return false; |
} |
|
$expire = time() + 60 * 60; |
$this->_storage->addDiscoveryInfo($id, $metaServeur['realId'], $metaServeur['serveur'], $metaServeur['version'], $expire); |
if (!empty($metaServeur['realId'])) { |
$id = $metaServeur['realId']; |
} |
return true; |
} |
|
|
|
|
//Parser l'HTML de réponse pour trouver la version du serveur OPEN ID |
function verifierVersion($reponseHtml) { |
|
// TODO : remplacer l'arlgorythme suivant par cette solution : |
//1. Chercher l'existence d'une balise openidN.provider |
//2. Déterminer la version en fonction de la chaine : openid2.provider => 2.0; openid.provider => 1.1 |
//3. Récupérer l'url du serveur href="serveur" |
//4. SI 2.0, récupérer la valeur réelle de l'ID |
|
//TODO : penser à tester les deux versions du serveur |
|
$metaServeur = Array(); |
|
if (preg_match( |
'/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i', |
$reponseHtml, |
$r)) { |
$metaServeur['version'] = 2.0; |
$metaServeur['serveur'] = $r[3]; |
} else if (preg_match( |
'/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\3[^>]*\/?>/i', |
$reponseHtml, |
$r)) { |
$metaServeur['version'] = 2.0; |
$metaServeur['serveur'] = $r[1]; |
} else if (preg_match( |
'/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i', |
$reponseHtml, |
$r)) { |
$metaServeur['version'] = 1.1; |
$metaServeur['serveur'] = $r[3]; |
} else if (preg_match( |
'/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\3[^>]*\/?>/i', |
$reponseHtml, |
$r)) { |
$metaServeur['version'] = 1.1; |
$metaServeur['serveur'] = $r[2]; |
} else { |
return false; |
} |
|
if ($metaServeur['version'] >= 2.0) { |
if (preg_match( |
'/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i', |
$reponseHtml, |
$r)) { |
$metaServeur['realId'] = $r[3]; |
} else if (preg_match( |
'/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\3[^>]*\/?>/i', |
$reponseHtml, |
$r)) { |
$metaServeur['realId'] = $r[2]; |
} |
} else { |
if (preg_match( |
'/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i', |
$reponseHtml, |
$r)) { |
$metaServeur['realId'] = $r[3]; |
} else if (preg_match( |
'/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\3[^>]*\/?>/i', |
$reponseHtml, |
$r)) { |
$metaServeur['realId'] = $r[2]; |
} |
} |
|
return $metaServeur; |
} |
|
/** |
* Create (or reuse existing) association between OpenID consumer and |
* OpenID server based on Diffie-Hellman key agreement. Returns true |
* on success and false on failure. |
* |
* @param string $url OpenID server url |
* @param float $version OpenID protocol version |
* @param string $priv_key for testing only |
* @return bool |
*/ |
protected function _associate($url, $version, $priv_key=null) |
{ |
/* Check if we already have association in chace or storage */ |
/* |
* TODO : Utiliser le stockage plutot |
* */ |
if ($this->_getAssociation( |
$url, |
$handle, |
$macFunc, |
$secret, |
$expires)) { |
return true; |
} |
|
/* |
* TODO : utiliser le fichier de config |
* if ($this->_dumbMode) { |
return true; |
}*/ |
|
$params = array(); |
|
if ($version >= 2.0) { |
$params = array( |
'openid.ns' => $this->NS_2_0, |
'openid.mode' => 'associate', |
'openid.assoc_type' => 'HMAC-SHA256', |
'openid.session_type' => 'DH-SHA256', |
); |
} else { |
$params = array( |
'openid.mode' => 'associate', |
'openid.assoc_type' => 'HMAC-SHA1', |
'openid.session_type' => 'DH-SHA1', |
); |
} |
|
$dh = DiffieHellmanUtil::createDhKey(pack('H*', DiffieHellmanUtil::DH_P), |
pack('H*', DiffieHellmanUtil::DH_G), |
$priv_key); |
$dh_details = DiffieHellmanUtil::getDhKeyDetails($dh); |
|
$params['openid.dh_modulus'] = base64_encode( |
DiffieHellmanUtil::btwoc($dh_details['p'])); |
$params['openid.dh_gen'] = base64_encode( |
DiffieHellmanUtil::btwoc($dh_details['g'])); |
$params['openid.dh_consumer_public'] = base64_encode( |
DiffieHellmanUtil::btwoc($dh_details['pub_key'])); |
|
while(1) { |
//FIXME : c'est pas une modification ... |
$ret = $this->client->modifier($url, $params); // FIXME : a quoi sert status ?, $status); |
if ($ret === false) { |
//$this->_setError("HTTP request failed"); |
trigger_error('La requête a échoué', E_USER_ERROR); |
return false; |
} |
|
$r = array(); |
$bad_response = false; |
foreach(explode("\n", $ret) as $line) { |
$line = trim($line); |
if (!empty($line)) { |
$x = explode(':', $line, 2); |
if (is_array($x) && count($x) == 2) { |
list($key, $value) = $x; |
$r[trim($key)] = trim($value); |
} else { |
$bad_response = true; |
} |
} |
} |
if ($bad_response && strpos($ret, 'Unknown session type') !== false) { |
$r['error_code'] = 'unsupported-type'; |
} |
$ret = $r; |
|
if (isset($ret['error_code']) && |
$ret['error_code'] == 'unsupported-type') { |
if ($params['openid.session_type'] == 'DH-SHA256') { |
$params['openid.session_type'] = 'DH-SHA1'; |
$params['openid.assoc_type'] = 'HMAC-SHA1'; |
} else if ($params['openid.session_type'] == 'DH-SHA1') { |
$params['openid.session_type'] = 'no-encryption'; |
} else { |
trigger_error("The OpenID service responded with: " . $ret['error_code'], E_USER_ERROR); |
return false; |
} |
} else { |
break; |
} |
} |
|
/* |
FIXME : gestion du statut avec la classe client ?? |
if ($status != 200) { |
$this->_setError("The server responded with status code: " . $status); |
return false; |
}*/ |
|
if ($version >= 2.0 && |
isset($ret['ns']) && |
$ret['ns'] != $this->NS_2_0) { |
$this->_setError("Wrong namespace definition in the server response"); |
return false; |
} |
|
if (!isset($ret['assoc_handle']) || |
!isset($ret['expires_in']) || |
!isset($ret['assoc_type']) || |
$params['openid.assoc_type'] != $ret['assoc_type']) { |
if ($params['openid.assoc_type'] != $ret['assoc_type']) { |
$this->_setError("The returned assoc_type differed from the supplied openid.assoc_type"); |
} else { |
$this->_setError("Missing required data from provider (assoc_handle, expires_in, assoc_type are required)"); |
} |
return false; |
} |
|
$handle = $ret['assoc_handle']; |
$expiresIn = $ret['expires_in']; |
|
if ($ret['assoc_type'] == 'HMAC-SHA1') { |
$macFunc = 'sha1'; |
} else if ($ret['assoc_type'] == 'HMAC-SHA256' && |
$version >= 2.0) { |
$macFunc = 'sha256'; |
} else { |
$this->_setError("Unsupported assoc_type"); |
return false; |
} |
|
if ((empty($ret['session_type']) || |
($version >= 2.0 && $ret['session_type'] == 'no-encryption')) && |
isset($ret['mac_key'])) { |
$secret = base64_decode($ret['mac_key']); |
} else if (isset($ret['session_type']) && |
$ret['session_type'] == 'DH-SHA1' && |
!empty($ret['dh_server_public']) && |
!empty($ret['enc_mac_key'])) { |
$dhFunc = 'sha1'; |
} else if (isset($ret['session_type']) && |
$ret['session_type'] == 'DH-SHA256' && |
$version >= 2.0 && |
!empty($ret['dh_server_public']) && |
!empty($ret['enc_mac_key'])) { |
$dhFunc = 'sha256'; |
} else { |
$this->_setError("Unsupported session_type"); |
return false; |
} |
if (isset($dhFunc)) { |
$serverPub = base64_decode($ret['dh_server_public']); |
$dhSec = DiffieHellmanUtil::computeDhSecret($serverPub, $dh); |
if ($dhSec === false) { |
$this->_setError("DH secret comutation failed"); |
return false; |
} |
$sec = $this->digest($dhFunc, $dhSec); |
if ($sec === false) { |
$this->_setError("Could not create digest"); |
return false; |
} |
$secret = $sec ^ base64_decode($ret['enc_mac_key']); |
} |
if ($macFunc == 'sha1') { |
if (DiffieHellmanUtil::strlen($secret) != 20) { |
$this->_setError("The length of the sha1 secret must be 20"); |
return false; |
} |
} else if ($macFunc == 'sha256') { |
if (DiffieHellmanUtil::strlen($secret) != 32) { |
$this->_setError("The length of the sha256 secret must be 32"); |
return false; |
} |
} |
|
$this->_addAssociation( |
$url, |
$handle, |
$macFunc, |
$secret, |
time() + $expiresIn); |
/* $this->association['url'] = $url; |
$this->association['handle'] = $handle; |
$this->association['macFunc'] = $macFunc; |
$this->association['secret'] = $secret; |
$this->association['expiresIn'] = time() + $expiresIn;*/ |
|
return true; |
} |
|
/** |
* Store assiciation in internal chace and external storage |
* |
* @param string $url OpenID server url |
* @param string $handle association handle |
* @param string $macFunc HMAC function (sha1 or sha256) |
* @param string $secret shared secret |
* @param integer $expires expiration UNIX time |
* @return void |
*/ |
protected function _addAssociation($url, $handle, $macFunc, $secret, $expires) |
{ |
$this->_cache[$url] = array($handle, $macFunc, $secret, $expires); |
return $this->_storage->addAssociation( |
$url, |
$handle, |
$macFunc, |
$secret, |
$expires); |
} |
|
/** |
* Retrive assiciation information for given $url from internal cahce or |
* external storage |
* |
* @param string $url OpenID server url |
* @param string &$handle association handle |
* @param string &$macFunc HMAC function (sha1 or sha256) |
* @param string &$secret shared secret |
* @param integer &$expires expiration UNIX time |
* @return void |
*/ |
protected function _getAssociation($url, &$handle, &$macFunc, &$secret, &$expires) |
{ |
if (isset($this->_cache[$url])) { |
$handle = $this->_cache[$url][0]; |
$macFunc = $this->_cache[$url][1]; |
$secret = $this->_cache[$url][2]; |
$expires = $this->_cache[$url][3]; |
return true; |
} |
if ($this->_storage->getAssociation( |
$url, |
$handle, |
$macFunc, |
$secret, |
$expires)) { |
$this->_cache[$url] = array($handle, $macFunc, $secret, $expires); |
return true; |
} |
return false; |
} |
|
/** |
* Normalizes URL according to RFC 3986 to use it in comparison operations. |
* The function gets URL argument by reference and modifies it. |
* It returns true on success and false of failure. |
* |
* @param string &$id url to be normalized |
* @return bool |
*/ |
static public function normalizeUrl(&$id) |
{ |
// RFC 3986, 6.2.2. Syntax-Based Normalization |
|
// RFC 3986, 6.2.2.2 Percent-Encoding Normalization |
$i = 0; |
$n = strlen($id); |
$res = ''; |
while ($i < $n) { |
if ($id[$i] == '%') { |
if ($i + 2 >= $n) { |
return false; |
} |
++$i; |
if ($id[$i] >= '0' && $id[$i] <= '9') { |
$c = ord($id[$i]) - ord('0'); |
} else if ($id[$i] >= 'A' && $id[$i] <= 'F') { |
$c = ord($id[$i]) - ord('A') + 10; |
} else if ($id[$i] >= 'a' && $id[$i] <= 'f') { |
$c = ord($id[$i]) - ord('a') + 10; |
} else { |
return false; |
} |
++$i; |
if ($id[$i] >= '0' && $id[$i] <= '9') { |
$c = ($c << 4) | (ord($id[$i]) - ord('0')); |
} else if ($id[$i] >= 'A' && $id[$i] <= 'F') { |
$c = ($c << 4) | (ord($id[$i]) - ord('A') + 10); |
} else if ($id[$i] >= 'a' && $id[$i] <= 'f') { |
$c = ($c << 4) | (ord($id[$i]) - ord('a') + 10); |
} else { |
return false; |
} |
++$i; |
$ch = chr($c); |
if (($ch >= 'A' && $ch <= 'Z') || |
($ch >= 'a' && $ch <= 'z') || |
$ch == '-' || |
$ch == '.' || |
$ch == '_' || |
$ch == '~') { |
$res .= $ch; |
} else { |
$res .= '%'; |
if (($c >> 4) < 10) { |
$res .= chr(($c >> 4) + ord('0')); |
} else { |
$res .= chr(($c >> 4) - 10 + ord('A')); |
} |
$c = $c & 0xf; |
if ($c < 10) { |
$res .= chr($c + ord('0')); |
} else { |
$res .= chr($c - 10 + ord('A')); |
} |
} |
} else { |
$res .= $id[$i++]; |
} |
} |
|
if (!preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?#]*)?((?:[?](?:[^#]*))?)((?:#.*)?)$|', $res, $reg)) { |
return false; |
} |
$scheme = $reg[1]; |
$auth = $reg[2]; |
$host = $reg[3]; |
$port = $reg[4]; |
$path = $reg[5]; |
$query = $reg[6]; |
$fragment = $reg[7]; /* strip it */ |
|
if (empty($scheme) || empty($host)) { |
return false; |
} |
|
// RFC 3986, 6.2.2.1. Case Normalization |
$scheme = strtolower($scheme); |
$host = strtolower($host); |
|
// RFC 3986, 6.2.2.3. Path Segment Normalization |
if (!empty($path)) { |
$i = 0; |
$n = strlen($path); |
$res = ""; |
while ($i < $n) { |
if ($path[$i] == '/') { |
++$i; |
while ($i < $n && $path[$i] == '/') { |
++$i; |
} |
if ($i < $n && $path[$i] == '.') { |
++$i; |
if ($i < $n && $path[$i] == '.') { |
++$i; |
if ($i == $n || $path[$i] == '/') { |
if (($pos = strrpos($res, '/')) !== false) { |
$res = substr($res, 0, $pos); |
} |
} else { |
$res .= '/..'; |
} |
} else if ($i != $n && $path[$i] != '/') { |
$res .= '/.'; |
} |
} else { |
$res .= '/'; |
} |
} else { |
$res .= $path[$i++]; |
} |
} |
$path = $res; |
} |
|
// RFC 3986,6.2.3. Scheme-Based Normalization |
if ($scheme == 'http') { |
if ($port == 80) { |
$port = ''; |
} |
} else if ($scheme == 'https') { |
if ($port == 443) { |
$port = ''; |
} |
} |
if (empty($path)) { |
$path = '/'; |
} |
|
$id = $scheme |
. '://' |
. $auth |
. $host |
. (empty($port) ? '' : (':' . $port)) |
. $path |
. $query; |
return true; |
} |
|
|
/** |
* Normaliser l'identifiant OpenId qui peut être une URL ou nom XRI |
* Retourne true ou false en cas d'erreur. |
* |
* Règles de normalisation : |
* 1. If the user's input starts with one of the "xri://", "xri://$ip*", |
* or "xri://$dns*" prefixes, they MUST be stripped off, so that XRIs |
* are used in the canonical form, and URI-authority XRIs are further |
* considered URL identifiers. |
* 2. If the first character of the resulting string is an XRI Global |
* Context Symbol ("=", "@", "+", "$", "!"), then the input SHOULD be |
* treated as an XRI. |
* 3. Otherwise, the input SHOULD be treated as an http URL; if it does |
* not include a "http" or "https" scheme, the Identifier MUST be |
* prefixed with the string "http://". |
* 4. URL identifiers MUST then be further normalized by both following |
* redirects when retrieving their content and finally applying the |
* rules in Section 6 of [RFC3986] to the final destination URL. |
* @param string &$id identifier to be normalized |
* @return bool |
*/ |
static public function normalize(&$id) |
{ |
$id = trim($id); |
if (strlen($id) === 0) { |
return true; |
} |
|
// 7.2.1 |
if (strpos($id, 'xri://$ip*') === 0) { |
$id = substr($id, strlen('xri://$ip*')); |
} else if (strpos($id, 'xri://$dns*') === 0) { |
$id = substr($id, strlen('xri://$dns*')); |
} else if (strpos($id, 'xri://') === 0) { |
$id = substr($id, strlen('xri://')); |
} |
|
// 7.2.2 |
if ($id[0] == '=' || |
$id[0] == '@' || |
$id[0] == '+' || |
$id[0] == '$' || |
$id[0] == '!') { |
return true; |
} |
|
// 7.2.3 |
if (strpos($id, "://") === false) { |
$id = 'http://' . $id; |
} |
|
// 7.2.4 |
return self::normalizeURL($id); |
} |
|
/** |
* Generates a hash value (message digest) according to given algorithm. |
* It returns RAW binary string. |
* |
* This is a wrapper function that uses one of available internal function |
* dependent on given PHP configuration. It may use various functions from |
* ext/openssl, ext/hash, ext/mhash or ext/standard. |
* |
* @param string $func digest algorithm |
* @param string $data data to sign |
* @return string RAW digital signature |
* @throws Zend_OpenId_Exception |
*/ |
public function digest($func, $data) |
{ |
if (function_exists('openssl_digest')) { |
return openssl_digest($data, $func, true); |
} else if (function_exists('hash')) { |
return hash($func, $data, true); |
} else if ($func === 'sha1') { |
return sha1($data, true); |
} else if ($func === 'sha256') { |
if (function_exists('mhash')) { |
return mhash(MHASH_SHA256 , $data); |
} |
} |
/*require_once "Zend/OpenId/Exception.php"; |
throw new Zend_OpenId_Exception( |
'Unsupported digest algorithm "' . $func . '".', |
Zend_OpenId_Exception::UNSUPPORTED_DIGEST);*/ |
trigger_error('Unsupported digest algorithm '.$func , E_USER_ERROR); |
} |
|
|
|
|
|
|
|
|
/** |
* Returns a full URL that was requested on current HTTP request. |
* |
* @return string |
*/ |
public function selfUrl() |
{ |
/*FIXME : |
* if ($this->$selfUrl !== null) { |
return $this->$selfUrl; |
} */ |
|
if (isset($_SERVER['SCRIPT_URI'])) { |
return $_SERVER['SCRIPT_URI']; |
} |
$url = ''; |
$port = ''; |
if (isset($_SERVER['HTTP_HOST'])) { |
if (($pos = strpos($_SERVER['HTTP_HOST'], ':')) === false) { |
if (isset($_SERVER['SERVER_PORT'])) { |
$port = ':' . $_SERVER['SERVER_PORT']; |
} |
$url = $_SERVER['HTTP_HOST']; |
} else { |
$url = substr($_SERVER['HTTP_HOST'], 0, $pos); |
$port = substr($_SERVER['HTTP_HOST'], $pos); |
} |
} else if (isset($_SERVER['SERVER_NAME'])) { |
$url = $_SERVER['SERVER_NAME']; |
if (isset($_SERVER['SERVER_PORT'])) { |
$port = ':' . $_SERVER['SERVER_PORT']; |
} |
} |
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { |
$url = 'https://' . $url; |
if ($port == ':443') { |
$port = ''; |
} |
} else { |
$url = 'http://' . $url; |
if ($port == ':80') { |
$port = ''; |
} |
} |
|
$url .= $port; |
if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { |
$url .= $_SERVER['HTTP_X_REWRITE_URL']; |
} elseif (isset($_SERVER['REQUEST_URI'])) { |
$query = strpos($_SERVER['REQUEST_URI'], '?'); |
if ($query === false) { |
$url .= $_SERVER['REQUEST_URI']; |
} else { |
$url .= substr($_SERVER['REQUEST_URI'], 0, $query); |
} |
} else if (isset($_SERVER['SCRIPT_URL'])) { |
$url .= $_SERVER['SCRIPT_URL']; |
} else if (isset($_SERVER['REDIRECT_URL'])) { |
$url .= $_SERVER['REDIRECT_URL']; |
} else if (isset($_SERVER['PHP_SELF'])) { |
$url .= $_SERVER['PHP_SELF']; |
} else if (isset($_SERVER['SCRIPT_NAME'])) { |
$url .= $_SERVER['SCRIPT_NAME']; |
if (isset($_SERVER['PATH_INFO'])) { |
$url .= $_SERVER['PATH_INFO']; |
} |
} |
return $url; |
} |
|
|
//TODO : vérifier si les fonctions FWK & ZEND sont bien équivalente |
/** |
* Retourne l'url absolue d'une url donnée |
* |
* @param string $url absilute or relative URL |
* @return string |
*/ |
public function absoluteUrl($url) |
{ |
if (!empty($ur)) { |
$urlAbsolue = new Url($url); |
$urlAbsolue->normaliser(); |
$url = $urlAbsolue->getUrl(); |
} else { |
$url = $this->selfUrl(); |
} |
|
return $url; |
/* |
if (empty($url)) { |
return $this->selfUrl(); |
} else if (!preg_match('|^([^:]+)://|', $url)) { |
if (preg_match('|^([^:]+)://([^:@]*(?:[:][^@]*)?@)?([^/:@?#]*)(?:[:]([^/?#]*))?(/[^?]*)?((?:[?](?:[^#]*))?(?:#.*)?)$|', $this->selfUrl(), $reg)) { |
$scheme = $reg[1]; |
$auth = $reg[2]; |
$host = $reg[3]; |
$port = $reg[4]; |
$path = $reg[5]; |
$query = $reg[6]; |
if ($url[0] == '/') { |
return $scheme |
. '://' |
. $auth |
. $host |
. (empty($port) ? '' : (':' . $port)) |
. $url; |
} else { |
$dir = dirname($path); |
return $scheme |
. '://' |
. $auth |
. $host |
. (empty($port) ? '' : (':' . $port)) |
. (strlen($dir) > 1 ? $dir : '') |
. '/' |
. $url; |
} |
} |
} |
return $url;*/ |
} |
|
|
//TODO : voir si on ne peut pas glisser ça dans client ? |
//FIXME : je met une fonction SIMPLISSIME a améliorer et reécrire |
// La fonction de Zend est plus poussée est prend en compte le cas ou l'header ne peut pas etre envoyé |
/** |
* Rediriger vers la page du serveur avec les paramètres de confiration |
* |
* @param string $url URL de retour |
* @param array $params paramètres additionnels |
*/ |
public function redirect($url, $params) { |
//1. fabriquer l'url Get |
$urlRedirection = new Url($url); |
$urlRedirection->setRequete($params); |
//echo $urlRedirection->getUrl(); |
try { |
header('Location:'.$urlRedirection->getUrl()); |
} catch (Exception $e) { |
//TODO : voir autres méthodes de redirection |
// > balise META |
// > formulaire HTML |
// > JS |
} |
} |
} |
?> |
Property changes: |
Added: svn:keywords |
+Id Author Date Revision HeadURL |
\ No newline at end of property |