From a7ed6672e18773ff07eb1c01fcf8e38b4828de11 Mon Sep 17 00:00:00 2001 From: Deon George Date: Thu, 2 Oct 2014 15:33:07 +1000 Subject: [PATCH] Login/Activation tested --- classes/Controller/Account.php | 4 ++ classes/Controller/User/Account.php | 4 ++ classes/lnApp/Auth/ORM.php | 60 +++++++++++----- classes/lnApp/Controller/Account.php | 14 ++++ classes/lnApp/Controller/Login.php | 67 ++++++++++-------- classes/lnApp/Controller/TemplateDefault.php | 2 + classes/lnApp/Controller/User/Account.php | 72 ++++++++++++++++++++ classes/lnApp/Model/Account.php | 12 +++- classes/lnApp/Model/Auth/UserDefault.php | 1 + config/config.php | 4 +- email/login_activate.txt | 2 +- messages/models/account.php | 18 +++++ views/account/user/resetpassword.php | 12 ++++ views/login/activate.php | 30 ++++++++ views/login/activate_resend.php | 19 ++++++ views/login/reset.php | 31 +++++++++ views/login/reset_sent.php | 29 ++++++++ views/theme/bootstrap/navbar.php | 10 ++- 18 files changed, 340 insertions(+), 51 deletions(-) create mode 100644 classes/Controller/Account.php create mode 100644 classes/Controller/User/Account.php create mode 100644 classes/lnApp/Controller/Account.php create mode 100644 classes/lnApp/Controller/User/Account.php create mode 100644 messages/models/account.php create mode 100644 views/account/user/resetpassword.php create mode 100644 views/login/activate.php create mode 100644 views/login/activate_resend.php create mode 100644 views/login/reset.php create mode 100644 views/login/reset_sent.php diff --git a/classes/Controller/Account.php b/classes/Controller/Account.php new file mode 100644 index 0000000..8bb56ee --- /dev/null +++ b/classes/Controller/Account.php @@ -0,0 +1,4 @@ + diff --git a/classes/Controller/User/Account.php b/classes/Controller/User/Account.php new file mode 100644 index 0000000..961ca6f --- /dev/null +++ b/classes/Controller/User/Account.php @@ -0,0 +1,4 @@ + diff --git a/classes/lnApp/Auth/ORM.php b/classes/lnApp/Auth/ORM.php index d1bf280..2a9c8cf 100644 --- a/classes/lnApp/Auth/ORM.php +++ b/classes/lnApp/Auth/ORM.php @@ -23,6 +23,22 @@ abstract class lnApp_Auth_ORM extends Kohana_Auth_ORM { 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. * @@ -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 ($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 $oldsess = session_id(); @@ -65,10 +84,6 @@ abstract class lnApp_Auth_ORM extends Kohana_Auth_ORM { $o->set('session_id',session_id()) ->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; } @@ -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(); } + /** + * 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 public function hash($str) { 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: * 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 (is_object($uo) AND ($uo instanceof Model_Account) AND $uo->loaded()) - if (! empty($role)) { - 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; + $status = TRUE; return $status; } diff --git a/classes/lnApp/Controller/Account.php b/classes/lnApp/Controller/Account.php new file mode 100644 index 0000000..8f45d09 --- /dev/null +++ b/classes/lnApp/Controller/Account.php @@ -0,0 +1,14 @@ + diff --git a/classes/lnApp/Controller/Login.php b/classes/lnApp/Controller/Login.php index 15361f7..8091309 100644 --- a/classes/lnApp/Controller/Login.php +++ b/classes/lnApp/Controller/Login.php @@ -25,21 +25,15 @@ class lnApp_Controller_Login extends Controller_TemplateDefault { HTTP::redirect('login'); elseif ($ao->activate_code() == $this->request->post('code')) { - $go = ORM::factory('Group',array('name'=>'Registered Users')); - - $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(); + $ao->verified = TRUE; + $ao->save(); SystemMessage::factory() ->title(_('Account Activated')) ->type('info') ->body(_('Your account has been activated.')); + + HTTP::redirect('welcome'); } } @@ -82,6 +76,7 @@ class lnApp_Controller_Login extends Controller_TemplateDefault { // Log the password reset $ao->log('Activation code sent'); + Session::instance()->set('activate',$ao); } } @@ -167,10 +162,33 @@ class lnApp_Controller_Login extends Controller_TemplateDefault { $ao = ORM::factory('Account',$this->request->param('id')); 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()) - HTTP::redirect('login'); + if ($ao->loaded()) { + $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() ->type('form-horizontal') @@ -184,33 +202,26 @@ class lnApp_Controller_Login extends Controller_TemplateDefault { */ public function action_reset() { // Minutes to keep our token - $token_expire = 15; - $co = Company::instance(); - - // If user already signed-in - if (Auth::instance()->logged_in()) - HTTP::redirect('welcome/index'); + $token_expire = 15*60; // If the user posted their details to reset their password if ($this->request->post()) { // If the username is correct, create a method token if ($ao=ORM::factory('Account',array('email'=>$this->request->post('username'))) AND $ao->loaded()) { - $mmto = ORM::factory('Module_Method_Token') - ->method(array('account','user:resetpassword')) - ->account($ao) - ->uses(2) - ->expire(time()+$token_expire*60); + $token = $ao->token($token_expire,'account','user:resetpassword',2); + + if ($token) { + $co = Company::instance(); - if ($mmto->generate()) { // Send our email with the token $email = Email::factory('login_reset') ->set('SITE',URL::base(TRUE,TRUE)) ->set('SITE_ADMIN',$co->admin()->name()) - ->set('TOKEN',$mmto->token) + ->set('TOKEN',$token) ->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->subject = 'Login Reset Token for '.$co->name(); $email->deliver(); diff --git a/classes/lnApp/Controller/TemplateDefault.php b/classes/lnApp/Controller/TemplateDefault.php index 12905e7..5973cfb 100644 --- a/classes/lnApp/Controller/TemplateDefault.php +++ b/classes/lnApp/Controller/TemplateDefault.php @@ -68,6 +68,8 @@ abstract class lnApp_Controller_TemplateDefault extends Kohana_Controller_Templa * @uses meta */ public function before() { + $this->ao = Auth::instance()->get_user(); + // 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()) throw HTTP_Exception::factory(412,_('Unable to fulfil request.')); diff --git a/classes/lnApp/Controller/User/Account.php b/classes/lnApp/Controller/User/Account.php new file mode 100644 index 0000000..2858147 --- /dev/null +++ b/classes/lnApp/Controller/User/Account.php @@ -0,0 +1,72 @@ +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)); + } +} +?> diff --git a/classes/lnApp/Model/Account.php b/classes/lnApp/Model/Account.php index 9d92f04..e6b2f49 100644 --- a/classes/lnApp/Model/Account.php +++ b/classes/lnApp/Model/Account.php @@ -50,7 +50,7 @@ abstract class lnApp_Model_Account extends Model_Auth_UserDefault { } 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) { + if (! class_exists('Model_Account_Log')) + return TRUE; + // Log a message for this account $alo = ORM::factory('Account_Log'); $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 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 */ diff --git a/classes/lnApp/Model/Auth/UserDefault.php b/classes/lnApp/Model/Auth/UserDefault.php index 1df4d00..a68d83b 100644 --- a/classes/lnApp/Model/Auth/UserDefault.php +++ b/classes/lnApp/Model/Auth/UserDefault.php @@ -14,6 +14,7 @@ abstract class lnApp_Model_Auth_UserDefault extends Model_Auth_User { public function rules() { return array( 'email' => array( + array(array($this, 'unique'), array('email', ':value')), array('not_empty'), array('min_length', array(':value', 4)), array('max_length', array(':value', 127)), diff --git a/config/config.php b/config/config.php index ad6a80a..a7fa57c 100644 --- a/config/config.php +++ b/config/config.php @@ -12,10 +12,12 @@ return array( '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 'language' => 'auto', // Site Language '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 'time_format' => 'H:i:s', // Site Time Format ); diff --git a/email/login_activate.txt b/email/login_activate.txt index e7833af..d1898af 100644 --- a/email/login_activate.txt +++ b/email/login_activate.txt @@ -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. -$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. diff --git a/messages/models/account.php b/messages/models/account.php new file mode 100644 index 0000000..96a5617 --- /dev/null +++ b/messages/models/account.php @@ -0,0 +1,18 @@ +array( + 'unique'=>'That email address is either already registered or is reserved', + ), +); +?> diff --git a/views/account/user/resetpassword.php b/views/account/user/resetpassword.php new file mode 100644 index 0000000..0dfb411 --- /dev/null +++ b/views/account/user/resetpassword.php @@ -0,0 +1,12 @@ +
+ Reset Password + + 'Password','class'=>'col-md-3','placeholder'=>'Login Password','type'=>'password','required','id'=>'password','data-minlength'=>8)); ?> + 'Confirm','class'=>'col-md-3','placeholder'=>'Confirm Password','type'=>'password','required','data-match'=>'#password','data-match-error'=>'Whoops, these don\'t match!')); ?> +
+ +
+
+ +
+
diff --git a/views/login/activate.php b/views/login/activate.php new file mode 100644 index 0000000..6481376 --- /dev/null +++ b/views/login/activate.php @@ -0,0 +1,30 @@ +
+ Activate Account + +
+

