At their core, users can be represented by a few simple pieces of information: • A unique identifier such as a user ID • A unique identifier that the user themselves can easily remember,
Trang 1Privacy policies
When users sign up to any website, they generally agree to the terms and conditions
of the website, and the privacy policy While the terms and conditions generally set
out information about liability, conduct on the site, and so on, the privacy policy
explains what will be done with the users' data
It is important to be clear and honest with users about their data, and reassuring
about the security of their data Facebook has had a lot of bad press recently relating
to its privacy policy and the tools available to their users to protect their data In
particular, one of their recent changes resulted in a document that was over 5,800
words long—something that most users won't read or understand (http://www
huffingtonpost.com/2010/05/12/facebook-privacy-policy-s_n_574389
html) When stating your privacy policies:
• Be clear and concise
• Make it clear who can access the data they add to the site:
° Are all profiles public?
° How much information is available to what type of user?
° How can the information be restricted?
• Explain who owns the data—does the user retain ownership or do they grant
a licence of use to us?
It is also important for us to think about how we might allow users to change their
own privacy settings, including which profile information they would like to make
public, public only to their network, or completely private—particularly with
regards to contact details and dates of birth
Some countries also have legislation in place governing the management of user
data, such as the Data Protection Act in the UK This covers issues such as:
• Security—ensuring data is held securely, and isn't easy for others to access,
unless the user's permission has been given
• Relevancy—ensuring data held is kept up to date and is relevant
• Removal—allowing users to request full removal of their data
• Access—allowing users to request copies of all data held about them
Trang 2At their core, users can be represented by a few simple pieces of information:
• A unique identifier such as a user ID
• A unique identifier that the user themselves can easily remember, such as
their chosen username or their e-mail address
• A password, which is used to authenticate the user—to prove they are who
they say they are
As far as our authentication system is concerned, this will be a user We will of
course extend this with a user profile, but in terms of authentication, this is all the
information we need
Our user object
Our user object is created when a user tries to log in, either based on submitting
a login form supplying their username and password, or based on session data
for the user ID
If username and password are supplied, then it checks the credentials and populates
its variables if such a user exists If only an ID is supplied, then it populates based on
whether there is a user of that ID Since the authentication class controls whether the
current user is logged in or not, we can use this object to view or perform actions on
other users if we wished, as by separating the two we won't be automatically logged
in as the user populated within this object As a result, we can extend this object to
reset the user's password, edit the user, deactivate the user, and so on
The constructor takes four arguments, the registry (dependency injection, so it
can communicate with the rest of the framework), a user ID, a username, and a
password, the latter three being optional, and used as described above
public function construct( Registry $registry, $id=0,
$username='', $password='' )
{
$this->registry = $registry;
If we haven't set a user ID (that is, $id is 0) and we have set a username and a
password, we should look up the user to see whether these are valid credentials:
if( $id=0 && $username != '' && $password != '' )
{
$user = $this->registry->getObject('db')-
>sanitizeData( $username );
Trang 3As our passwords are hashed in the database, we need to hash the password we
were supplied We can hash the password directly in the query (by using the MySQL
function MD5), however, this exposes the password in plain text more than required,
as it would be processed and accessed by both PHP and the MySQL server
(which may be stored on a remote machine):
$hash = md5( $password );
$sql = "SELECT * FROM users WHERE username='{$user}' AND
password_hash='{$hash}' AND deleted=0";
$this->registry->getObject('db')->executeQuery( $sql );
if( $this->registry->getObject('db')->numRows() == 1 )
{
We have a record in the database, so the user is valid, so we set the various
properties of our user object:
$data = $this->registry->getObject('db')->getRows();
$this->id = $data['ID'];
$this->username = $data['username'];
$this->active = $data['active'];
$this->banned = $data['banned'];
$this->admin = $data['admin'];
$this->email = $data['email'];
$this->pwd_reset_key = $data['pwd_reset_key'];
$this->valid = true;
}
}
elseif( $id > 0 )
{
If we supplied a user ID, then we look up the user with that ID and populate the
object with their details As discussed above, we don't want to set them as logged-in
here, because we may use this object to edit, delete, and create users, and integrating
authentication would log out the administrator and log them in as someone else if
they tried to edit an existing user
$id = intval( $id );
$sql = "SELECT * FROM users WHERE ID='{$id}' AND deleted=0";
$this->registry->getObject('db')->executeQuery( $sql );
if( $this->registry->getObject('db')->numRows() == 1 )
{
$data = $this->registry->getObject('db')->getRows();
$this->id = $data['ID'];
$this->username = $data['username'];
$this->active = $data['active'];
Trang 4$this->admin = $data['admin'];
$this->email = $data['email'];
$this->pwd_reset_key = $data['pwd_reset_key'];
$this->valid = true;
}
}
}
Our authentication registry object
One of the first things our framework needs to do, once it is connected to the
database, and some core settings are loaded, is to check whether the current user
is logged in This is simply done by checking for an active session, and if one exists,
building the user object from that, or checking to see if a username and password
have been supplied, and building the user from that
This will make up part of our authentication object (registry/authentication
class.php), which will reside in our registry and interact with the user object
The checkForAuthentication method checks both for an active session and user
credentials being passed in POST data, and calls additional methods to build the
user object if appropriate
public function checkForAuthentication()
{
Initially, we remove any error template tags on the page (which we would use to
inform the user of an invalid login):
$this->registry->getObject('template')->getPage()-
>addTag('error', '');
if( isset( $_SESSION['sn_auth_session_uid'] ) && intval( $_
SESSION['sn_auth_session_uid'] ) > 0 )
{
If session data is set, we call the sessionAuthenticate method:
$this->sessionAuthenticate( intval( $_SESSION['sn_auth_
session_uid'] ) );
Trang 5
The sessionAuthenticate method then sets the loggedIn property to indicate
whether the user is logged in or not:
if( $this->loggedIn == true )
{
$this->registry->getObject('template')->getPage()-
>addTag('error', '');
}
else
{
If the user is not logged in, and we have a valid session, then something went wrong
somewhere, so we should inform the user their login attempt was not successful:
$this->registry->getObject('template')->getPage()-
>addTag('error', '<p><strong>Error: Your username or
password was not correct,
please try again</p><strong>');
}
}
If session data was not set, we check for post data, and call the postAuthenticate
method if appropriate, following the same steps as above
elseif( isset( $_POST['sn_auth_user'] ) &&
$_POST['sn_auth_user'] != '' && isset(
$_POST['sn_auth_pass'] ) && $_POST['sn_auth_pass'] != '')
{
$this->postAuthenticate( $_POST['sn_auth_user'] , $_
POST['sn_auth_pass'] );
if( $this->loggedIn == true )
{
$this->registry->getObject('template')->getPage()-
>addTag('error', '');
}
else
{
$this->registry->getObject('template')->getPage()-
>addTag('error', '<p><strong>Error: Your username or
password was not correct,
please try again</p><strong>');
}
}
elseif( isset( $_POST['login']) )
Trang 6If the login post variable has been set, but neither session data or POST login data
has been submitted, then the user didn't enter a username or a password, so we
should tell them this:
$this->registry->getObject('template')->getPage()-
>addTag('error', '<p><strong>Error:
Your must enter a username and a password</p><strong>');
}
}
This method also sets suitable template tag variables for standard errors if there was
a problem authenticating the user
POST authentication
In the code above, if the user has tried to log in by submitting a login form, the
postAuthenticate method is called This method is shown below It utilizes the
user object to query the database, if the user exists and is logged in, then it sets the
appropriate session data, as highlighted below:
private function postAuthenticate( $u, $p )
{
$this->justProcessed = true;
require_once(FRAMEWORK_PATH.'registry/user.class.php');
$this->user = new User( $this->registry, 0, $u, $p );
if( $this->user->isValid() )
{
if( $this->user->isActive() == false )
{
$this->loggedIn = false;
$this->loginFailureReason = 'inactive';
}
elseif( $this->user->isBanned() == true )
{
$this->loggedIn = false;
$this->loginFailureReason = 'banned';
}
else
{
$this->loggedIn = true;
$_SESSION['sn_auth_session_uid'] =
>getUserID();
}
Trang 7
}
else
{
$this->loggedIn = false;
$this->loginFailureReason = 'invalidcredentials';
}
}
SESSION authentication
If the user hasn't tried to log in by submitting a form, but has some session data set,
we try and authenticate them based on the session data:
private function sessionAuthenticate( $uid )
{
require_once(FRAMEWORK_PATH.'registry/user.class.php');
$this->user = new User( $this->registry, intval( $_SESSION['sn_
auth_session_uid'] ), '', '' );
if( $this->user->isValid() )
{
if( $this->user->isActive() == false )
{
$this->loggedIn = false;
$this->loginFailureReason = 'inactive';
}
elseif( $this->user->isBanned() == true )
{
$this->loggedIn = false;
$this->loginFailureReason = 'banned';
}
else
{
$this->loggedIn = true;
}
}
else
{
$this->loggedIn = false;
$this->loginFailureReason = 'nouser';
}
if( $this->loggedIn == false )
{
$this->logout();
}
Trang 8
Salt your passwords!
Our passwords are stored in the database as an MD5 one-way hash
This means we don't keep a copy of the user's password; instead, we
hash the password when they try to log in, and compare the hash to
the password in the database If our database was compromised, our
users' passwords should be safe This hashing cannot be reversed, but
there are dictionaries available for common words or phrases, which
means it is possible to work out some passwords from the hashes We
can prevent this further by salting the password; this involves adding
a "salt" to the password and then hashing it This is typically done by
creating a random string for each user and storing it in their row in the
users table Passwords in the Dino Space code are currently not salted,
to make it easier should you wish to change how the passwords are
hashed, or integrate with other login systems
Structuring the database
For our users table (without social profile data), we need the following fields:
Field Type Description
Key, Auto-increment The unique user ID
Password_salt Varchar(5) If we decide to salt our passwords
administrator or not
banned
when the user forgets it Reset_expires Timestamp Time at which that reset string expires—
preventing someone spamming a user by constantly requesting a new key
Trang 9We currently have two primary database tables for our users A users table,
containing the core user data, and a users_profile table, containing other
(non-essential) information
Standard details
Our core registration fields are defined in our registration controller; they
are stored as array pairs, referencing the field name with a more descriptive
name (the more descriptive name is used for error messages)
/**
* Standard registration fields
*/
private $fields = array( 'user' => 'username', 'password' =>
'password', 'password_confirm' => 'password confirmation',
'email' => 'email address');
/**
* Any errors in the registration
*/
private $registrationErrors = array();
/**
* Array of error label classes - allows us to make a field a
different color, to indicate there were errors
*/
private $registrationErrorLabels = array();
/**
* The values the user has submitted when registering
*/
private $submittedValues = array();
/**
* The santized versions of the values the user has submitted -
these are database ready
*/
private $sanitizedValues = array();
/**
* Should our users automatically be "active" or should they
require email verification?
Trang 10private $activeValue = 1;
private function checkRegistration()
{
We set an allClear variable, to indicate that the values submitted are all acceptable
Each time an error is encountered, this is set to false, so that we can report the error
back to the user:
$allClear = true;
The first stage is to check whether the user has actually submitted all of the required
fields, if any of them are blank, then we flag these errors to the user
// blank fields
foreach( $this->fields as $field => $name )
{
if( ! isset( $_POST[ 'register_' $field ] ) ||
$_POST[ 'register_' $field ] == '' )
{
If any are blank, our allClear variable is set to false, and we generate error strings,
and store them in our errors array:
$allClear = false;
$this->registrationErrors[] = 'You must enter a ' $name;
$this->registrationErrorLabels['register_' $field '_
label'] = 'error';
}
}
Next, we can check the values in more detail Let's start with the password!
We will want the password to be at least seven characters, to help ensure it is secure
To prevent issues of a user not knowing their password because they entered it
incorrectly, we ask the user to verify their password, so we must also check the
password and its verification match:
// passwords match
if( $_POST[ 'register_password' ]!= $_POST[ 'register_password_
confirm' ] )
{
$allClear = false;
$this->registrationErrors[] = 'You must confirm your
password';
$this->registrationErrorLabels['register_password_label'] =
'error';
$this->registrationErrorLabels['register_password_confirm_
label'] = 'error';