LiveUser

PEAR::LiveUser authentication and permission framework

AdvancedSearch | AreaMap ]

Search:

  Welcome to LiveUser   Documentation   RoadMap   Wishlist   About  
  Documentation   FAQ   References   Tutorials   Other  

Username:

Password:


Areas In
This Wiki

BEPHPUG

Conferences

emPHPower

LiveUser

Main

MDB2

PDO

PEARThinkTank

PHPSVN

PHPTODO

RDBMS

WebBuilder2

This document is supposed to be the Wiki version of Arnaud's LiveUser tutorial and to be considered work in progress. It will be updated to reflect the latest LiveUser release while converting it to the wiki..



As long as this disclaimer exists, many things explained in this document can be considered outdated!!!


About this document

Authors

At the time of this version, Arnaud Limbourg wrote this document. With help from Markus Wolff, Jean-Marc Fontaine, Bjoern Kraus and others on LiveUser mailing-list.

Abbreviations used throughout the document

  • LU: LiveUser
  • DB: Database
  • GUI: Graphical User Interface

Intended audience

Who should read this document ?

If you are planning on using LiveUser (LU) in your application and wonder how to get on with it then this document is for you. If you are a developer wishing to help with the development of LU or are wondering how to extend LU features then this document is not for you, this will be part of some other documentation.


What is LiveUser

LiveUser purpose is to provide a flexible and extensible framework to be used for authentication and authorization schemes. It does not aim at being the “One-true-solution” but rather providing the means to achieve what you need.

Rationale: What is authentication and what are permissions

Authentication is a simple concept. It consists of checking if “you-are-who-you-say-you-are”. Of course, there are different ways to implement that idea. The one which interests us is based on the classic login/password mechanism.

Permissions are simple as well. You want to make sure somebody is allowed to perform a given action. Of course, there are many ways to implement a permission handling mechanism.


Where to find LiveUser

LiveUser resides in PEAR. To install LiveUser you need a running PEAR environment. More information on PEAR can be found at http://pear.php.net/

Once it is up and running you can launch the following command:

pear install LiveUser-beta

You probably want some additional modules, including:

pear install LiveUser_Admin-beta
pear install MDB2 MDB2_Schema MDB2_Driver_mysql

This command will download the latest version of the LiveUser package from the PEAR server and install it in the PEAR directory on your system.


LiveUser Design

LiveUser design is based on containers. A container is similar in concept to a driver. Take a printer, you know what a printer is but every model behaves differently from the others. Thus you need a specific driver which works for your printer. Containers are the same, to handle a specific authentication scheme you need a container.

The design is based on two sets of containers. One set is used for authentication and the other for permissions.

Bundled with the package are several ready-made containers which answer common problems encountered in real-life applications. Remember that if there isn't one that suits your needs, just write a new container ! Liveuser is not a single-sign-on system (yet !) but you can build one with it.

Directory layout and database schema are provided in the appendices 1 and 2.

Rationale: Why containers ?

This approach gives you a great flexibility. If bundled containers do not fit your needs, you can just add your own. Extending LiveUser is not part of this document.

Rights Management

All rights are saved into some kind of data storage container (the type of that container - i.e. flat files, RDBMS, LDAP or others - depends on what type of auth/perm container you have chosen). Each right has an integer ID and a name for easy idenfitication. A script is bundled with LiveUser which will read the database and generate a PHP file. This file will contain one constant for every available right. You can then include this file in your application and use the constants when you check for a right. This approach saves you the hassle of remembering that right id 1 is to edit news, you just use a constant named EDITNEWS. It also helps you when you´re moving an application using LiveUser from one server to another: If some other application on the new server already uses the right id´s that you used on the old server, simply run the generator script again and everything´s fine.

Note: the defineGenerator script only generates a php file for a DB based container. XML based or other containers are not treated by it.

Depending on what container you plan to use you will choose a different option for constants naming. The following chapter will give you hints on what to choose.

The LiveUser class

The LiveUser class (formerly known as LoginManager? in early LiveUser versions) is the class developer will use to for authentication/permission checking. Examples are to login/logout users and check for a specific right. This, along with the administration classes, is the only class in the package that you will use directly.