You should have received an email with a pass code. Please enter that pass code here.

+ + loaded()) : ?> + + + +
+ + +
+ + +
+ + +
+ Need to it? +
+
+ +
+
+ + +
+
diff --git a/views/login/activate_resend.php b/views/login/activate_resend.php new file mode 100644 index 0000000..36742d9 --- /dev/null +++ b/views/login/activate_resend.php @@ -0,0 +1,19 @@ +
+ Send Activation Code + +
+
+
+ + +
+
+
+
+ +
+
+ + +
+
diff --git a/views/login/reset.php b/views/login/reset.php new file mode 100644 index 0000000..b34612d --- /dev/null +++ b/views/login/reset.php @@ -0,0 +1,31 @@ +
+
+
+
+
+
+
Reset Password
+
+ +
+

If you have forgotten your password, we can issue you a temporary access code via email that will allow you to change your password.

+ +
+

To start this process, please enter your Username. If you dont know your Username, please contact us.

+ +
+ + +
+
+ +
+
+ +
+
+
+
+
+
+
diff --git a/views/login/reset_sent.php b/views/login/reset_sent.php new file mode 100644 index 0000000..7d31a98 --- /dev/null +++ b/views/login/reset_sent.php @@ -0,0 +1,29 @@ +
+
+
+
+
+
+
Reset Password
+
+ +
+
+

You should have received an email with a pass code. Please enter that pass code here.

+ +
+ + +
+
+ +
+
+ +
+
+
+
+
+
+
diff --git a/views/theme/bootstrap/navbar.php b/views/theme/bootstrap/navbar.php index 9ad033e..e32d569 100644 --- a/views/theme/bootstrap/navbar.php +++ b/views/theme/bootstrap/navbar.php @@ -5,7 +5,7 @@
  • Faq
  • - logged_in()) : ?> + logged_in()) : ?>
  • Login
  • Login
  • @@ -13,6 +13,10 @@ -