Location: PHPKode > projects > OpenRat CMS > openrat/serviceClasses/OpenId.class.php
<?php


/**
 * Open-Id Authentisierung gem�� OpenId-Spezifikation 1.0.
 *
 */
class OpenId
{
	/**
	 * Open-Id Server, an den die Authentisierungsanfrage gestellt wird.
	 *
	 * @var String
	 */
	var $server;
	
	
	/**
	 * Informationen zum Benutzer.
	 *
	 * @var Array
	 */
	var $info;
	
	/**
	 * Open-Id Identity.
	 *
	 * @var String
	 */
	var $identity;
	
	/**
	 * Fehlermeldung (falls vorhanden).
	 *
	 * @var String
	 */
	var $error;

	/**
	 * OpenId-Benutzername.
	 *
	 * @var String
	 */
	var $user;

	/**
	 * OpenId-Provider.
	 *
	 * @var String
	 */
	var $provider;

	
	var $supportAX;
	var $supportSREG;
	var $supportOpenId1_1;
	var $supportOpenId2_0;
	
	
	/**
	 * Neue Open-Id Anfrage.
	 *
	 * @param String $user
	 * @return OpenId
	 */
	function OpenId( $provider='',$user='' )
	{
		$this->provider = $provider;
		$this->user     = $user;
	}

	
	/**
	 * Stellt fest, ob der Server vertrauensw�rdig ist.
	 *
	 * @return true, wenn vertrauensw�rdig.
	 */
	function serverOk()
	{
		global $conf;
		$servers = $conf['security']['openid']['trusted_server'];
		
		if	( empty($servers) )
		{
			return true;
		}
		else
		{
			$serverList = explode(',',$servers);
			
			$http = new Http($this->server);
			if	( !in_array($http->url['host'],$serverList) )
			{
				$this->error = 'Server '.$this->server.' is not trusted';
				return false;
			}
			else
				return true;
		}
		
	}
	
	
	
	/**
	 * Authentisierung Schritt 1.<br>
	 * Ermitteln der Identity.
	 *
	 * @return boolean TRUE, wenn Identity ermittelt wurde.
	 */
	function login()
	{
		if	( $this->provider != 'identity' ) 
		{
			$this->user     = config('security','openid','provider.'.$this->provider.'.xrds_uri'); 
			$this->identity = 'http://specs.openid.net/auth/2.0/identifier_select'; 
		}
		$this->supportSREG = config('security','openid','provider.'.$this->provider.'.sreg_1_0');
		$this->supportAX   = config('security','openid','provider.'.$this->provider.'.ax_1_0'  );
		
		// Schritt 1: Identity aus Yadis-Dokument laden.
		$this->getIdentityFromYadis();

		// Schritt 2: Fallback auf HTML-Dokument.
		if	( empty($this->server) )
		{
			$this->getIdentityFromHtmlMetaData();
		}
		
		// Falls immer noch kein Servername gefunden wurde, dann Abbruch.
		if	( empty($this->server) )
		{
			if	( empty($this->error) )
				$this->error = 'Unable to locate OpenId-Server in URL';
			return false;
		}
		
		if	( !$this->serverOk() )
			return false; // Server nicht vertrauenswuerdig.
		
		if	( empty($this->identity) )
			// Falls die Identity bis hierher nicht deligiert wurde...
			// Lt. Spezifikation mit Prefix "http://".
			$this->identity = 'http://'.$this->user;
		
		return true;
	}
	
	
	
	/**
	 * Erzeugt einen HTTP-Redirect auf den OpenId-Provider.
	 */
	function redirect()
	{
		global $conf;
		
		$this->handle = md5(microtime().session_id());

		$redirHttp = new Http($this->server);
		
		if	( $this->supportOpenId2_0 )
			$redirHttp->requestParameter['openid.ns'       ] = 'http://specs.openid.net/auth/2.0';
			
		$redirHttp->requestParameter['openid.mode'         ] = 'checkid_setup';
		$redirHttp->requestParameter['openid.identity'     ] = $this->identity;
		
		if	( $this->supportOpenId2_0 )
			$redirHttp->requestParameter['openid.claimed_id'] = $this->identity;
		

		// Profilangaben anfordern. E-Mail wird ben�tigt, Name und Sprache sind optional.
		
		if	( $this->supportAX )
		{
			Logger::info("OpenId-Server is using OpenID Attribute Exchange 1.0");
			$redirHttp->requestParameter['openid.ns.ax'            ] = 'http://openid.net/srv/ax/1.0';
			$redirHttp->requestParameter['openid.ax.mode'          ] = 'fetch_request';
			$redirHttp->requestParameter['openid.ax.type.email'    ] = 'http://axschema.org/contact/email';
			$redirHttp->requestParameter['openid.ax.type.username' ] = 'http://axschema.org/namePerson/friendly';
		    $redirHttp->requestParameter['openid.ax.type.fullname' ] = 'http://axschema.org/namePerson';
		    $redirHttp->requestParameter['openid.ax.type.language' ] = 'http://axschema.org/pref/language';
		    $redirHttp->requestParameter['openid.ax.required'      ] = 'username,email';
		    $redirHttp->requestParameter['openid.ax.if_available'  ] = 'language,fullname';
		}
		
		if	( $this->supportSREG )
		{
			Logger::info("OpenId-Server is using OpenID Simple Registration Extension 1.0");
			$redirHttp->requestParameter['openid.ns.sreg'      ] = 'http://openid.net/sreg/1.0';
			$redirHttp->requestParameter['openid.sreg.required'] = 'email,nickname';
			$redirHttp->requestParameter['openid.sreg.optional'] = 'fullname,language';
		}
		
		$trustRoot = @$conf['security']['openid']['trust_root'];
		$server = Http::getServer();
		if	( empty($trustRoot) )
			$trustRoot = $server;
			
		$redirHttp->requestParameter['openid.trust_root'   ] = slashify($trustRoot);
		$redirHttp->requestParameter['openid.return_to'    ] = slashify($server).'openid.'.PHP_EXT;
		//$redirHttp->requestParameter['openid.realm'        ] = slashify($server).'openid.'.PHP_EXT;
		$redirHttp->requestParameter['openid.assoc_handle' ] = $this->handle;

		$redirHttp->sendRedirect(); // Browser umleiten.
		exit;                       // Ende.
	}
	
	
	
