<?php
require_once ('domit/xml_domit_include.php');
require_once ('ParamsProxy.php');
require_once ('UTF8.php');
require_once ('DbProxy.php');
require_once ('Authenticator.php');
/**
* Showcases the functionality of the Authenticator class.
* The Authenticator class is an users registrar kernel. It provides low-level API for user
* registration, login and credentials management.
*
* @author Claudius Tiberiu Iacob <claudius.iacob@gmail.com>.
* @license Creative Commons Attribution Share Alike - Claudius Tiberiu Iacob 2009
*/
class AuthenticatorDemo {
/**
* Logs in an user. For simple, open registration (which is our case), this is done as:
*
* Authenticator::getInstance()->authenticate (<password>, <username>);
*/
private function loginUser () {
$userPass = trim ($_POST['pass']);
$userName = trim ($_POST['username']);
if (!empty ($userName) && !empty ($userPass)) {
$sessionData = Authenticator::getInstance()->authenticate ($userPass, $userName);
$isAuthenticated = !is_numeric ($sessionData);
if ($isAuthenticated) {
$this->storeSessionData ($sessionData);
$this->gotoProtectedContent();
} else {
$errorMessage;
switch ($sessionData) {
case Authenticator::UNKNOWN_USER_PASS_COMBO:
$errorMessage = 'wrong username and/or password.';
break;
case Authenticator::USER_HAS_NOT_CONFIRMED_REGISTRATION:
$errorMessage = 'you must confirm your registration first';
break;
default:
$errorMessage = "Error no. $sessionData.";
break;
}
$this->makeMessagePage (
'Login Failed',
"Cannot authenticate you: $errorMessage."
);
}
} else {
$this->makeMessagePage (
'Missing Credentials',
'Please provide credentials in order to login.');
}
}
/**
* Attempts authentication with cached unique session id. Upon succes, a new unique session id
* is produced, which you must cache instead of the old one. This is done as follows:
*
* Authenticator::getInstance()->authenticate (<session uniqe id>);
*/
private function attemptToLoginFromCache () {
$isAuthenticated;
$newSessionData;
$cachedSessionData = trim ($_COOKIE["AuthenticatorDemo"]);
if (!empty ($cachedSessionData)) {
$newSessionData = Authenticator::getInstance()->authenticate ($cachedSessionData);
$isAuthenticated = !is_numeric ($newSessionData);
}
if ($isAuthenticated) {
$this->storeSessionData ($newSessionData);
$this->gotoProtectedContent();
}
}
/**
* Recovers a forgotten passwords. This is done as:
*
* Authenticator::getInstance()->retrievePassword (<registration e-mail address>);
*
* If the Authenticator module has been set to drop support for password recovery, then the same
* API above will reset the passsword to a random value and return that random value instead.
*/
private function recoverUserPassword () {
$email = trim ($_POST['email']);
if (!empty ($email)) {
$passwordData = Authenticator::getInstance()->retrievePassword ($email);
// Passwords cannot be shorter that 4 chars; if this is the case, we deal with an error
// number:
$recoveryFailed = (is_numeric ($passwordData) && strlen($passwordData) < 4);
if ($recoveryFailed) {
$errorMessage;
switch ($passwordData) {
case Authenticator::UNKNOWN_EMAIL:
$errorMessage = "The e-mail $email was never registered.";
break;
default:
$errorMessage = "Error no. $passwordData.";
break;
}
$this->makeMessagePage (
'Error',
$errorMessage
);
} else {
$this->sendEmail (
$email,
'AuthenticatorDemo <noreply@nowhere.com>',
'Your password to AuthenticatorDemo',
"This is your forgotten password to AuthenticatorDemo (leave out the ".
"enclosing quotes and trailing dot):\n".
"\"$passwordData\".");
$this->makeMessagePage (
'Password Recovered',
"An e-mail has been sent to you at $email, containing your forgotten password."
);
}
}
}
/**
* Registers an user. For simple, open registration (which is our case), this is done as:
*
* Authenticator::getInstance()->register (array (Authenticator::PASSWORD => <password>),
* <username>, <email>);
*/
private function registerUser () {
$username = trim ($_POST['username']);
$email = trim ($_POST['email']);
$pass = trim ($_POST['pass']);
$passAgain = trim ($_POST['passAgain']);
if (!empty ($username) && !empty ($email) && !empty ($pass) && !empty ($passAgain)) {
if ($pass !== $passAgain) {
$this->makeMessagePage (
'Missmatched Password',
'Please enter the same password twice.'
);
} else {
$confirmationData = Authenticator::getInstance()-> register (
array (Authenticator::PASSWORD => $pass), $username, $email);
$isUserRegistered = !is_numeric ($confirmationData);
if ($isUserRegistered) {
$this->sendEmail (
$email,
'AuthenticatorDemo <noreply@nowhere.com>',
'Please confirm your registration to AuthenticatorDemo',
"Copy & paste in your browser's address bar the following URL (leaving ".
"out enclosing quotes):\n".
"\"" . $this->makeConfirmationURL ($confirmationData) . "\"");
$this->makeMessagePage (
'Confirm Registration',
"An e-mail has been sent to you at $email.
Please follow the confirmation link contained in this e-mail in order to
complete registration."
);
} else {
$errorMessage;
switch ($confirmationData) {
case Authenticator::ILLEGAL_USER_NAME:
$errorMessage = 'Provided user name is not acceptable.';
break;
case Authenticator::ILLEGAL_EMAIL:
$errorMessage = 'Provided e-mail is not acceptable.';
break;
case Authenticator::ILLEGAL_PASSWORD:
$errorMessage = 'Provided password is not acceptable.';
break;
default:
$errorMessage = "Error no. $confirmationData.";
break;
}
$this->makeMessagePage (
'Error',
$errorMessage
);
}
}
} else {
$this->makeMessagePage (
'Missing Data',
'Please provide all required data in order to sign up.'
);
}
}
/**
* Completes registration for a self-registering user. This is done as:
*
* Authenticator::getInstance()->register (array (Authenticator::CONFIRMATION_UID => <uid>));
*/
private function completeRegistration ($confirmationUid) {
$confirmationAnswer = Authenticator::getInstance()->register (
array (Authenticator::CONFIRMATION_UID => $confirmationUid));
$isConfirmed = (!is_numeric ($confirmationAnswer));
if ($isConfirmed) {
// If confirmation is successfull, the returned result is a valid session unique id.
//
// In this example, you could use the $confirmationAnswer variable to automatically
// authenticate the user, rather than printing a confirmation message.
$this->makeMessagePage (
'Registration Complete',
'You have successfully signed up to AuthenticatorDemo. You may now proceed to the
Login Page, and authenticate using your supplied credentials.'
);
} else {
$errorMessage;
switch ($confirmationAnswer) {
case Authenticator::IVALID_CONFIRMATION_UID:
$errorMessage = 'Provided confirmation unique id is invalid. Have you
entered the URL correctly?';
break;
case Authenticator::EXPIRED_CONFIRMATION_UID:
$errorMessage = 'Provided confirmation unique id has expired. You will need
to sign up again.';
break;
default:
$errorMessage = "Error no. $confirmationAnswer.";
break;
}
$this->makeMessagePage (
'Error',
$errorMessage
);
}
}
/**
* Stores the session unique id on the client machine.
*
* You must locally store the session id yourself; the Authenticator module doesn't do it for
* you.
*
* The session lifetime is limited by the Authenticator module, internally. If you use a
* cookie as the local storage medium, give it a long lifetime. This will rule out potential
* issues. In this demo, we make the cookie last one day, although our Authenticator is
* configured to allow 15 minutes per session at most.
*/
private function storeSessionData ($sessionData) {
$cookieExpireTime = (time() + 86400);
$cookiePath = '/';
setcookie ('AuthenticatorDemo', $sessionData, $cookieExpireTime, $cookiePath);
}
/**
* Concatenates an URL containing confirmation data.
*
* The Authenticator module doesn't produce confirmation URLs for you, therefore you must carry
* on this aspect by yourself.
*/
private function makeConfirmationURL ($confirmationData) {
$url = ($_SERVER['HTTPS'] == "on")? 'https://' : 'http://';
$url .= $_SERVER['HTTP_HOST'];
if ($_SERVER['SERVER_PORT'] != '80') {
$url .= (':' . $_SERVER['SERVER_PORT']);
}
$path = str_replace ($_SERVER['QUERY_STRING'], '', $_SERVER['REQUEST_URI']);
$url .= $path;
if (strpos ($url, '?') !== strlen ($url) - 1) {
$url .= '?';
}
$url .= ('confirmation=' . $confirmationData);
return $url;
}
/**
* Sends a plain text e-mail.
*
* The Authenticator module doesn't deal with sending e-mails. You must write your own code to do
* that.
*/
private function sendEmail ($to, $from, $subject, $message) {
$message = wordwrap ($message, 70, "\n", true);
$headers =
"From: $from\n" .
"Reply-To: $from\n" .
'X-Mailer: PHP/' .phpversion(). "\n";
$addParam = "-f$from";
$result = @mail ($to, $subject, $message, $headers, $addParam);
return $result;
}
/**
* @constructor
*
* This logic is essentially a state machine. It detects the action to be performed and invokes
* the associated class method.
*
* The Authenticator module only provides you core functionality; you must aggregate it yourself
* in order to build/integrate with a user control panel page.
*/
public function __construct () {
$this->blindlyInitialize ();
$action = trim ($_POST['action']);
switch ($action) {
case 'Sign me in':
$this->loginUser();
break;
case 'E-mail me':
$this->recoverUserPassword();
break;
case 'Sign me up';
$this->registerUser();
break;
case 'login page':
$this->makeLoginPage();
break;
case 'password recovery page':
$this->makeRecoverPassPage();
break;
case 'registration page':
$this->makeRegistrationPage();
break;
default:
// If we are given a confirmation unique id, then complete registration for that
// unique id:
$confirmationUid = trim ($_GET['confirmation']);
if (!empty ($confirmationUid)) {
$this->completeRegistration ($confirmationUid);
return;
}
// Otherwise, attempt to re-login using cached session data:
$this->attemptToLoginFromCache();
// If we have no valid session data, we load the login page:
$this->makeLoginPage();
break;
}
}
/**
* @destructor
*/
public function __destruct () {
$this->makePageFooter ();
}
/**
* DO NOT, UNDER NO CIRCUMSTANCE, blindlyInitialize() THE AUTHENTICATOR MODULE IN A REAL WEB
* APPLICATION!
*
* This method FAKES a real initialization for the Authenticator module. We do it in order to
* keep our demo's user interface as simple as possible.
*
* Because this is a demo, and because we included no administrative features in this demo,
* we will initialize the Authenticator module in the background, using hardcoded master
* credentials. For real-life scenarios though, master credentials are essential. You will
* typically create a page in your web application that initializes master credentials and sends
* them to your e-mail address. Beware not to make this the default page of your web application.
* It is also common practice to delete this page once setup is complete.
*
* The API used for initializing master credentials looks like:
*
* <result> = Authenticator::getInstance()-> register (
* array (Authenticator::PASSWORD => <mster password>), <master user name>,
* <master e-mail>, 'master');
*
* You must register one master account before being able to use the Authenticator. Master
* registration locks itself after a master account has been defined.
*/
private function blindlyInitialize () {
$sessionData = Authenticator::getInstance()->authenticate ('demo', 'demo', 'master');
$hasMaster = ($sessionData !== Authenticator::MASTER_NOT_SET);
if ($hasMaster) {
$isAuthenticated = !is_numeric ($sessionData);
if ($isAuthenticated) {
Authenticator::getInstance()->unAuthenticate ($sessionData);
}
} else {
Authenticator::getInstance()-> register (array (Authenticator::PASSWORD => 'demo'),
'demo', 'demo@nowhere.com');
}
}
// Less relevant class methods (printing and redirection)...
private function makePageHeader () {
echo '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
<head>
<title>AuthenticatorDemo</title>
</head>
<body>';
}
private function makeLoginPage () {
$this->makePageHeader ();
echo '
<h2>Login Page</h2>
<form method="post" action="">
<h3>Sign in</h3>
<p>
<label>
User name:
<input type="text" name="username" />
</label>
</p>
<p>
<label>
Password:
<input type="password" name="pass" />
</label>
</p>
<p>
<input type="submit" name="action" value="Sign me in" />
</p>
</form>
';
}
private function makeRecoverPassPage () {
$this->makePageHeader ();
echo '
<h2>Password Recovery Page</h2>
<form method="post" action="">
<h3>Forgot Password?</h3>
<p>
Enter your e-mail address below, so we can send you your password. You
must provide the same address you used at registration time.
</p>
<p>
<input type="text" name="email" />
</p>
<p>
<input type="submit" name="action" value="E-mail me" />
</p>
</form>
';
}
private function makeRegistrationPage () {
$this->makePageHeader ();
echo '
<h2>Registration Page</h2>
<form method="post" action="">
<h3>Sign Up</h3>
<p>
You need a valid e-mail address to sign up. A confirmation link will be sent
to this address once you click the <em>Sign me up</em> button. You have
follow this link in order to complete registration.
</p>
<p>
<label>
Username:
<input type="text" name="username" />
</label>
</p>
<p>
<label>
E-mail address:
<input type="text" name="email" />
</label>
</p>
<p>
<label>
Password:
<input type="password" name="pass" />
</label>
</p>
<p>
<label>
Password again:
<input type="password" name="passAgain" />
</label>
</p>
<p>
<input type="submit" name="action" value="Sign me up" />
</p>
</form>
';
}
private function makeMessagePage ($title, $content) {
$this->makePageHeader ();
$ret = '
<h2>' . $title . '</h2>
<p>' . $content . '</p>';
echo $ret;
}
private function makePageFooter () {
echo '
<form method="post" action="">
<hr />
<p>
Go to:
<input type="submit" name="action" value="login page" />
<input type="submit" name="action" value="password recovery page" />
<input type="submit" name="action" value="registration page"/>
</p>
</form>
</body>
</html>';
}
private function gotoProtectedContent () {
header ('Location: ProtectedContent.php');
exit ();
}
}
$authenticatorDemo = new AuthenticatorDemo();
?> |