Welcome Guest, Not a member yet? Register   Sign In
Login system need a little help
#1

[eluser]Wathfea[/eluser]
Hello everyone,

I'm trying to develop a login system but I stucked a little. If you know how to make it better, faster just let me know. I'm using HMVC. My main controller is the login controller. If the user log in with the good data redirect them to the admin site. I need two groups: super admin and normal admin. If someone try to login and it's fail more then 3 times the account have to locked down. I also would like to salt the password. Now I have a code, but It's not the best, the error msgs not shows well and I think my redirect method could be different. If someone could help it would be great full. And one more thing, I don't want to use a ready library like Ion Auth I would like to solve it with my own. Smile Here is the code:

Login controller:
Code:
<?php
if (!defined('BASEPATH'))
    exit('No direct script access allowed');

class Login extends MX_Controller {

    function __construct() {
        parent::__construct();
        //Load model
        $this -> load -> model('login_model');
    }

    public function index() {
        //Check for the user session exist or not
        $user = $this -> session -> userdata('acc_username');
        if (!empty($user)) {
            redirect('admin');
        } else {
            //Prepare post form data
            $this -> form_validation -> set_rules('username', 'Username', 'required|min_length[4]|max_length[45]|trim|xss_clean');
            $this -> form_validation -> set_rules('password', 'Password', 'required|min_length[4]|max_length[50]|trim|xss_clean');
    
            if ($this->form_validation->run() == FALSE)
            {
                $this->load->view('login_view');
            }
            else {
                // then validation passed. Get data from the db
                $res = $this -> login_model -> verify_user($this -> input -> post('username'), $this -> input -> post('password'));
                if ($res !== false) {
                    // login was correct we can set the session and redirect the user
                    $data = array('acc_username' => $res -> username, 'acc_priv' => $res -> priv, 'acc_id' => $res -> id);
                    $this -> session -> set_userdata($data);
                    redirect('admin');
                }
                else {
                    // login failed , check why?
                    $res = $this -> login_model -> chk_lock($this -> input -> post('username'));
                    if ($res !== false) {
                        // user locked
                        $data['error_message'] = "Your account locked";
                        $this -> load -> view('login_view_error', $data);
                        } else {
                            $data['error_message'] = "The username or password you entered is incorrect";
                            $this -> load -> view('login_view_error', $data);
                        }
                }                
            }
        }
    }
    
    public function logout() {
        $this->session->sess_destroy();
        $this->index();
    }

}

Login modell:
Code:
<?php
if (!defined('BASEPATH'))
    exit('No direct script access allowed');

class Login_model extends CI_Model {

    function __construct() {
        parent::__construct();
    }

    //Create salt
    protected function _create_salt()
    {
        return sha1(random_string('alnum', 32));
    }
    
    //Check post user data
    public function verify_user($username, $password) {
       //Get the salt
       $q = $this -> db -> where('username', $username) -> select('salt') -> limit(1) -> get('users');
       if ($q -> num_rows() > 0) {
               $row = $q -> row();
               $salt = $row->salt;
            }        
      
       $q = $this -> db -> where('username', $username) -> where('password', sha1($password.$salt)) -> where('locked_status !=', "yes") -> limit(1) -> get('users');
       if ($q -> num_rows() > 0) {
            // person has account with us
            return $q -> row();
        } else {
            $q = $this -> db -> where('username', $username) -> limit(1) -> get('users');
            $user_row = $q -> row();
            if ( $user_row->login_attempt >= 3) {
                //Lock the user out
                $this->db->set('locked_status', '"yes"', FALSE);
                $this->db->where('username', $username)->update('users');
                
                return false;
            } else {
                // failed login set attempt +1
                $this->db->set('login_attempt', 'login_attempt+1', FALSE);
                $this->db->where('username', $username)->update('users');
                
                return false;                
            }
        }
        return false;
    }

    //Check for the user is locked or not
    public function chk_lock($username) {

         $q = $this -> db -> where('username', $username) -> where('locked_status = "yes" ') -> limit(1) -> get('users');  
         if ($q -> num_rows() > 0) {
            // person locked
            return  $q -> row();
        }
        return false;
    }
      
}
#2

