<?php
/**
* Improved version of BasicAuth.
* Login/password retrieved from DB table
* Functionality of password recovery and new user registration is included.
*
* Passwords could be stored as plain text or sha1 hash. By default they are encrypted.
* If you do not want them to be encrypted - use Auth::setEncrypted(false)
*
* If you want to use password recovery feature:
* - create a fake page for password recovery (it is needed!)
* - add a link to password recovery page: Auth::addPwdRecoveryLink()
* - specify a page name to password recovery in your config.php ($config['auth']['pwd_recovery']['page'])
* - specify a table name which will be used for password recovery process ($config['auth']['pwd_recovery']['table'])
* This table structure is:
* CREATE TABLE pwd_recovery_request (
* id int(11) unsigned NOT NULL auto_increment,
* user_id int(11) unsigned NOT NULL default '0',
* email varchar(32) NOT NULL default '',
* expire datetime default NULL,
* changed int(1) default '0',
* changed_dts datetime default NULL,
* PRIMARY KEY (id)
* ) ENGINE=MyISAM
* - specify a period in minutes in which link to recovery will be actual ($config['auth']['pwd_recovery']['timeout'])
*
* If you want to use register feature:
* - create a page with the user registration data
* - add a link to login form: Auth::addRegisterLink()
* - specify register page in your config.php
*
* Created on 04.07.2006 by *Camper* (hide@address.com)
*/
class DBAuth extends BasicAuth{
public $dq;
protected $email_field;
protected $secure = true;
protected $can_register = false;
protected $show_lost_password = false;
protected $doRecovery=false;
protected $from='';
protected $subj='Password recovery';
function init(){
parent::init();
//process recovery and register
if($this->api->page==$this->api->getConfig('auth/register_page','none')){
$this->processRegister();
}
if($this->api->page==$this->api->getConfig('auth/pwd_recovery/page','none')){
$this->doRecovery=true;
}
$this->api->addHook('pre-exec',array($this,'check'));
}
function processRegister(){
$p=$this->add('Page');
$p->template->loadTemplate('empty');
$p->add('page_'.$this->api->getConfig('auth/register_page'), null, 'Content');
$p->downCall('render');
echo $p->template->render();
exit;
}
function processRecovery(){
$p=$this->add('Page');
$p->template->loadTemplate('empty');
$p->template->set('page_title', 'Password recovery');
if($_GET['key']){
//user clicked link in e-mail
$this->api->stickyGET('rp');
$this->api->stickyGET('key');
$id=$_GET['rp'];
$key=$_GET['key'];
$row=$this->api->db->getHash("select * from ".DTP.$this->api->getConfig('auth/pwd_recovery/table').
" where id=$id and changed=0");
//looking for the key in DB and checking expiration dts
$db_key=($row['id'])?sha1($row['id'].$row['email'].strtotime($row['expire'])):'';
$can_change=$db_key==$key&&strtotime($row['expire'])>time();
if($can_change){
//displaying changepass form
$username=$this->api->db->getOne("select $this->name_field from ".
$this->dq->args['table']." where id=".$row['user_id']);
$form=$p->frame('Content', "Change password for $username")->add('Form', null, 'content');
$form
->addField('hidden', 'rp_id')
->addField('password', 'password', 'Enter new password')
->validateField('strlen($this->get())>=6', 'Password is too short')
->addField('password', 'password2', 'Confirm new password')
->validateField('$this->get()==$this->owner->get(\'password\')',
'Confirmation differs from password')
->addField('checkbox', 'send', 'Send me new password by e-mail')
->addSubmit('Change')
;
if(isset($_REQUEST['rp']))$form->set('rp_id', $_REQUEST['rp']);
if($form->isSubmitted()){
//getting a user id
$user_id=$this->api->db->getOne("select user_id from ".DTP.
$this->api->getConfig('auth/pwd_recovery/table').
" where id = ".$form->get('rp_id'));
//changing password
$this->api->db->query("update ".$this->dq->args['table']." set ".$this->pass_field.
" = '".$this->encrypt($form->get('password')).
"' where id = $user_id");
//storing info about changes
$this->api->db->query("update ".DTP.$this->api->getConfig('auth/pwd_recovery/table').
" set changed=1, changed_dts=SYSDATE()" .
" where id=".$form->get('rp_id'));
$p->add('Text', null, 'Content')->set('<center>Password changed succefully</center>');
if($form->get('send')=='Y')$this->sendPassword($user_id, $form->get('password'));
unset($this->api->sticky_get_arguments['rp']);
unset($this->api->sticky_get_arguments['key']);
}
}else{
//denial page
unset($this->api->sticky_get_arguments['rp']);
unset($this->api->sticky_get_arguments['key']);
$p->frame('Content', 'Request error')->add('Text', null, 'content')
->set("Sorry, this page is not valid. Activation period might have been expired." .
" <a href=".
$this->api->getDestinationURL($this->api->getConfig('auth/pwd_recovery/page')).
">Click here</a> if You want to repeat Your request.");
}
}else{
//displaying a form with username for password recovery
$form=$p->frame('Content', 'Password recovery')->add('Form', 'pwd_recovery_form', 'content');
$form
->addComment('To restore Your password please enter the '.$this->title_name.' specified at registration')
->addField('line', 'username', $this->title_name)->setNotNull()
->addSubmit('Submit')
;
if($form->isSubmitted()){
$user=$this->api->db->getHash("select id, $this->name_field, $this->email_field from ".
$this->dq->args['table']." where $this->name_field='".
$form->get('username')."'");
if(sizeof($user)!=0){
//generating and sending e-mail
$this->sendChangeLink($user['id'], $user[$this->name_field], $user[$this->email_field]);
$p->add('Text', null, 'Content')->set("<div align=center>An e-mail with instruction " .
"to restore password has been sent" .
" to user '".$form->get('username')."'</div>");
}else{
$form->elements['username']->displayFieldError("User with a name you specified have not been found. Please try again");
}
}
}
$p->add('Text', 'back', 'Content')->set("<div align=center><a href=".
$this->api->getDestinationURL('Index').">Back to login</a></div>");
$p->downCall('render');
echo $p->template->render();
exit;
}
function check(){
if($this->doRecovery===true)$this->processRecovery();
else parent::check();
}
protected function sendPassword($user_id, $password){
$mail=$this->add('SMlite')->loadTemplate($this->api->getConfig('auth/mail/pwd_recovery_pwd'),'.txt');
$server=$this->getServerName();
$address=$this->api->db->getOne("select $this->email_field from ".$this->dq->args['table'].
" where id=".$user_id);
$username=$this->api->db->getOne("select $this->name_field from ".$this->dq->args['table'].
" where id=".$user_id);
//combining a message
$from=$this->from==''?"noreply@$server":$this->from;
$mail->trySet('server', $server);
$mail->trySet('username', $username);
$mail->trySet('password',$password);
$mail->trySet('from', $from);
$subj=$mail->get('subject');
//$subj=$subj[0];
$mail->tryDel('subject');
mail($address,$subj[0],null,str_replace("\n","\r\n",$mail->render()));
}
protected function sendChangeLink($user_id, $username, $address){
//adding a DB record with a key to a change password page
$table=DTP.$this->api->getConfig('auth/pwd_recovery/table');
$expire=time()+$this->api->getConfig('auth/pwd_recovery/timeout', 15)*60;
$this->api->db->query("insert into $table (user_id, email, expire) values($user_id, '$address', " .
"'".date('Y-m-d H:i:s', $expire)."')");
$id=mysql_insert_id();
$server=$this->getServerName();
//combining a message
$link=$this->getServerName(true).dirname($_SERVER['PHP_SELF'])."/".
$this->api->getDestinationURL(null, array('rp'=>$id, 'key'=>sha1($id.$address.$expire)));
$mail=$this->add('SMlite')->loadTemplate($this->api->getConfig('auth/mail/pwd_recovery_link'),'.txt');
$address=$this->api->db->getOne("select $this->email_field from ".$this->dq->args['table'].
" where id=".$user_id);
//combining a message
$from=$this->from==''?"noreply@$server":$this->from;
$mail->trySet('server', $server);
$mail->trySet('link',$link);
$mail->trySet('from', $from);
$mail->trySet('timeout', $this->api->getConfig('auth/pwd_recovery/timeout', 15));
$mail->trySet('username', $username);
$subj=$mail->get('subject');
//$subj=$subj[0];
$mail->tryDel('subject');
mail($address,$subj[0],null,str_replace("\n","\r\n",$mail->render()));
}
function setEncrypted($secure=true){
$this->secure=$secure;
return $this;
}
function setPassword($password){
return false;
}
function setSource($table,$login='login',$password='password',$email='email'){
$this->name_field = $login;
$this->pass_field = $password;
$this->email_field = $email;
$this->dq=$this->api->db->dsql()
->table($table)
->field($this->name_field)
->field($this->pass_field)
;
return $this;
}
function setEmailParams($from, $subj){
/**
* Sets subj and from for e-mail sent by password recovery subsystem
* As we going to use this class for many projects it is cool to customize class output
*/
$this->from=$from;
$this->subj=$subj;
}
function verifyCredintials($user,$password){
$data=$this->dq->where($this->name_field, $user)->do_getHash();
$this->info=array_merge($this->recall('info',array()), $this->dq->do_getHash());
$this->memorize('info',$this->info);
return(sizeof($data)>0&&($data[$this->pass_field]==$password||$data[$this->pass_field]==sha1($password)));
}
private function encrypt($str){
return $this->secure?sha1($str):$str;
}
function loggedIn(){
parent::loggedIn();
}
function showLoginForm(){
$p=parent::showLoginForm();
if($this->can_register){
$this->form->add('RegisterLink', null, 'form_body');
}
if($this->show_lost_password){
$this->form->add('PwdRecoveryLink', null, 'form_body');
}
return $p;
}
function addRegisterLink(){
$this->can_register=true;
return $this;
}
function addPwdRecoveryLink(){
$this->show_lost_password=true;
return $this;
}
function getServerName($full=false){
/**
* Static method
* Returns server name by these rules:
* 1) if there is $_SERVER['HTTP_X_FORWARDED_HOST'] set - takes first server from the line
* 2) else returns $_SERVER['HTTP_HOST']
*/
$server='';
if($_SERVER['HTTP_X_FORWARDED_HOST']){
$server=$_SERVER['HTTP_X_FORWARDED_HOST'];
$server=strpos(',',$server)==0?$server:split(',',$server);
if(is_array($server))$server=$server[0];
}
if($server==''||strtolower($server)=='unknown')$server=$_SERVER['HTTP_HOST'];
return $full?$server:str_replace('www.', '', $server);
}
}
class RegisterLink extends Text{
function init(){
parent::init();
$this->set('<tr><td align=left><a href='.
$this->api->getDestinationURL($this->api->getConfig('auth/register_page')).'>Register</a></td><td></td></tr>');
}
}
class PwdRecoveryLink extends Text{
function init(){
parent::init();
$this->set('<tr><td align=left><a href='.
$this->api->getDestinationURL($this->api->getConfig('auth/pwd_recovery/page')).
'>Lost password</a></td><td></td></tr>');
}
}