<?php
namespace com\whitemagicsoftware;
require "constants.php";
require "class.BaseController.php";
require "class.Mail.php";
class Login extends BaseController {
const AUTH_LOGIN_EMAIL_LOG_IN = "AUTH_LOGIN_EMAIL_LOG_IN";
const AUTH_LOGIN_EMAIL_INVALID = "AUTH_LOGIN_EMAIL_INVALID";
const AUTH_LOGIN_EMAIL_PENDING = "AUTH_LOGIN_EMAIL_PENDING";
const AUTH_LOGIN_EMAIL_ERROR = "AUTH_LOGIN_EMAIL_ERROR";
const AUTH_LOGIN_TOKEN_INVALID = "AUTH_LOGIN_TOKEN_INVALID";
private $auth_status_map = array(
Login::AUTH_LOGIN_EMAIL_LOG_IN => "Log in using your email address.",
Login::AUTH_LOGIN_EMAIL_INVALID => "Use a different email address.",
Login::AUTH_LOGIN_EMAIL_PENDING => "Log in link sent; check your email.",
Login::AUTH_LOGIN_EMAIL_ERROR => "Error sending confirmation email.",
Login::AUTH_LOGIN_TOKEN_INVALID => "Expired link; try again.",
);
private $auth_status = Login::AUTH_LOGIN_EMAIL_LOG_IN;
function __construct() {
parent::__construct();
}
protected function exists( $id ) {
return $this->isTrue( $this->call( "is_existing_account", "exists", $id ) );
}
protected function getParameterIdName() {
return "account-id";
}
protected function getAuthorizationFunctionName() {
return "is_authorized_account";
}
protected function getLastResortId() {
return $this->getAccountId();
}
protected function getStylesheetName() {
return "xsl/login.xsl";
}
private function getXml() {
$result = $this->call( "generate_account_xml", "x", $this->getId() );
return isset( $result ) ?
$result[0]["x"] : $this->getErrorXml( "account" );
}
protected function getXhtml() {
$xslt = $this->getXsltEngine();
$xslt->setXml( $this->getXml() );
$xslt->setStylesheet( $this->getStylesheetName() );
$xslt->setParameter( $this->getParameterIdName(), $this->getId() );
$xslt->setParameter( "status", $this->getAuthStatus() );
$xslt->setParameter( "message", $this->getAuthStatusMessage() );
return $xslt->transform();
}
private function updateAuthenticationNonce( $email, $nonce ) {
$cookie = $this->getCookieToken();
if( !$this->isTrue( $this->call(
"is_authenticated", "exists", $email, $cookie ) ) ) {
$this->log( "Before generate cookie: $cookie" );
$this->generateCookieToken();
$cookie = $this->getCookieToken();
$this->log( "After generate cookie: $cookie" );
}
$this->call( "authentication_nonce_upsert", "", $email, $nonce, $cookie );
}
private function transmitNonce() {
global $AUTH_LOGIN_EMAIL_SUBJECT;
$email = $this->getParameter( "email" );
$mail = new Mail();
if( $mail->validate( $email ) === true ) {
$nonce = $this->generateLoginNonce();
$message = $this->getLoginMessage( $nonce );
if( $mail->send( $email, $AUTH_LOGIN_EMAIL_SUBJECT, $message ) ) {
$this->updateAuthenticationNonce( $email, $nonce );
$this->setAuthStatus( Login::AUTH_LOGIN_EMAIL_PENDING );
}
else {
$this->setAuthStatus( Login::AUTH_LOGIN_EMAIL_ERROR );
}
}
else {
$this->setAuthStatus( Login::AUTH_LOGIN_EMAIL_INVALID );
}
}
private function isValidNonce( $nonce ) {
return $this->isTrue( $this->call( "is_valid_nonce", "exists", $nonce ) );
}
private function isEmailValidated( $nonce ) {
return $this->isTrue( $this->call( "is_valid_email", "exists", $nonce ) );
}
private function setAccountId( $cookie ) {
$id = $this->call( "get_account_id_cookie", "id", $cookie );
if( isset( $id[0] ) ) {
$id = $id[0]["id"];
if( $id > 0 ) {
$this->setId( $id );
}
}
}
private function authenticate( $nonce ) {
if( $this->isValidNonce( $nonce ) ) {
if( $this->isEmailValidated( $nonce ) ) {
global $AUTH_COOKIE_NAME;
$token = $this->getCookieToken();
$this->log( "Cookie Token: $token" );
$this->log( "Nonce: $nonce" );
$cookie = $this->call( "authenticate_nonce", "c", $nonce, $token );
$cookie = isset( $cookie[0] ) ? $cookie[0]["c"] : $token;
$this->log( "Resolved cookie: $cookie" );
$this->setCookieToken( $AUTH_COOKIE_NAME, $cookie );
$this->setAccountId( $cookie );
}
else {
$this->call( "authenticate_account", "", $nonce );
}
}
}
private function authenticateNonce() {
global $AUTH_LOGIN_TOKEN_NAME;
$result = false;
$nonce = $this->getParameter( $AUTH_LOGIN_TOKEN_NAME );
if( isset( $nonce ) ) {
$this->setAuthStatus( Login::AUTH_LOGIN_TOKEN_INVALID );
$this->authenticate( $nonce );
$result = true;
}
return $result;
}
private function getLoginMessage( $nonce ) {
global $AUTH_LOGIN_SERVICE;
global $DEFAULT_APP_TITLE;
$path = realpath( dirname( __FILE__ ) );
$html = file_get_contents( "$path/email/login.html" );
$html = str_replace( '${AUTH_LOGIN_SERVICE}', $AUTH_LOGIN_SERVICE, $html );
$html = str_replace( '${AUTH_LOGIN_TOKEN}', $nonce, $html );
$html = str_replace( '${DEFAULT_APP_TITLE}', $DEFAULT_APP_TITLE, $html );
return $html;
}
private function generateLoginNonce() {
return bin2hex( openssl_random_pseudo_bytes( 16 ) );
}
private function getAuthStatus() {
return $this->auth_status;
}
private function setAuthStatus( $status ) {
$this->auth_status = $status;
}
private function getAuthStatusMessage() {
return $this->auth_status_map[ $this->getAuthStatus() ];
}
protected function isEditable() {
return true;
}
protected function handleRequest() {
$command = $this->getCommand();
$subcommand = $this->getSubCommand();
if( $command === "login" && $subcommand === "transmit" ) {
$this->transmitNonce();
}
else {
if( $this->authenticateNonce() ) {
global $BASE_ACCOUNT;
if( $this->redirect( $BASE_ACCOUNT, $this->getId() ) ) {
return;
}
}
}
$this->render( false );
}
}