Move OpenID login handling into separate class

This commit is contained in:
Christian Weiske 2012-01-26 13:27:47 +01:00
parent 14640ed8a5
commit 1ab3c45d71
6 changed files with 281 additions and 108 deletions

View File

@ -0,0 +1,33 @@
<?php
require_once 'SemanticScuttle/Exception/User.php';
class SemanticScuttle_Exception extends Exception
{
/**
* Returns an error message that can be shown to the user.
*
* FIXME: maybe move that method to somewhere else
*
* @return string Error message to display to the user
*/
public static function getErrorMessage(Exception $e)
{
if ($e instanceof SemanticScuttle_Exception_User) {
if (isset($GLOBALS['debugMode']) && $GLOBALS['debugMode']) {
$prev = $e->getPrevious();
if ($prev) {
$msg = $prev->getMessage();
} else {
$msg = $e->getMessage();
}
} else {
$msg = $e->getMessage();
}
} else {
$msg = $e->getMessage();
}
return $msg;
}
}
?>

View File

@ -0,0 +1,10 @@
<?php
/**
* Exception class whose message may be displayed to users in the frontend.
*
* The detailled message is in the previous exception (getPrevious()).
*/
class SemanticScuttle_Exception_User extends Exception
{
}
?>

View File

@ -0,0 +1,212 @@
<?php
/**
* SemanticScuttle - your social bookmark manager.
*
* PHP version 5.
*
* @category Bookmarking
* @package SemanticScuttle
* @author Christian Weiske <cweiske@cweiske.de>
* @license AGPL http://www.gnu.org/licenses/agpl.html
* @link http://sourceforge.net/projects/semanticscuttle
*/
require_once 'SemanticScuttle/Exception/User.php';
require_once 'OpenID.php';
require_once 'OpenID/RelyingParty.php';
require_once 'OpenID/Extension/SREG11.php';
/**
* SemanticScuttle OpenID verification and management
*
* @category Bookmarking
* @package SemanticScuttle
* @author Christian Weiske <cweiske@cweiske.de>
* @license AGPL http://www.gnu.org/licenses/agpl.html
* @link http://sourceforge.net/projects/semanticscuttle
*/
class SemanticScuttle_Service_OpenID extends SemanticScuttle_DbService
{
/**
* Creates a new instance, sets database variable and table name.
*
* @param sql_db $db Database object
*/
protected function __construct($db)
{
$this->db = $db;
$this->tablename = $GLOBALS['tableprefix'] . 'users_openids';
}
/**
* Returns the single service instance
*
* @param sql_db $db Database object
*
* @return SemanticScuttle_Service_OpenID
*/
public static function getInstance($db)
{
static $instance;
if (!isset($instance)) {
$instance = new self($db);
}
return $instance;
}
/**
* Part 1 of the OpenID login process: Send user to his identity provider.
*
* This method exits the PHP process.
*
* @param string $identifier OpenID URL
* @param string $returnUrl URL the identity provider shall send the user
* back to
*
* @return void No return value needed since it exits.
*
* @throws SemanticScuttle_Exception_User When something goes wrong
*/
public function sendIdRequest($identifier, $returnUrl)
{
//send request to ID provider
try {
$identifier = OpenID::normalizeIdentifier($identifier);
} catch (OpenID_Exception $e) {
throw new SemanticScuttle_Exception_User(
'Invalid OpenID identifier', 11, $e
);
}
try {
$rp = new OpenID_RelyingParty(
$returnUrl,
addProtocolToUrl(ROOT)/* realm */,
$identifier
);
$authRequest = $rp->prepare();
//FIXME: when user exists already, use immediate mode first and
// fall back to normal when it fails
//FIXME: (?) when user exists already, don't request details
$sreg = new OpenID_Extension_SREG11(OpenID_Extension::REQUEST);
//$sreg->set('required', 'email');
$sreg->set('optional', 'email,nickname,fullname');
$authRequest->addExtension($sreg);
//$auth->setMode(OpenID::MODE_CHECKID_IMMEDIATE);
header('Location: ' . $authRequest->getAuthorizeURL());
exit();
} catch (OpenID_Exception $e) {
throw new SemanticScuttle_Exception_User(
'Error communicating with OpenID identity server', 12, $e
);
}
}
/**
* Part 2 of the OpenID login process: Handle the IDP response
*
* @param string $returnUrl URL the identity provider shall send the user
* back to
*
* @return array Array with user data. Keys:
* - identifier - OpenID URL/identifier
* - userId - Local user ID from database
* - email - OpenID-submitted email
* - nickname - OpenID-submitted nickname
* - fullname - OpenID-submitted full name
*
* @throws SemanticScuttle_Exception_User When something goes wrong
*/
public function handleIdResponse($returnUrl)
{
$rp = new OpenID_RelyingParty(
$returnUrl,
addProtocolToUrl(ROOT)/* realm */
);
if (!count($_POST)) {
list(, $queryString) = explode('?', $_SERVER['REQUEST_URI']);
} else {
$queryString = file_get_contents('php://input');
}
try {
$request = new Net_URL2($returnUrl . '?' . $queryString);
$message = new OpenID_Message($queryString, OpenID_Message::FORMAT_HTTP);
$mode = $message->get('openid.mode');
if ($mode == 'cancel') {
throw new SemanticScuttle_Exception_User(
'OpenID login cancelled', 10
);
} else if ($mode == 'setup_needed') {
throw new SemanticScuttle_Exception_User(
'Immediate OpenID login not possible', 12
);
}
$result = $rp->verify($request, $message);
if (!$result->success()) {
throw new SemanticScuttle_Exception_User(
'OpenID verification failed', 13
);
}
$identifier = $message->get('openid.claimed_id');
} catch (OpenID_Exception $e) {
//FIXME: report error
var_dump($e);die();
throw $e;
}
try {
$identifier = OpenID::normalizeIdentifier($identifier);
} catch (OpenID_Exception $e) {
throw new SemanticScuttle_Exception_User(
'Invalid OpenID identifier', 11, $e
);
}
return array(
'identifier' => $identifier,
'userId' => $this->getUserId($identifier),
'email' => $message->get('openid.sreg.email'),
'nickname' => $message->get('openid.sreg.nickname'),
'fullname' => $message->get('openid.sreg.fullname'),
);
}
/**
* Returns the user ID for the given OpenID identifier.
*
* @param string $identifier OpenID identifier (URL)
*
* @return integer User ID or NULL if not found
*
* @throws SemanticScuttle_Exception_User When the identifier is invalid
*/
public function getUserId($identifier)
{
$query = 'SELECT sc_users_openids.uId'
. ' FROM sc_users_openids JOIN sc_users USING (uId)'
. ' WHERE url = "' . $this->db->sql_escape($identifier) . '"';
if (!($dbresult = $this->db->sql_query($query))) {
message_die(
GENERAL_ERROR,
'Could not get user',
'', __LINE__, __FILE__, $query, $this->db
);
}
$row = $this->db->sql_fetchrow($dbresult);
$this->db->sql_freeresult($dbresult);
if (!$row) {
//OpenID not found in database.
return null;
}
return $row['uId'];
}
}
?>