	/**
	 * Ermittelt OpenId-Server und OpenId-Identity aus Yadis-Dokument.<br>
	 *
	 * @return unknown
	 */
	function getIdentityFromYadis()
	{
		$http = new Http($this->user);
//		$http->url['host'] = $this->user;

		$http->header[] = 'Accept: application/xrds+xml';
		if	( ! $http->request() )
		{
			$this->error = 'Unable to get XML delegate information';
			return false;
		}
		
		Logger::debug("OpenId: Found YADIS-document for ".$http->getUrl());
		//die();
		$p = xml_parser_create();
		$ok = xml_parse_into_struct($p, $http->body, $vals, $index);
		xml_parser_free($p);

		foreach( $vals as $tag )
		{
			if	( strtolower($tag['tag']) == 'type' )
			{
				if	( $tag['value'] == 'http://openid.net/srv/ax/1.0' )
					$this->supportAX = true;
					
				if	( $tag['value'] == 'http://openid.net/sreg/1.0' )
					$this->supportSREG = true;
					
				if	( $tag['value'] == 'http://openid.net/signon/1.1' )
					$this->supportOpenId1_1 = true;
					
				if	( $tag['value'] == 'http://specs.openid.net/auth/2.0/server' )
					$this->supportOpenId2_0 = true;
			}
			
			if	( strtolower($tag['tag']) == 'uri' )
			{
				$this->server = $tag['value'];
			}

			if	( strtolower($tag['tag']) == 'openid:delegate' )
			{
				$this->identity = $tag['value'];
			}
		}
		
		if	( !$this->supportOpenId1_1 && !$this->supportOpenId2_0 )
		{
			$this->error = 'Only OpenId 1.1 and 2.0 is supported but this identity-provider does not seem to support any of these.';
			return false;
		}
		if	( !$this->supportAX && !$this->supportSREG )
		{
			$this->error = 'The identity-provider must support either Attribute-Exchange (AX) oder Simple-Registration (SREG), but it does not seem to support any of these.';
			return false;
		}
	}

	
	/**
	 * Ermittelt OpenId-Server und OpenId-Identity aus HTML Meta-Tags.<br>
	 */
	function getIdentityFromHtmlMetaData()
	{
		$http = new Http($this->user);
//		$http = new Http();
//		$http->url['host'] = $this->user;
		$http->header[] = 'Accept: text/html';

		if	( ! $http->request() )
		{
			$this->error = 'Unable to get HTML delegate information';
			return false;
		}
		
		$seite = $http->body;
		
		// Die Meta-Tags mit regulaerem Ausdruck auslesen.
		$treffer = array();
		preg_match('/rel="openid.server"\s+href="(\S+)"/',$seite,$treffer);
		if	( count($treffer) >= 1 )
		{
			$this->server = $treffer[1];
			$this->supportOpenId1_1 = true;
		}

		$treffer = array();
		preg_match('/rel="openid2.provider"\s+href="(\S+)"/',$seite,$treffer);
		if	( count($treffer) >= 1 )
		{
			$this->supportOpenId2_0 = true;
			$this->server = $treffer[1];
		}

		$treffer = array();
		preg_match('/rel="openid.delegate"\s+href="(\S+)"/',$seite,$treffer);
		if	( count($treffer) >= 1 )
			$this->identity = $treffer[1];
	}
	