[eluser]TheFuzzy0ne[/eluser]
Welcome to the CodeIgniter forums!

I think you should take a look at the existing auth libraries out there, and apply what you see to your HMVC module. You login model is generating a random salt each time, which isn't going to help, since the SHA-1 hash will be different each time. The salt should be generated once only -- when the account is first created. It then needs to be stored in the database.

Also, I would suggest you move most of that controller logic into you model. Your model can handle everything, including locking down the login functionality for someone who has tried to login with the wrong credentials more than x number of times.

Essentially, your controller might look something like this:
Code:
class Login extends MX_Controller {

    function index()
    {
        $this->load->library('form_validation');
        
        $this->form_validation->set_rules('username', '', 'callback_try_login');
        
        // If validation passes, redirect to the admin panel...
        if ($this->form_validation->run())
        {
            redirect('/admin');
        }
        
        // ... Otherwise, load the view.
        $this->load->view('login_view');
    }
    
    function logout()
    {
        // Just unset the user ID, and redirect back to the login page.
        // You may want to set a flash data message here to display to the user.
        $this->session->unset_userdata('user_id');
        redirect('/login');
    }
    
    function try_login()
    {
        $username = $this->input->post('username', TRUE);
        $password = $this->input->post('password', TRUE);
        
        // Do we have both a username and a password?
        if ( ! $username OR ! $password)
        {
            $this->form_validation->set_message('try_login', 'Username and password are required!');
            return FALSE;
        }
        
        // Were we able to log the user in?
        if ( ! $this->login_model->do_login($username, $password))
        {
            $this->form_validation->set_message('try_login', 'Invalid username and/or password!');
            return FALSE;
        }
        
        return TRUE;
    }
}

And your model might look something like this:
Code:
class Login_model extends CI_Model {

    public $allowed_failed_logins = 3;

    function try_login($username = '', $password = '')
    {
        // Try to get the user from the database.
        $user = $this->db
            ->select('id, username, password_hash, salt, failed_logins')
            ->where('username', $username)
            ->get('users');
            
        // If we don't have a user, halt in ze name of ze law!
        if ( ! $user)
        {
            return FALSE;
        }
        
        // Check that the number of allowed failed logins hasn't been exceded.
        if ($user['failed_login'] > $this->allowed_failed_logins)
        {
            return FALSE;
        }
        
        // Does the password has match the one in the database?
        if ($user['password_hash'] != sha1($password.$user['salt']))
        {
            // Increment the failed login count again.
            $this->increment_failed_logins($user['id']);
            return FALSE;
        }
        
        // If we made it this far, all is well, so log the user in.
        
        // Reset the failed login attempts if necessary.
        if ($user['failed_logins'] > 0)
        {
            $this->reset_failed_logins($user['id']);
        }
        
        // Set some stuff.
        $this->session->set_userdata(array(
            'username' => $username,
            'user_id' => $user['id'],
        ));
        
        return TRUE;
    }
    
    function increment_failed_logins($user_id = 0)
    {
        $this->db
            ->set('`failed_logins`', '`failed_logins` + 1', FALSE)
            ->where('id', $user_id)
            ->update('users');
    }
    
    function reset_failed_logins($user_id = 0)
    {
        $this->db
            ->set('failed_logins', '0')
            ->where('id', $user_id)
            ->update('users');
    }
}

The above code is untested, and thrown together in about 5 minutes.

Note how the salt is pulled from the database and used to hash the password. To log a user out, I simply unset the user_id. This can be useful in some situations, such as when you have a "remember me" checkbox on your Web site. Rather than destroying the data, we preserve it. You could take a different approach if you wanted to, and unset everything, or everything but the username. It's your call. You could also make some improvements to my code, since it was only a quick example.

Also, you should find the code is much easier to follow. In you controller, I haven't bothered validating each field separately. You can if you want, but I think it's easier to validate them both at the same time for logins.

Hope this helps.
#3

[eluser]TheFuzzy0ne[/eluser]
If you want to tell the user their account is locked, you could pass a third parameter into the try_login() model method by reference, which contain the error string. You can then output the error string in your view using form_error() or validation_errors().




Theme © iAndrew 2016 - Forum software by © MyBB