View File

@ -538,101 +538,6 @@ class SemanticScuttle_Service_User extends SemanticScuttle_DbService
}
}
/**
* Try to authenticate and login a user with his OpenID
*
* @param string $identifier OpenID URL, may be null if $return is active
* @param boolean $return If the request is the answer from the provider
* @param boolean $remember If a long-time cookie shall be set
*
* @return boolean True if the user could be authenticated,
* false if not.
*/
public function loginOpenId($identifier, $return, $remember = false)
{
require_once 'OpenID.php';
require_once 'OpenID/RelyingParty.php';
require_once 'OpenID/Extension/SREG11.php';
$returnUrl = addProtocolToUrl(createURL('login', 'openidreturn'));
if ($identifier !== null) {
$identifier = OpenID::normalizeIdentifier($identifier);
}
$rp = new OpenID_RelyingParty(
$returnUrl/*returnTo*/,
addProtocolToUrl(ROOT)/* realm */,
$identifier
);
if ($return) {
//answer from ID provider
if (!count($_POST)) {
list(, $queryString) = explode('?', $_SERVER['REQUEST_URI']);
} else {
$queryString = file_get_contents('php://input');
}
try {
$request = new Net_URL2($returnUrl . '?' . $queryString);
$message = new OpenID_Message($queryString, OpenID_Message::FORMAT_HTTP);
$result = $rp->verify($request, $message);
if (!$result->success()) {
return false;
}
$identifier = $message->get('openid.claimed_id');
} catch (Exception $e) {
//FIXME: report error
return false;
}
//FIXME: join user table to be sure the user exists
$query = 'SELECT uId'
. ' FROM sc_users_openids'
. ' WHERE url = "' . $this->db->sql_escape($identifier) . '"';
if (!($dbresult = $this->db->sql_query($query))) {
message_die(
GENERAL_ERROR,
'Could not get user',
'', __LINE__, __FILE__, $query, $this->db
);
return false;
}
$row = $this->db->sql_fetchrow($dbresult);
$this->db->sql_freeresult($dbresult);
if (!$row) {
//OpenID not found in database. FIXME: Need to register
return false;
}
$this->setCurrentUserId($row['uId'], true);
//FIXME: update email and name
//FIXME: remember login setting
return true;
}
//send request to ID provider
try {
$authRequest = $rp->prepare();
//FIXME: when user exists already, use immediate mode first and
// fall back to normal when it fails
//FIXME: (?) when user exists already, don't request details
$sreg = new OpenID_Extension_SREG11(OpenID_Extension::REQUEST);
//$sreg->set('required', 'email');
$sreg->set('optional', 'email,nickname,fullname');
$authRequest->addExtension($sreg);
//$auth->setMode(OpenID::MODE_CHECKID_IMMEDIATE);
header('Location: ' . $authRequest->getAuthorizeURL());
exit();
} catch (Exception $e) {
//FIXME: throw user exception
var_dump($e);
return false;
}
}
/**
* Try to authenticate via the privateKey
*

View File

@ -98,6 +98,7 @@ if (DEBUG_MODE) {
// 2 // Second requirements part which could display bugs
// (must come after debug management)
require_once 'SemanticScuttle/Exception.php';
require_once 'SemanticScuttle/Service.php';
require_once 'SemanticScuttle/DbService.php';
require_once 'SemanticScuttle/Service/Factory.php';

View File

@ -39,20 +39,31 @@ $keeppass = (POST_KEEPPASS=='yes')?true:false;
$login = false;
if ($method == 'openidreturn') {
$login = $userservice->loginOpenId(null, true);
if (!$login) {
$tplVars['error'] = T_('OpenID login error');
}
} else if (POST_SUBMITTED != ''
&& isset($_POST['openid_identifier']) && $_POST['openid_identifier'] != ''
if ($method == 'openidreturn'
|| (POST_SUBMITTED != ''
&& isset($_POST['openid_identifier']) && $_POST['openid_identifier'] != ''
)
) {
$login = $userservice->loginOpenId(
$_POST['openid_identifier'], false, POST_KEEPPASS
);
if (!$login) {
//may be an exception with the ID
$tplVars['error'] = T_('OpenID login error');
$oids = SemanticScuttle_Service_Factory::get('OpenID');
$returnUrl = addProtocolToUrl(createURL('login', 'openidreturn'));
try {
if ($method == 'openidreturn') {
//part 2: handle response
$ret = $oids->handleIdResponse($returnUrl);
if ($ret['userId'] === null) {
//FIXME: ask to register
$tplVars['error'] = T_('OpenID login error');
} else {
$userservice->setCurrentUserId($ret['userId'], true);
$login = true;
}
//FIXME POST_KEEPPASS
} else {
//part 1: send request
$oids->sendIdRequest($_POST['openid_identifier'], $returnUrl);
}
} catch (Exception $e) {
$tplVars['error'] = SemanticScuttle_Exception::getErrorMessage($e);
}
} else if (POST_SUBMITTED!='' && POST_USERNAME!='' && POST_PASSWORD!='') {
$posteduser = trim(utf8_strtolower(POST_USERNAME));
@ -61,6 +72,7 @@ if ($method == 'openidreturn') {
$tplVars['error'] = T_('The details you have entered are incorrect. Please try again.');
}
}
if ($login) {
if (POST_QUERY)
header('Location: '. createURL('bookmarks', $posteduser .'?'. POST_QUERY));