In this tutorial we will add the Pilot package and use it to provide access control to our existing Handlers.
The Pilot controls access as follows:
Access is controlled through Access Levels, Groups, Users and Site Functions.
Each of the Handlers we have previously written corresponds to a Site Function. A Site Function has an Access Level assigned to it, which restricts access to that Site Function to Users who are members of a Group with a sufficient Access Level.
a) If you have sufficient access the Pilot will automatically create the required tables if they do not exist.
b) If the Pilot is not able to automatically create the tables, you can do it manually with:
$ mysql -u root --pass=PASS pastries < WEBROOT/yourdir/packages/pilot/pilot.sql
pilot.sql contains SQL statements to add tables to store Access Levels, Groups, Users and Site Functions, and creates Access Levels for Root (highest access) and Public (lowest access). It also creates a Group for system developers with Root access.
<?php class MyApplication extends PilotApplication { function MyApplication() { $this->Application(); } } ?>
In WEBROOT/yourdir/packages/my/login.rsml.php add:
<fragment>
<div id="pageContent">
<view-form name="Login" />
</div>
</fragment>
... and in WEBROOT/yourdir/packages/my/login.frm.php add:
<form handler="Home" name="Login"> <form-error name="user_email"> <li class="errorItem" id="user_emailErrorItem">MESSAGE</li> </form-error> <li id="user_emailFormItem"> <label id="user_emailFormLabel" for="user_email">Email:</label> <text-line name="user_email"> <validation type="REQUIRED"> <error-message>You need to enter your email in order to login</error-message> </validation> <validation type="EMAIL"> <error-message>The email address you entered appears not to be valid. Please try again</error-message> </validation> </text-line> </li> <form-error name="user_password"> <li class="errorItem" id="user_passwordErrorItem">MESSAGE</li> </form-error> <li id="user_passwordFormItem"> <label id="user_passwordFormLabel" for="user_password">Password: </label> <password name="user_password"> <validation type="REQUIRED"> <error-message>You need to enter your password in order to login</error-message> </validation> </password> </li> <li id="loginFormItem"> <submit name="login" value="Login" /> </li> <li id="redirectFormItem"> <hidden name="redirect"/> </li> </form>
Note the <hidden> tag at the bottom, we will use this to store the Handler the user attempted to access before they were redirected to the Home Handler (where applicable).
Edit home.class.php as follows:
<?php
class Home extends Handler
{
function Home($name = '',$description = '')
{
$this->Handler($name,$description);
}
public function performHandlerTasks()
{
if(Application::formPosted())
{
if(Form::load('my.Login')->validate())
{
try
{
PilotApplication::login(Application::param('user_email'),
Application::param('user_password'));
if ($r = Application::param('redirect'))
Application::redirectWithoutPost($r);
else
Application::redirectWithoutPost('AddPastry');
}
catch(UserNotFoundException $exc)
{
FormErrors::add('user_email','No user with that email is
registered');
}
catch(InvalidPasswordException $exc)
{
FormErrors::add('user_email','The password was
incorrect');
}}}
else if(Application::param('logout'))
PilotApplication::logout();
}
public function display()
{
$disp = Display::current();
$disp->setTitle('Home Page');
$form = Form::load('my.Login');
$form->setInputValue('redirect',Application::param('redirect'));
$disp->addForm($form);
$disp->addView('page_content','my.Login');
$disp->displaySiteTemplate();
}
}
?>
First assume that the form hasn't been posted and take a look at display(). Note the line $form->setInputValue()('redirect', Application::param('redirect')); which picks up any Handler we attempted to access before logging in and adds it to the form.
Next look at performHandlerTasks(). We use Application::formPosted() to check if a form has been posted, if so we load the Login form and attempt to validate it. Once validated, we try to login. Any problems with the login will throw exceptions which will prevent the Application::redirectWithoutPost() lines from executing, meaning the display() method will be called again and the form will be shown with the errors set with FormErrors::add(). These errors are added manually to the FormErrors system. When a <validation> node fails, it uses the same system to populate the <form-error> nodes in your FormML markup.
Add WEBROOT/yourdir/packages/my/my_setup.class.php with the following code:
<?php
class MySetup extends PilotSetup
{
function MySetup($name = '',$description = '')
{
$this->PilotSetup($name,$description);
}
public function display()
{
$disp = Display::current();
$disp->setTitle('My System Setup');
$this->setupDisplay($disp);
$disp->addView('page_content','my.MySetup');
$disp->displaySiteTemplate();
}
}
?>
In the above code we simply extend the PilotSetup class as it already does most of the work for us.
... we will also need WEBROOT/yourdir/packages/my/my_setup.rsml.php:
<fragment>
<h2>Setup for My Package</h2>
<view name="system_setup" />
</fragment>
The <view> tag above refers to a view defined in the PilotSetup class (for more details see PilotSetup::setupDisplay()), this view handles all of the forms required for access configuration.
You may wish to examine the PilotSetup handler, particularly the PilotSetup::setupDisplay() method - it would be possible to extend certain methods to provide a different interface to the access configuration.
In the order specified:
Note that none of our Handlers provide a logout functionality, so at this point to log out you will have to visit WEBROOT/yourdir/reinitialise.php
Now try to visit index.php?h=ListPastries - this should display without requesting a login.
Finally visit index.php?h=Home then login - you should be redirected to the AddPastry handler which is what we specified should be the default handler after logging in.
To improve this behaviour somewhat, we can override the Application::handleAccessDenied() method in MyApplication as follows:
public function handleAccessDenied() { if($h = Application::current()->handlerClass()) { if(class_exists($h)&&PilotApplication::userIsGuest()) { // logging in for the first time // set redirect param so that user gets the page they wanted // after login Application::setUrlParam('redirect',$h); Application::redirect('Home'); } else if(class_exists($h)) { //logged in but no access, display some sort of unauthorised //message echo "insert unauth msg here"; } else { //junk in the URL or an old one ... custom 404 echo "insert 404 here"; } } }
As per the comments, when a non-public Handler is requested it will redirect to the Home Handler so the user can login. Before redirecting, it calls Application::setUrlParam() to add the 'redirect' value to the URL, we will pick this up in the Home Handler and add it to the login form so that the user can be redirected to the desired page after logging in.
Also note the other branches which provide places to add code to handle other cases.
Now if you try to access WEBROOT/yourdir/?h=NotARealHandler you will see the "insert 404 here" message (which ideally would be replaced with a redirect to a 404 Handler).
If you attempt to access WEBROOT/yourdir/?h=MySetup you should be redirected to the Home Handler, and after logging in to the MySetup Handler.
1.5.7