However, this is not how it must be done every time. If you have some running application that is already using another authentication package, for example PEAR::AUTH, you always have the option of using the permission container classes directly to add permission handling – they all can be used standalone, if you wish. But using LiveUser class is a lot more comfortable.

Note: there is an experimental PEAR::Auth container in CVS.


LiveUser out-of-the-box

Bundled containers

LiveUser comes with bundled containers. Two sets are provided:

  • Authentication:
    • DB.php
    • MDB.php
    • XML.php
  • Permission:
    • DB_Simple.php
    • DB_Medium.php
    • DB_Complex.php
    • MDB_Simple.php
    • MDB_Medium.php
    • MDB_Complex.php
    • XML_Simple.php

The names are pretty straightforward, DB means the container uses PEAR::DB, MDB obviously uses PEAR::MDB (which makes both containers database-centric) while XML... well, is XML-file based.

What container to choose is dependent on what you want to achieve. For a simple application or website where you don't have many users and don't want to bother the XML based containers might just do the trick.

If you prefer a DB based mechanism and don't have that many users DB.php for authentication and DB_Simple for permissions should be fine.

For busier applications/websites you may be looking at using DB_Medium or DB_Complex.

You may be wondering what are the differences between Simple, Medium and Complex, and rightly so! The differences lie in how the permissions are handled.

Note: Many people already have an existing user database. LiveUser can be customized to specify which table/fields to use. It means that you do not have to transfer all the users to a specific schema but you can use your own.

Following is a list that shows what is implemented by which containers:

  • DB_Simple and XML_Simple:
    • Every Right may be given to any Person.
  • DB_Medium:
    • All features of DB_Simple, plus:
    • User groups can be defined.
    • Every group can have an unlimited number of rights assigned to it.
    • Users can be assigned to an unlimited number of groups, thus inheriting its rights.
  • DB_Complex:
    • All features of DB_Medium, plus:
    • Every Right may imply another Right. For example « WRITE » may imply « READ ». Thus giving the « WRITE » right to a user will also mean he automatically has the « READ » right.
    • Every Right may have a "Scope" that limits the scope of the right to Users or Admins
    • Every Right may have Levels* (for example Level do not make sense for a right like "new", so this is optional):
      • Level 1: Person may only apply this right to « Objects » he owns
      • Level 2: Person may only apply this right to « Objects » that he owns, that one of his groups own or that someone owns that is in a group in which the person is also a member
      • Level 3: Person may apply this right to all « Objects »
    • Every Person may be either of Type « User » or Type « Admin ».
    • Every User may have any of the following special levels:
      • Areauser (has all rights in a given Area that a user may have)
      • Superuser (has all rights that a user may have)
    • Every Admin may have any of the following special levels:
      • Areaadmin (has all rights in a given Area that a admin may have)
      • Superadmin (has all rights that a admin may have, except administrating Masteradmins)
      • Masteradmin (has all rights that a admin may have)
    • Every Group may have subgroups that may span an infinately deep tree.

To take advantage of the Levelsystem you have to store ownership of all «Objects» to which you want to control access via LiveUser.

The beauty of all this is, that even though the mechanisms for retrieving the rights differ (which leads to performance differences between the containers – DB_Simple being the fastest, DB_Complex the slowest) the API to your application remains the same regardless of which container you actually use.

Because of that, you can easily switch container types without having to touch even one line of code in your application – just edit some lines in the configuration file and you´re done. As for the three different DB containers provided with LiveUser, even the database models are compatible – therefore, DB_Simple can use the full model of DB_Complex (some tables simply won´t be used) and if one day it should become necessary to implement a more complex approach for administering permissions, just switch the container type to DB_Medium or DB_Complex and you´re done.

Using rights

Previous chapters hinted how the choice of a container will affect the generation of a php file with constants for every right. The following list should give you a pretty comprehensive view about the matter:

  • DB_Simple: you will want constants named equally to the defined rights.
  • DB_Medium, DB_Complex: you will probably want contants prefixed with the application and/or area to which they belong

Of course, it is up to you to choose the way that suits you most. If this all looks too abstract I recommend reading "A sample applicatoin" and the appendices (all see below :-))


A sample application

