Login/Activation tested

This commit is contained in:
Deon George 2014-10-02 15:33:07 +10:00
parent 037633f084
commit a7ed6672e1
18 changed files with 340 additions and 51 deletions

View File

@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Controller_Account extends lnApp_Controller_Account {}
?>

View File

@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Controller_User_Account extends lnApp_Controller_User_Account {}
?>

View File

@ -23,6 +23,22 @@ abstract class lnApp_Auth_ORM extends Kohana_Auth_ORM {
parent::__construct($config); parent::__construct($config);
} }
/**
* Get the user that a token applies to
*
* This will check that the token is valid (not expired and for the request)
*
* @param $token The token
* @return Model_Account|NULL The user that the token is valid for.
*/
private function _get_token_user($token) {
list($id,$key) = explode(':',$token,2);
$uo = ORM::factory('Account',$id);
return ($uo->token(NULL,NULL,NULL,NULL) == $token) ? $uo : NULL;
}
/** /**
* Logs a user in. * Logs a user in.
* *
@ -50,6 +66,9 @@ abstract class lnApp_Auth_ORM extends Kohana_Auth_ORM {
// If we have the right password, we'll check the status of the account // If we have the right password, we'll check the status of the account
if ($user->password === $password AND $user->active) { if ($user->password === $password AND $user->active) {
if (! $user->activated())
HTTP::redirect(URL::link('user','account/activate'));
// Record our session ID, we may need to update our DB when we get a new ID // Record our session ID, we may need to update our DB when we get a new ID
$oldsess = session_id(); $oldsess = session_id();
@ -65,10 +84,6 @@ abstract class lnApp_Auth_ORM extends Kohana_Auth_ORM {
$o->set('session_id',session_id()) $o->set('session_id',session_id())
->update(); ->update();
//@TODO
if (! $user->has_any('group',ORM::factory('Group',array('name'=>'Registered Users'))->list_childgrps(TRUE)))
HTTP::redirect(URL::link('user','account/activate'));
return TRUE; return TRUE;
} }
@ -90,6 +105,28 @@ abstract class lnApp_Auth_ORM extends Kohana_Auth_ORM {
return is_null($x=$this->get_user()) ? ORM::factory('Group')->where('id','=',0)->find_all() : $x->groups(); return is_null($x=$this->get_user()) ? ORM::factory('Group')->where('id','=',0)->find_all() : $x->groups();
} }
/**
* Gets the currently logged in user from the session.
* Returns NULL if no user is currently logged in.
*
* @param boolean Check token users too
* @return mixed
*/
public function get_user($default=NULL,$tokenuser=TRUE) {
// If we are a CLI, we are not logged in
if (PHP_SAPI === 'cli')
throw new Kohana_Exception('Calling :method from the CLI is not allowed!',array(':method'=>__METHOD__));
// Get the current user
$uo = parent::get_user($default);
// If we are not logged in, see if there is token for the user
if (is_null($uo) AND $tokenuser AND ($token=Session::instance()->get('token')) OR (! empty($_REQUEST['token']) AND $token=$_REQUEST['token']))
$uo = $this->_get_token_user($token);
return $uo;
}
// Override Kohana Auth requirement to have a hash_key // Override Kohana Auth requirement to have a hash_key
public function hash($str) { public function hash($str) {
switch ($this->_config['hash_method']) { switch ($this->_config['hash_method']) {
@ -100,7 +137,7 @@ abstract class lnApp_Auth_ORM extends Kohana_Auth_ORM {
} }
/** /**
* OSB authentication is controlled via database queries. * lnApp authentication is controlled via database queries.
* *
* This method can be used to test two situations: * This method can be used to test two situations:
* 1) Is the user logged in? ($role == FALSE) * 1) Is the user logged in? ($role == FALSE)
@ -121,18 +158,7 @@ abstract class lnApp_Auth_ORM extends Kohana_Auth_ORM {
// If we are not a valid user object, then we are not logged in // If we are not a valid user object, then we are not logged in
if (is_object($uo) AND ($uo instanceof Model_Account) AND $uo->loaded()) if (is_object($uo) AND ($uo instanceof Model_Account) AND $uo->loaded())
if (! empty($role)) { $status = TRUE;
if (($x = Request::current()->mmo()) instanceof Model)
// If the role has the authorisation to run the method
foreach ($x->group->find_all() as $go)
if ($go->id == 0 OR $uo->has_any('group',$go->list_childgrps(TRUE))) {
$status = TRUE;
break;
}
// There is no role, so the method should be allowed to run as anonymous
} else
$status = TRUE;
return $status; return $status;
} }

View File

@ -0,0 +1,14 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides account management
*
* @package lnApp
* @category Controllers
* @author Deon George
* @copyright (c) 2014 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class lnApp_Controller_Account extends Controller_TemplateDefault {
}
?>

View File

@ -25,21 +25,15 @@ class lnApp_Controller_Login extends Controller_TemplateDefault {
HTTP::redirect('login'); HTTP::redirect('login');
elseif ($ao->activate_code() == $this->request->post('code')) { elseif ($ao->activate_code() == $this->request->post('code')) {
$go = ORM::factory('Group',array('name'=>'Registered Users')); $ao->verified = TRUE;
$ao->save();
$ago = ORM::factory('Account_Group',array('account_id'=>$ao,'group_id'=>$go));
if (! $ago->loaded()) {
$ago->account_id=$ao;
$ago->group_id=$go;
}
$ago->active = TRUE;
$ago->save();
SystemMessage::factory() SystemMessage::factory()
->title(_('Account Activated')) ->title(_('Account Activated'))
->type('info') ->type('info')
->body(_('Your account has been activated.')); ->body(_('Your account has been activated.'));
HTTP::redirect('welcome');
} }
} }
@ -82,6 +76,7 @@ class lnApp_Controller_Login extends Controller_TemplateDefault {
// Log the password reset // Log the password reset
$ao->log('Activation code sent'); $ao->log('Activation code sent');
Session::instance()->set('activate',$ao); Session::instance()->set('activate',$ao);
} }
} }
@ -167,10 +162,33 @@ class lnApp_Controller_Login extends Controller_TemplateDefault {
$ao = ORM::factory('Account',$this->request->param('id')); $ao = ORM::factory('Account',$this->request->param('id'));
if ($this->request->post() AND $ao->values($this->request->post())->changed() AND (! $this->save($ao))) if ($this->request->post() AND $ao->values($this->request->post())->changed() AND (! $this->save($ao)))
$ao->reload()->values($this->request->post()); $ao->reload()->values($this->request->post());
if ($ao->loaded()) if ($ao->loaded()) {
HTTP::redirect('login'); $co = Company::instance();
// Send our email with the token
$email = Email::factory('login_activate')
->set('SITE',URL::base(TRUE,TRUE))
->set('SITE_ADMIN',$co->admin()->name())
->set('CODE',$ao->activate_code())
->set('EMAIL',$ao->email)
->set('ID',$ao->id)
->set('USER_NAME',$ao->name());
$email->to = array('email'=>array($ao->email=>$ao->name()));
$email->from = array('email'=>array($co->admin()->email=>$co->admin()->name()));
$email->subject = 'Please activate your account for '.$co->name();
$email->deliver();
SystemMessage::factory()
->title(_('Account Registered'))
->type('info')
->body(_('Please check your email for more instructions!'));
Session::instance()->set('activate',$ao);
HTTP::redirect('login/activate/'.$ao->id);
}
Block::factory() Block::factory()
->type('form-horizontal') ->type('form-horizontal')
@ -184,33 +202,26 @@ class lnApp_Controller_Login extends Controller_TemplateDefault {
*/ */
public function action_reset() { public function action_reset() {
// Minutes to keep our token // Minutes to keep our token
$token_expire = 15; $token_expire = 15*60;
$co = Company::instance();
// If user already signed-in
if (Auth::instance()->logged_in())
HTTP::redirect('welcome/index');
// If the user posted their details to reset their password // If the user posted their details to reset their password
if ($this->request->post()) { if ($this->request->post()) {
// If the username is correct, create a method token // If the username is correct, create a method token
if ($ao=ORM::factory('Account',array('email'=>$this->request->post('username'))) AND $ao->loaded()) { if ($ao=ORM::factory('Account',array('email'=>$this->request->post('username'))) AND $ao->loaded()) {
$mmto = ORM::factory('Module_Method_Token') $token = $ao->token($token_expire,'account','user:resetpassword',2);
->method(array('account','user:resetpassword'))
->account($ao) if ($token) {
->uses(2) $co = Company::instance();
->expire(time()+$token_expire*60);
if ($mmto->generate()) {
// Send our email with the token // Send our email with the token
$email = Email::factory('login_reset') $email = Email::factory('login_reset')
->set('SITE',URL::base(TRUE,TRUE)) ->set('SITE',URL::base(TRUE,TRUE))
->set('SITE_ADMIN',$co->admin()->name()) ->set('SITE_ADMIN',$co->admin()->name())
->set('TOKEN',$mmto->token) ->set('TOKEN',$token)
->set('TOKEN_EXPIRE_MIN',$token_expire) ->set('TOKEN_EXPIRE_MIN',$token_expire)
->set('USER_NAME',$mmto->account->name()); ->set('USER_NAME',$ao->name());
$email->to = array('email'=>array($mmto->account->email=>$mmto->account->name())); $email->to = array('email'=>array($ao->email=>$ao->name()));
$email->from = array('email'=>array($co->admin()->email=>$co->admin()->name())); $email->from = array('email'=>array($co->admin()->email=>$co->admin()->name()));
$email->subject = 'Login Reset Token for '.$co->name(); $email->subject = 'Login Reset Token for '.$co->name();
$email->deliver(); $email->deliver();

View File

@ -68,6 +68,8 @@ abstract class lnApp_Controller_TemplateDefault extends Kohana_Controller_Templa
* @uses meta * @uses meta
*/ */
public function before() { public function before() {
$this->ao = Auth::instance()->get_user();
// Actions that start with ajax, should only be ajax // Actions that start with ajax, should only be ajax
if (! Kohana::$config->load('debug')->ajax AND preg_match('/^ajax/',Request::current()->action()) AND ! Request::current()->is_ajax()) if (! Kohana::$config->load('debug')->ajax AND preg_match('/^ajax/',Request::current()->action()) AND ! Request::current()->is_ajax())
throw HTTP_Exception::factory(412,_('Unable to fulfil request.')); throw HTTP_Exception::factory(412,_('Unable to fulfil request.'));

View File

@ -0,0 +1,72 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides User Account Update functions
*
* @package lnApp
* @category Controllers/User
* @author Deon George
* @copyright (c) 2009-2013 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class lnApp_Controller_User_Account extends Controller_Account {
protected $secure_actions = array(
'edit'=>TRUE,
'resetpassword'=>TRUE,
);
/**
* Enable User to Edit their Account Details
*/
public function action_edit() {
if ($_POST AND $this->ao->values($_POST)->changed() AND (! $this->save($this->ao)))
$this->ao->reload();
Block::factory()
->title(sprintf('Account: %s',$this->ao->accnum()))
->title_icon('fa-wrench')
->type('form-horizontal')
->body(View::factory('account/user/edit')->set('o',$this->ao));
}
public function action_resetpassword() {
if ($this->request->post()) {
$validation = Validation::factory($this->request->post())
->rule('password','not_empty')
->rule('password','min_length',array(':value',6))
->rule('password_confirm','matches',array(':validation',':field','password'));
// Store our new values
$this->ao->values($this->request->post());
if (! $validation->check())
SystemMessage::factory()
->title(_('Record NOT updated'))
->type('error')
->body(_('Your password didnt pass validation.'));
// Run validation and save
elseif ($this->ao->changed())
if ($this->ao->save()) {
SystemMessage::factory()
->title('Record updated')
->type('success')
->body(_('Your account record has been updated.'));
// Log the password reset
$this->ao->log('Password reset');
}
HTTP::redirect('login');
}
Block::factory()
->title(sprintf('Password Reset: %s',$this->ao->accnum()))
->title_icon('fa-cog')
->id('reset')
->type('form-horizontal')
->body(View::factory('account/user/resetpassword')->set('o',$this->ao));
}
}
?>

View File

@ -50,7 +50,7 @@ abstract class lnApp_Model_Account extends Model_Auth_UserDefault {
} }
public function activated() { public function activated() {
return $this->has_any('group',ORM::factory('Group',array('name'=>'Registered Users'))->list_childgrps(TRUE)); return $this->verified;
} }
/** /**
@ -68,6 +68,9 @@ abstract class lnApp_Model_Account extends Model_Auth_UserDefault {
} }
public function log($message) { public function log($message) {
if (! class_exists('Model_Account_Log'))
return TRUE;
// Log a message for this account // Log a message for this account
$alo = ORM::factory('Account_Log'); $alo = ORM::factory('Account_Log');
$alo->account_id = $this->id; $alo->account_id = $this->id;
@ -110,6 +113,13 @@ abstract class lnApp_Model_Account extends Model_Auth_UserDefault {
return trim(sprintf('%s %s',$this->first_name,$this->last_name)); return trim(sprintf('%s %s',$this->first_name,$this->last_name));
} }
/**
* Return a token valid for this user
*/
public function token($token_expire,$module,$method,$uses) {
return $this->id.':'.md5(sprintf('%s-%s',$this->accnum(),$this->date_last));
}
/** /**
* Search for accounts matching a term * Search for accounts matching a term
*/ */

View File

@ -14,6 +14,7 @@ abstract class lnApp_Model_Auth_UserDefault extends Model_Auth_User {
public function rules() { public function rules() {
return array( return array(
'email' => array( 'email' => array(
array(array($this, 'unique'), array('email', ':value')),
array('not_empty'), array('not_empty'),
array('min_length', array(':value', 4)), array('min_length', array(':value', 4)),
array('max_length', array(':value', 127)), array('max_length', array(':value', 127)),

View File

@ -12,10 +12,12 @@
return array( return array(
'id' => '', // Site ID, for mullti site usage 'id' => '', // Site ID, for mullti site usage
'disabled_noaccess_redirect' => FALSE, // Whether to redirect on no access, or show an error 'disabled_noaccess_redirect' => FALSE, // Whether to redirect on no access, or show an error
'date_format' => 'd-M-Y', // Site Date Format 'date_format' => 'd-M-Y', // Site Date Format
'language' => 'auto', // Site Language 'language' => 'auto', // Site Language
'method_security' => FALSE, // Enable User Based method security 'method_security' => FALSE, // Enable User Based method security
'session_change_trigger'=>array( // Updates to tables to make when our session ID is changed
),
'theme' => 'bootstrap', // Site Theme 'theme' => 'bootstrap', // Site Theme
'time_format' => 'H:i:s', // Site Time Format 'time_format' => 'H:i:s', // Site Time Format
); );

View File

@ -4,7 +4,7 @@ You (or someone who knows your email) has recently submitted a request to activa
If this was you, you can click the link below to activate your account. If this was you, you can click the link below to activate your account.
$SITE$u/login/activate?id=$ID$&email=$EMAIL$&code=$CODE$ $SITE$login/activate/$ID$?email=$EMAIL$
If the link above does not display as a link in your browser, you will need to paste it into the address bar of your browser instead. If the link above does not display as a link in your browser, you will need to paste it into the address bar of your browser instead.

View File

@ -0,0 +1,18 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Account validation messages.
*
* @package lnApp
* @category Validation
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
return array(
'email'=>array(
'unique'=>'That email address is either already registered or is reserved',
),
);
?>

View File

@ -0,0 +1,12 @@
<fieldset>
<legend>Reset Password</legend>
<?php echo Form::input('password','',array('label'=>'Password','class'=>'col-md-3','placeholder'=>'Login Password','type'=>'password','required','id'=>'password','data-minlength'=>8)); ?>
<?php echo Form::input('password_confirm','',array('label'=>'Confirm','class'=>'col-md-3','placeholder'=>'Confirm Password','type'=>'password','required','data-match'=>'#password','data-match-error'=>'Whoops, these don\'t match!')); ?>
</fieldset>
<div class="row">
<div class="col-md-offset-1">
<button type="submit" class="btn btn-primary">Save changes</button>
</div>
</div>

30
views/login/activate.php Normal file
View File

@ -0,0 +1,30 @@
<fieldset>
<legend>Activate Account</legend>
<div class="panel-body">
<p>You should have received an email with a pass code. Please enter that pass code here.</p>
<?php if (is_object($o) AND $o->loaded()) : ?>
<input type="hidden" name="email" value="<?php echo $o->email; ?>"/>
<?php else : ?>
<div class="input-group col-md-3">
<span class="input-group-addon"><i class="fa fa-envelope fa-fw"></i></span>
<input type="email" id="email" name="email" value="" placeholder="Email" class="form-control" required />
</div>
<?php endif ?>
<div class="input-group col-md-3">
<span class="input-group-addon"><i class="fa fa-bomb fa-fw"></i></span>
<input type="text" id="code" name="code" value="" placeholder="Activation Code" class="form-control" required />
</div>
<span class="help-block">Need to <?php echo HTML::anchor(URL::link('','login/activate_resend'),'resend'); ?> it?</span>
</div> <!-- /panel-body -->
</fieldset>
<div class="row">
<div class="col-md-offset-1">
<button type="submit" class="btn btn-primary">Activate</button>
<button type="button" class="btn btn-default">Cancel</button>
</div>
</div>

View File

@ -0,0 +1,19 @@
<fieldset>
<legend>Send Activation Code</legend>
<div class="panel-body">
<div class="form-group">
<div class="input-group col-md-3">
<span class="input-group-addon"><i class="fa fa-envelope fa-fw"></i></span>
<input type="email" id="email" name="email" value="" placeholder="Email" class="form-control" required />
</div>
</div> <!-- /form-group -->
</div> <!-- /panel-body -->
</fieldset>
<div class="row">
<div class="col-md-offset-1">
<button type="submit" class="btn btn-primary">Send</button>
<button type="button" class="btn btn-default">Cancel</button>
</div>
</div>

31
views/login/reset.php Normal file
View File

@ -0,0 +1,31 @@
<div class="center-block" style="width: 400px;">
<div class="content clearfix">
<form method="post" action="<?php echo URL::site('login/reset'); ?>">
<br/>
<div class="panel panel-default">
<div class="panel-heading">
<div class="panel-title">Reset Password</div>
</div>
<div class="panel-body">
<p>If you have forgotten your password, we can issue you a temporary access code via email that will allow you to change your password.</p>
<div class="form-group">
<p>To start this process, please enter your Username. If you dont know your Username, please contact us.</p>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-user fa-fw"></i></span>
<input type="text" id="username" name="username" value="" placeholder="Username" class="form-control" required />
</div>
</div>
<div class="form-group">
<div class="btn-group">
<button class="btn btn-default btn-large">Reset</button>
</div> <!-- /btn-group -->
</div>
</div> <!-- /panel-body -->
</div> <!-- /panel -->
</form>
</div> <!-- /content -->
</div> <!-- /center-block -->

View File

@ -0,0 +1,29 @@
<div class="center-block" style="width: 400px;">
<div class="content clearfix">
<form method="post" action="<?php echo URL::site('login/reset'); ?>">
<br/>
<div class="panel panel-default">
<div class="panel-heading">
<div class="panel-title">Reset Password</div>
</div>
<div class="panel-body">
<div class="form-group">
<p>You should have received an email with a pass code. Please enter that pass code here.</p>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-bomb fa-fw"></i></span>
<input type="text" id="token" name="token" value="" placeholder="Token" class="form-control" required />
</div>
</div>
<div class="form-group">
<div class="btn-group">
<button class="btn btn-default btn-large">Reset</button>
</div> <!-- /btn-group -->
</div>
</div> <!-- /panel-body -->
</div> <!-- /panel -->
</form>
</div> <!-- /content -->
</div> <!-- /center-block -->

View File

@ -5,7 +5,7 @@
<li><a href="<?php echo $x; ?>">Faq</a></li> <li><a href="<?php echo $x; ?>">Faq</a></li>
<?php endif ?> <?php endif ?>
<?php if (class_exists('Auth') AND Auth::instance()->logged_in()) : ?> <?php if (array_key_exists('auth',Kohana::modules()) AND Auth::instance()->logged_in()) : ?>
<li><a href="<?php echo URL::site('login'); ?>">Login</a></li> <li><a href="<?php echo URL::site('login'); ?>">Login</a></li>
<?php else : ?> <?php else : ?>
<li><a href="<?php echo URL::site('login'); ?>" data-toggle="modal" data-target="#modal-login">Login</a></li> <li><a href="<?php echo URL::site('login'); ?>" data-toggle="modal" data-target="#modal-login">Login</a></li>
@ -13,6 +13,10 @@
<?php endif ?> <?php endif ?>
</ul> </ul>
<div class="modal hide fade" id="modal-login" role="dialog" aria-hidden="true"> <div class="modal fade" id="modal-login" role="dialog" aria-hidden="true">
<div class="modal-body"></div> <div class="modal-dialog">
<div class="modal-content">
<div class="modal-body"></div>
</div>
</div>
</div> </div>