User Management
How it works...
This is similar functionality to creating a new user (explained earlier), but instead of writing a row to the users table, we're removing a row based on the user's primary key.
First, we'll need to grab the user's ID. At this point the user's ID is probably coming from an URL, but may also be coming from the post array.
The following code works out how the $id variable is coming in (either from post or by URL) and stores it in the $id variable ready for processing later:
if ($this->input->post()) {
$id = $this->input->post('id');
} else {
$id = $this->uri->segment(3);
}
If public function delete_user() is being called for the first time (that is, it is not being called by a form submission), then the user's primary key is passed to public function delete_user() from within the URL. It is picked up by $this->uri->segment(3)and is sent to the, users/delete_user.php view by assigning it in $this->load->view ('user/delete_user', $data['id]). Within the view, the $id value is written as a hidden HTML form the element.
It is necessary to assign the user's ID as a hidden element in the form because when the form is submitted, public function delete_user() will require the ID of the user.
A the form is being submitted rather than a first load, the ID will not be available from $this->uri->segment(3).
public function delete_user() performs several functions similar to public function new_user(). These are loading the view file, validating any data inputted after submission, and displaying a view.
If public function delete_user() is being called as the result of a form submission, CodeIgniter will begin checking and validating the user input; in this case, submitted input consists only of the users ID, which is written as a hidden form element in the view. The first line of the function loads the necessary library to enable checking the user's input: $this-
>library('form_validation'), and our error delimiters are set with the function, set_error_deimiters(). The user ID is then checked against the criteria we specify.
A full list of validation criteria options are available at:
30
Chapter 2
http://ellislab.com/codeigniter/user-guide/libraries/form_validation.
html. We will also discuss form validation in greater detail in Chapter 5, Managing Data In and Out.
If validation isn't passed (the input from the user didn't meet the requirements we set), then $this->form_validation->run() will return FALSE, and the form will be displayed again.
Once validation is passed ($this->form_validation->run() returns TRUE), then we'll package up the input into an array: $data. As we're using Active Record to interact with the database, the keys of the $data array must match the column names of our database table.
The $data array is then sent to the Users_model for deletion from the database using the syntax: $this->Users_model->delete_user($id).
Generating passwords with CodeIgniter
There are two ways to explain this. As this is a recipe book, I'm going to give you the structure for a user to register (part of this process is creating a hash from the password the user will provide) and also the signin form (part of this process is to validate a password against a hash). But I'm aware that you won't necessarily need all the following files, the lines which focus on password hashing in the following examples. This way, you can quickly see how the process works and apply it to your situation.
Getting ready
First, let's make the database schema to support the recipe. If you have your own table ready and are just looking for the hashing code, you can probably skip this part. Otherwise, copy the following code into your database:
CREATE TABLE IF NOT EXISTS `register` (
ùser_idìnt(11) NOT NULL AUTO_INCREMENT,
ùser_first_namè varchar(125) NOT NULL,
ùser_last_namè varchar(125) NOT NULL,
ùser_email` varchar(255) NOT NULL,
ùser_hash` text NOT NULL,
PRIMARY KEY (ùser_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you
purchased this book elsewhere, you can visit h
31
User Management
The register table description is as follows:
Item name
Attributes
Description
user_id
INTEGER(11)
Table primary key
user_first_name
VARCHAR(125)
The user's first name
user_last_name
VARCHAR(125)
The user's last name
user_email
VARCHAR(255)
The user's e-mail address, for example,
name@example.org
user_hash
TEXT
The hash of their password generated by
$this->encrypt->sha1($string_
to_hash [, $key])
You'll also have to create a sessions table and ensure that the config file is set up to handle database stored sessions. For instructions on how to do that, see
That's the database done! We're going to use the CodeIgniter encrypt library to do the heavy lifting of hashing the password for us, specifically, $this->encrypt->sha1($string_to_
hash [, $key]), where $key is optional. There are a few things we need to set up first.
You'll need to decide on the encryption key you want to use: this can either be the encryption key that you've set in $config['encryption_key'] in config.php, or you can pass a new key as a second parameter to CodeIgniter. The presence of this second parameter overrides the value set in $config['encryption_key'].
In the following recipe, we are using the value in $config['encryption_key'] to serve as our encryption key; as such, we won't be passing a second parameter.
When creating a key, try not to use just a single word as this may be cracked using a rainbow table; instead use a fairly long string with random alphanumeric characters.
How to do it...
In this recipe, we're going to create the following seven files:
f
/path/to/codeigniter/application/controllers/register.php:
This file contains a form allowing the user to sign up, and a record is then added to the database table (SQL in the Getting ready section)
f
/path/to/codeigniter/application/models/register_model.php:
This file interacts with the database for the controller
f
/path/to/codeigniter/application/views/register/register.php:
This file is for the registration form
32
Chapter 2
f
/path/to/codeigniter/application/controllers/signin.php: This file handles the login process, including comparing the password against the hash f
/path/to/codeigniter/application/models/signin_model.php: This file interacts with the database for the controller
f
/path/to/codeigniter/application/views/signin/signin.php: This file is for the signin form
f
/path/to/codeigniter/application/views/signin/loggedin.php:
This file presents a page indicating a successful sign-in
1. Copy the following code into the, /path/to/codeigniter/application/
controllers/register.php file:
<?php if (!defined('BASEPATH')) exit('No direct script
access allowed');
class Register extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->helper('form');
$this->load->helper('url');
$this->load->helper('security');
$this->load->model('Register_model');
$this->load->library('encrypt');
$this->load->database();
}
public function index() {
redirect('register/register_user');
}
public function register_user() {
// Load support assets
$this->load->library('form_validation');
$this->form_validation->set_error_delimiters(
'', '<br />');
// Set validation rules
$this->form_validation->set_rules('first_name',
'First Name',
'required|min_length[1]|max_length[125]');
$this->form_validation->set_rules('last_name',
'Last Name',
'required|min_length[1]|max_length[125]');
$this->form_validation->set_rules('email', 'Email',
'required|min_length[1]|max_length[255]|
valid_email');
$this->form_validation->set_rules('password1',
33
User Management
'Password', 'required|min_length[5]|
max_length[15]');
$this->form_validation->set_rules('password2',
'Confirmation Password', 'required|min_length[5]|
max_length[15]|matches[password1]');
// Begin validation
if ($this->form_validation->run() == FALSE) {
// First load, or problem with form
$data['page_title'] = "Register";
$this->load->view('register/register',$data);
} else {
// Create hash from user password
$hash = $this->encrypt->sha1(
$this->input->post('password1'));
$data = array(
'user_first_name' =>
$this->input->post('first_name'),
'user_last_name' =>
$this->input->post('last_name'),
'user_email' =>
$this->input->post('email'),
'user_hash' => $hash
);
if ($this->Register_model-
>register_user($data)) {
redirect('signin');
} else {
redirect('register');
}
}
}
}
2. Copy the following code into the, /path/to/codeigniter/application/
models/register_model.php file:
<?php if (! defined('BASEPATH')) exit('No direct script
access allowed');
class Register_model extends CI_Model {
function __construct() {
parent::__construct();
}
public function register_user($data) {
if ($this->db->insert('register', $data)) {
return true;
34
Chapter 2
} else {
return false;
}
}
public function update_user($data, $email) {
$this->db->where('user_email', $email);
$this->db->update('register', $data);
}
}
3. Copy the following code into the, /path/to/codeigniter/application/
views/register/register.php file:
<?php echo form_open('register/register_user') ; ?>
<?php if (validation_errors()) : ?>
<h3>Whoops! There was an error:</h3>
<p><?php echo validation_errors(); ?></p>
<?php endif; ?>
<table border="0" >
<tr>
<td>First Name</td>
<td><?php echo form_input(array('name' =>
'first_name', 'id' => 'first_name',
'value' => set_value('first_name', ''),
'maxlength' => '100', 'size' => '50',
'style' => 'width:100%')); ?></td>
</tr>
<tr>
<td>Last Name</td>
<td><?php echo form_input(array('name' =>
'last_name', 'id' => 'last_name',
'value' => set_value('last_name', ''),
'maxlength' => '100', 'size' => '50',
'style' => 'width:100%')); ?></td>
</tr>
<tr>
<td>User Email</td>
<td><?php echo form_input(array('name' =>
'email', 'id' => 'email',
'value' => set_value('email', ''),
'maxlength' => '100', 'size' => '50',
'style' => 'width:100%')); ?></td>
</tr>
<tr>
<td>Password</td>
<td><?php echo form_password(array('name' =>
'password1', 'id' => 'password1',
'value' => set_value('password1', ''),
'maxlength' => '100', 'size' => '50',
35
User Management
'style' => 'width:100%')); ?></td>
</tr>
<tr>
<td>Confirm Password</td>
<td><?php echo form_password(array('name' =>
'password2', 'id' => 'password2',
'value' => set_value('password2', ''),
'maxlength' => '100', 'size' => '50',
'style' => 'width:100%')); ?></td>
</tr>
</table>
<?php echo form_submit('submit', 'Submit'); ?>
or <?php echo anchor('form', 'cancel'); ?>
<?php echo form_close(); ?>
4. Copy the following code in to the, /path/to/codeigniter/application/
controllers/signin.php file:
<?php if (!defined('BASEPATH')) exit('No direct script
access allowed');
class Signin extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->helper('form');
$this->load->helper('url');
$this->load->helper('security');
}
public function index() {
redirect('signin/login');
}
public function login() {
if ($this->session->userdata('logged_in') == TRUE) {
redirect('signin/loggedin');
} else {
$this->load->library('form_validation');
// Set validation rules for view filters
$this->form_validation->set_rules('email', 'Email',
'required|valid_email|min_length[5]|
max_length[125]');
$this->form_validation->set_rules('password',
'Password ', 'required|min_length[5]|
max_length[30]');
if ($this->form_validation->run() == FALSE) {
$this->load->view('signin/signin');
36
Chapter 2
} else {
$email = $this->input->post('email');
$password = $this->input->post('password');
$this->load->model('Signin_model');
$query =
$this->Signin_model->does_user_exist($email);
if ($query->num_rows() == 1) {
// One matching row found
foreach ($query->result() as $row) {
// Call Encrypt library
$this->load->library('encrypt');
// Generate hash from a their password
$hash = $this->encrypt->sha1($password);
// Compare the generated hash with that in the
// database
if ($hash != $row->user_hash) {
// Didn't match so send back to login
$data['login_fail'] = true;
$this->load->view('signin/signin', $data);
} else {
$data = array(
'user_id' => $row->user_id,
'user_email' => $row->user_email,
'logged_in' => TRUE
);
// Save data to session
$this->session->set_userdata($data);
redirect('signin/loggedin');
}
}
}
}
}
}
function loggedin() {
if ($this->session->userdata('logged_in') == TRUE) {
$this->load->view('signin/loggedin');
} else {
redirect('signin');
}
}
}
37
User Management
5. Copy the following code in to the, /path/to/codeigniter/application/
models/signin_model.php file:
<?php if ( ! defined('BASEPATH')) exit('No direct script
access allowed');
class Signin_model extends CI_Model {
function __construct() {
parent::__construct();
}
public function does_user_exist($email) {
$this->db->where('user_email', $email);
$query = $this->db->get('register');
return $query;
}
}
6. Then copy the following code in to the, /path/to/codeigniter/application/
views/signin/signin.php file:
<?php echo form_open('signin/login') ; ?>
<?php if (validation_errors()) : ?>
<h3>Whoops! There was an error:</h3>
<p><?php echo validation_errors(); ?></p>
<?php endif; ?>
<?php if (isset($login_fail)) : ?>
<h3>Login Error:</h3>
<p>Username or Password is incorrect,
please try again.</p>
<?php endif; ?>
<table border="0" >
<tr>
<td>User Email</td>
<td><?php echo form_input(array('name' =>
'email', 'id' => 'email',
'value' => set_value('email', ''),
'maxlength' => '100', 'size' => '50',
'style' => 'width:100%')); ?></td>
</tr>
<tr>
<td>Password</td>
<td><?php echo form_password(array('name' =>
'password', 'id' => 'password',
'value' => set_value('password', ''),
'maxlength' => '100', 'size' => '50',
38
Chapter 2
'style' => 'width:100%')); ?></td>
</tr>
</table>
<?php echo form_submit('submit', 'Submit'); ?>
or <?php echo anchor('signin', 'cancel'); ?>
<?php echo form_close(); ?>
7. Then copy the following code in to the, /path/to/codeigniter/application/
views/signin/loggedin.php file:
Success! Logged in as <?php echo
$this->session->userdata('user_email'); ?>
How it works...
Okay, there's a lot going on in this example but it's actually fairly simple. Take a look at the preceding code again—specifically the lines which are highlighted as these are the lines that are password specific. The files created in the preceding section show the creation of a user and the logging in of that user. Of course, your code will be different; but let's concentrate on those highlighted lines.
They show the code that performs the hashing and comparison of passwords (a concise version can be found in the following recipe).
Firstly, let's look at the register user process. The register controller accepts user-submitted information from the /path/to/codeigniter/application/views/register/
register.php view. Upon successfully passing the following validation line: $hash = $this->encrypt->sha1($password);
will generate a hashed value using the password the user supplied, this hashed value is stored in the, $hash variable (obvious huh?).
$hash is then added to the $data array for insertion into the database as follows:
// Create hash from user password
$hash = $this->encrypt->sha1($this->input->post('password1')); $data = array(
'user_first_name' => $this->input->post('first_name'),
'user_last_name' => $this->input->post('last_name'),
'user_email' => $this->input->post('email'),
'user_hash' => $hash
);
39
User Management
Now let's take a look at the sign-in process. public function login() accepts the e-mail address and password from the user (from the, /path/to/codeigniter/application/
views/signin/signin.php view), and upon successfully passing validation, we look up the user-supplied e-mail address in the register table as follows: $this->load->model('Signin_model');
$query = $this->Signin_model->does_user_exist($email);
if ($query->num_rows() == 1) {
// One matching row found
foreach ($query->result() as $row) {
..
}
If the e-mail exists, we generate a hash from the user-supplied password. This process is the same as the functionality found in the registration process as follows:
// Call Encrypt library
$this->load->library('encrypt');
// Generate hash from a their password
$hash = $this->encrypt->sha1($password);
// Compare the generated hash with that in the
// database
if ($hash != $row->user_hash) {
// Didn't match so send back to login
$data['login_fail'] = true;
$this->load->view('signin/signin', $data);
} else {
$data = array(
'user_id' => $row->user_id,
'user_email' => $row->user_email,
'logged_in' => TRUE
);
// Save data to session
$this->session->set_userdata($data);
redirect('signin/loggedin');
}
40
Chapter 2
Now, take a look at the highlighted line in the preceding code. We're comparing the hash we generated from the user-supplied password against user_hash in the record we pulled from the register table. If the two hashes do not match, then the user must not have supplied the correct password, so we send them back to the signin form and wait for another attempt.
However, if the two hashes do match, then the user must have supplied the correct password, so we'll start a session for them and redirect them to public function loggedin().In this case, it is a brief message, indicating that they are successfully logged in. However, in your application, this would be some sort of password protected member area, perhaps a dashboard.
Generating passwords with CodeIgniter –
the bare bones
Okay, this is just the bare bones process. If you want a full example, then the preceding recipe is for you. This recipe is for people who already have a create-user process, but wish to integrate some password protection into an existing process.
How to do it...
If you don't need the preceding recipe and only require the bare bones of hashing/comparing; please refer to the following steps:
Generating a hash
To generate a hash, perform the following steps:
1. Generate a hash with a key in $config['encryption_key'] as follows:
// Call Encrypt library
$this->load->library('encrypt');
$hash = $this->encrypt->sha1($text_to_be_hashed);
2. Generate a hash with a key other than that in $config['encryption_key']
as follows:
// Call Encrypt library
$this->load->library('encrypt');
$key = "This-is-the-key";
$hash = $this->encrypt->sha1($text_to_be_hashed, $key);
In a production environment, replace the $key value (This-is-the-key) with a realistic value. Make it a long string of alphanumeric characters; the more random the better!
41
User Management
Comparing hashed values
The hash values are compared as follows:
// Call Encrypt library
$this->load->library('encrypt');
// Generate hash from a their password
$hash = $this->encrypt->sha1($password);
// Compare the generated hash with that in the database
if ($hash != $row->user_hash) {
// Didn't match so send back to login
redirect('signin/login');
} else {
// Did match so log them in if you wish
}
How it works...
Generating a hash with the $config['encryp