We will build a news management application to show how to use LiveUser. A full online-demo will be available at some point (no date can be given, there was a demo version almost ready but changes in LiveUser which happened in-between forces to update it).

The specifications of the application are simple:

John Doe wants a web application which allows him to publish news on a web page. He wants to give access to anyone he wants on particular news sections. There are three sections, namely: "General", "World" and "Gaming". As it is the first application he is making, he does not want to bother too much. He understands what a right is and wants a simple way of giving rights to a user. Having to think about groups/subgroups gives him a headache so he wants to keep it pretty basic.

He designed the application in three parts, one will show the news and is accessible to anyone, the other is restricted to given users. The third part is restricted to John Doe himself and is where he manages user accounts.

Having read the documentation, John Doe knows that choosing an authentication container is not of paramount imoprtance to get his hands dirty. He also knows that he has to choose a permission container before starting.

John Doe does not feel very well today so he chooses the DB_Simple container which should cause him very few headaches.

He decides to start hacking the main page which will welcome visitors and show them the news. There is no authentication needed on this page, everybody has access to it. He just wants to have a login form displayed on the screen for contributors.

In order to do his work he read some literature and feels that separating the display and the control is a good thing. So he installs PEAR::DB as a database access layer and HTML_Template_IT to display information.

He comes up with the following template:

/(insert screenshot here)/

He obtained it using the following two files

  • index.php: see code in appendices
  • index.tpl: see code in appendices

That took him about two hours to get this up and running.

He feels quite excited though at the same time knowing that this was the easy part. He now needs to tackle the administration part.

He already decided to have one script called admin.php which will handle it all. He starts by entering one user and three rights. Being a bit lazy John Doe will use the provided admin classes, the following script should help. In this example Auth_DB and Perm_DB_Medium are used but every container charing the same API you can interchange them.


<?php
require_once 'config.inc.php';
require_once 'LiveUser/Admin/Perm/Container/DB_Medium.php';
require_once 'LiveUser/Admin/Auth/Container/DB.php';

$lu_dsn = array('dsn' => $dsn);

$objRightsAdminAuth = new
    LiveUser_Admin_Auth_Container_DB(
        $lu_dsn, $conf['authContainers'][0]
    );

$objRightsAdminPerm = new
    LiveUser_Admin_Perm_Container_DB_Medium($lu_dsn, $conf);

if (!$objRightsAdminPerm->init_ok) {
    die('impossible to initialize' . $objRightsAdminPerm->getMessage());
}

$objRightsAdminPerm->setCurrentLanguage('FR');

// Add a user to the database
// LiveUser design allowing for several containers
// the user must be added to both containers
$user_auth_id = $objRightsAdminAuth->addUser('johndoe', 'dummypass', true);

if (DB::isError($user_auth_id)) {
    $user_auth_id->getMessage();
    //exit;
}

$objRightsAdminPerm->addUser($user_auth_id);

echo '$user_id created ' . $user_auth_id . "n";

// create application and areas
$app_id = $objRightsAdminPerm->addApplication('LIVEUSER', 'website');
$area_id = $objRightsAdminPerm->addArea($app_id, 'ONLY_AREA', 'the one and only area');

// Then he adds three rights
$right_1 = $objRightsAdminPerm->addright($area_id, 'MODIFYNEWS',   'read something');
$right_2 = $objRightsAdminPerm->addright($area_id, 'EDITNEWS',  'write something');

echo 'Created two rights with id ' . $right_1 . ' and ' . $right_2 . "n";

// Grant the user rights
$objRightsAdminPerm->grantUserRight($user_auth_id, $right_1);
$objRightsAdminPerm->grantUserRight($user_auth_id, $right_2);
?>

Appendices

Appendix 1: Directory Layout

/TODO/

Appendix 2: Database

/TODO/

Appendix 3: How to implement this in my application?

Many users may ask how to put this in their application without writing this piece of code on every page. This would make any change a nightmare. It is not the purpose of this document to talk about application design issues. The following non-exhaustive list should give you general hints:

  • if you have access to your php.ini (or can write php configuration statements in you apache http.conf or .htaccess) you can use the<br>auto_prepend=”configuration_file.php”<br>and put the above lines in it.