	/**
	 * Ermittelt den Hostnamen aus der Identity.
	 *
	 * @return String
	 */
	function getUserFromIdentiy()
	{
		if	( $this->provider == 'identity' )
		{
			$http = new Http($this->identity);
			return $http->url['host'];
		}
		else
		{
			$attribute_name = config('security','openid','provider.'.$this->provider.'.map_attribute');
			return $this->info[$attribute_name];
		}
	}
		
	
	/**
	 * Open-Id Login, �berpr�fen der Anmeldung.<br>
	 * Spezifikation: http://openid.net/specs/openid-authentication-1_1.html<br>
	 * Kapitel "4.4. check_authentication"<br>
	 * <br>
	 * Im 2. Schritt (Mode "id_res") erfolgte ein Redirect vom Open-Id Provider an OpenRat zur�ck.<br>
	 * Wir befinden uns nun im darauf folgenden Request des Browsers.<br>
	 * <br>
	 * Es muss noch beim OpenId-Provider die Best�tigung eingeholt werden, danach ist der
	 * Benutzer angemeldet.<br>
	 */
	function checkAuthentication()
	{
		$queryVars = $this->getQueryParamList(); 
		       
		if	( $queryVars['openid.invalidate_handle'] != $this->handle )
		{
			$this->error = 'Association-Handle mismatch.';
			return false;
		}

		if	( $queryVars['openid.mode'] != 'id_res' )
		{
			$this->error ='Open-Id: Unknown mode:'.$queryVars['openid.mode'];
			return false;
		}
		
		if	( $this->provider=='identity' && $queryVars['openid.identity'] != $this->identity )
		{
			$this->error ='Open-Id: Identity mismatch. Wrong identity:'.$queryVars['openid.identity'];
			return false;
		}
		

		$params = array();
		
		if	( $this->supportAX )
			// Den Namespace-Prefix für AX (attribute exchange) herausfinden.
			// Leider kann das ein anderer Prefix sein, als wir im Request verwendet haben.
			foreach( $queryVars as $request_key=>$request_value )
				if	(  substr($request_key,0,10)=='openid.ns.' && $request_value == 'http://openid.net/srv/ax/1.0' )
					$axPrefix = substr($request_key,10);
		
		foreach( $queryVars as $request_key=>$request_value )
		{
			// Benutzer-Attribute ermitteln.
			// Benutzer-Attribute über SREG ermitteln.
			if	( $this->supportSREG && substr($request_key,0,12)=='openid.sreg.' )
				$this->info[ substr($request_key,12) ] = $request_value;
			// Benutzer-Attribute über AX ermitteln.
			elseif	( $this->supportAX && substr($request_key,0,14+strlen($axPrefix))=='openid.'.$axPrefix.'.value.' )
				$this->info[ substr($request_key,14+strlen($axPrefix)) ] = $request_value;

			// Alle OpenId-Parameter in den Check-Authentication-Request übertragen.
			if	( substr($request_key,0,7)=='openid.' )
					$params['openid.'.substr($request_key,7) ] = $request_value;			
		}
		$params['openid.mode'] = 'check_authentication';
		
		$checkRequest = new Http($this->server);
		
		$checkRequest->method = 'POST'; // Spezifikation verlangt POST.
		$checkRequest->header['Accept'] = 'text/plain';
		$checkRequest->requestParameter = $params;
		
		if	( ! $checkRequest->request() )
		{
			// Der HTTP-Request ging in die Hose.
			$this->error = $checkRequest->error;
			return false;
		}
		//Html::debug($checkRequest);
		
		// Analyse der HTTP-Antwort, Parsen des BODYs.
		// Die Anmeldung ist best�tigt, wenn im BODY die Zeile "is_valid:true" vorhanden ist.
		// Siehe Spezifikation Kapitel 4.4.2
		$result = array();
		foreach( explode("\n",$checkRequest->body) as $line )
		{
			$pair = explode(':',trim($line));
			if	(count($pair)==2)
				$result[strtolower($pair[0])] = strtolower($pair[1]);
		}
		
		if	( !array_key_exists('is_valid',$result) )
		{
			// Zeile nicht gefunden.
			$this->error = 'Undefined Open-Id response: "is_valid" expected, but not found';
			return false;
		}
		elseif	( $result['is_valid'] == 'true' )
		{
			// Anmeldung wurde mit "is_valid:true" best�tigt.
			return true;
		}
		else
		{
			// Bestaetigung wurde durch den OpenId-Provider abgelehnt.
			$this->error = 'Server refused login.';
			return false;
		}
	}
	
	
	/**
	 * Liefert die Query-Parameter aus der aktuellen URL.<br>
	 * <br>
	 * PHP hat leider die sehr bescheuerte Angewohnheit, Punkte und Leerzeichen in Request-Variablen
	 * durch Unterstriche zu ersetzen. Diese Funktion liefert die GET-Parameter ohne diese Ersetzung.
	 * 
	 * @return Parameter der aktuellen URL
	 */
	function getQueryParamList()
	{
		// Quelle: php.net
		$str = $_SERVER['QUERY_STRING'];
		$op = array();
		$pairs = explode("&", $str);
		foreach ($pairs as $pair)
		{
			list($k, $v) = array_map("urldecode", explode("=", $pair));
			$op[$k] = $v;
		}
		
		return $op;
	}

	
}

?>
Return current item: OpenRat CMS