The major drawback is that it puts authentication automatically on every page.

  • You can write a function which will return a reference to the instantiated LiveUser object, thus enabling you to perform further actions, such as permissions handling. Example follows: /(...add example here...)/

This solution gives you a good flexibility. While providing a standard behaviour it leaves you a satisfactory level control. You can push it further, adding a parameter to give a specific configuration file for example.

If you need complete control instantiating the class is probably what you are looking for.

Appendix 4: index.php code


<?php
require_once 'DB.php';
require_once 'config.inc.php';
require_once 'HTML/Template/IT.php';

// Setup db and template objects
$db  = DB::connect($dsn);
$tpl =& new HTML_Template_IT('./');

$tpl =& new HTML_Template_IT('./');
$tpl->loadTemplatefile('index.tpl', true, true);

// assign the content to the vars
$tpl->setVariable('GENERALNEWS', getNews($db, 'general'));
$tpl->setVariable('GAMINGNEWS',  getNews($db, 'gaming'));
$tpl->setVariable('WORLDNEWS',   getNews($db, 'world'));
$tpl->setVariable('FLASH',       getNews($db, 'flash'));

$tpl->show();

// This function is to fetch news from the DB
function getNews(&$db, $newsCategory)
{
    $query = "
        SELECT
            news_id      AS id,
            news_date    AS date,
            news_title   AS title,
            news_content AS content
        FROM
            news
        WHERE
            news_category = '$newsCategory'";

    $news = $db->getAssoc($query, DB_FETCHMODE_ASSOC);

    if (DB::isError($news)) {
        die($news->getMessage() . ' ' . $news->getUserinfo());
    } else {
        $tpl =& new HTML_Template_IT('./');

        $tpl->loadTemplatefile('news.tpl', true, true);

        foreach($news as $name) {
            foreach($name as $cell) {
                // Assign data to the inner block
                $tpl->setCurrentBlock('cell');
                $tpl->setVariable("DATA", nl2br($cell));
                $tpl->parseCurrentBlock('cell');
            }
            // Assign data and the inner block to the
            // outer block
            $tpl->setCurrentBlock('row');
            $tpl->parseCurrentBlock('row');
        }
        return $tpl->get();
    }
}
?>

Appendix 5: index.tpl code

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

<html>
<head>
    <title>LiveUser Tutorial Site</title>
     <style type="text/css" media="screen">@import "layout_frontend.css";</style>
</head>

<body>

<div class="content">
    <h1>LiveUser Demo and tutorial</h1>
    <p>This demo was made to show results given in the LiveUser tutorial.<br />
    Several accounts are set up so that you can see the funtionalities of
    DB_Simple and DB_Medium.
    <ul>
        <li>Admin/Admin will give you full access using the DB_Simple container</li>
        <li>simple_user/simple_user gives you the right to login but you cannot edit news content</li>
    </ul>
    </p>
    <p>Please note that any content submitted by users do not
    necessarily reflect the state of mind of any member of the
    team working on LiveUser.</p>
</div>

<div class="content">
<h2>General news:</h2>
{GENERALNEWS}
</div>

<div class="content">
<h2>Video Games News:</h2>
{GAMINGNEWS}
</div>

<div id="navAlpha">
<h2>Flash News :</h2>
{FLASH}
</div>

<div id="navBeta">
<h2>Log-in to the admin part</h2>
<!-- login form to the admin part -->
<form method="post" action="admin.php">
<table style="border:1px dashed black;">
    <tr>
        <td>login:</td>
        <td><input name="handle" type="text" size="5" manlength="15" /></td>
    </tr>
    <tr>
        <td>password:</td>
        <td><input name="passwd" type="password" size="5" manlength="10" /></td>
    </tr>
    <tr>
        <td>Remember me <input type="checkbox" name="rememberMe" /></td>
    </tr>
    <tr>
        <td colspan="2"><input type="submit" value="Log-in"></td>
    </tr>
</table>
</form>
<p>
<h2>World News :</h2>
{WORLDNEWS}
</p>
</div>
</body>
</html>
LiveUser:Complete (217.40.184.113)
Wed, 01 Mar 2006, 13:53
[ Links | Source | History | RSS ]

This site powered by YaWiki 0.22 beta.

Tutorials
Quickstart
Complete
Observers
AdminFilters
Custom properties