Developers' Guide     ODP::Passport

ODP::Passport Developers' Guide


Introduction:

ODP::Passport provides a centralised editor authentication system for non-official ODP tools.

It allows tool developers to restrict tools to active editors, or further by privilege level, without needing to implement their own authentication system, provide users with another password, and check whether or not editors are active.

Whilst this document covers use of the system assuming a Perl environment, one can implement the client side in any language they choose.

If this document looks long and complicated, don't worry, to implement the basics is straightforward. If you have any questions about the system, or any difficulties setting it up, please don't hesitate to get in touch with editor rpfuller.


Basics:

To implement an ODP::Passport client first you will need to install the relevant modules on your system. You can find the code at http://research.dmoz.org/~rpfuller/src/

In order for the system to work, your client and the ODP::Passport server must share a 'secret'. You should choose a complex secret (up to 255 characters)

You will then need a login.cgi and a logout.cgi.

login.cgi

Sample code:

#!/usr/bin/perl

use CGI qw(:standard);
use ODP::Passport;

my $client = new ODP::Passport ();
$client -> login('your secret',param('user'),param('key'),param('issued'),param('privs'));

my %tools=(
   'Test'=>'http://rpfuller.org/odp/spell/test.cgi',
   'Another'=>'http://rpfuller.org/odp/spell/another.cgi'
);

if (substr(param('tool'),0,9) eq 'ppstatic:')
{
   my $url = substr(param('tool'),9);

   print <<END_HTML;
<head>
<meta http-equiv="refresh" content="0;URL=$url">
</head>
<body></body>
END_HTML
}
elsif ($tools{param('tool')})
{

   my $params;
   foreach my $param (param())
   {
      if ($param =~ /^pp_p_(.*)$/)
      {
         if ($params){$params.='&'}
         $params.="$1=".param($param);
      }
   }

   if ($params){$params="?$params"}
   
   print <<END_HTML;
<head>
<meta http-equiv="refresh" content="0;URL=$tools{param('tool')}$params">
</head>
<body></body>
END_HTML
}
else
{
   print 'Available tools:';
   print '<li><a href="http://rpfuller.org/odp/spell/test.cgi">test.cgi</a></li>';
   print '<li><a href="http://rpfuller.org/odp/spell/another.cgi">another.cgi</a></li>';
   print '<hr><a href="logout.cgi">Logout</a>';
}

logout.cgi

#!/usr/bin/perl

use CGI qw(:standard);
use ODP::Passport;

my $client = new ODP::Passport;
$client -> logout();

Collection Names, Tool Names

Your entire collection of tools needs a name. You can use the hostname of your server, or some other suitable descriptor.

Each tool requires a name unique within the collection. You are free to control this namespace as you wish, although for obvious reasons tool names should not be excessively long or offensive. You should also note that ppstatic: is a reserved prefix.

Protecting a Script

Once you've got this set up, any scripts you wish to protect should start with at least the following:
#!/usr/bin/perl

use ODP::Passport;

my $client = new ODP::Passport;
$client -> checklogin('your secret','your tool collection name','tool name');

Protecting Parts of a Script

Sometimes you don't want an entire tool to be restricted to ODP editors, just parts. ODP::Passport allows you to do this.

Instead of using $client -> checklogin use $client -> checklogin_nohalt instead.

Passport will no longer halt or redirect the user, but return. If the user is logged in with ODP::Passport, appropriate environment variables will be set. You can then test if the user is logged in by using, for example, the PP_ISSUED environment variable.

Example code:

#!/usr/bin/perl

use ODP::Passport;

my $client = new ODP::Passport;
$client -> checklogin_nohalt('your secret','your tool collection name','tool name');

print "Content-type: text/html\n\n";

if ($ENV{'PP_ISSUED'})
{
   print "Hello $ENV{'REMOTE_USER'}.";
}
else
{
   print "Hello guest.";
}

Sending Information to rpfuller

For the entire collection, you need to send:

Once you have a secret in the system you can create/modify tools using the web interface. For each tool you'll need to enter:


Customising login.cgi:

The Standard login.cgi

If you use the standard login.cgi outlined above you'll need to add an entry for each tool you want direct forwarding for. To set this up you need to change the my %tools= block. Example:

my %tools=(
   'Test'=>'http://rpfuller.org/odp/spell/test.cgi',
   'Another'=>'http://rpfuller.org/odp/spell/another.cgi'
);

In this example Test and Another are the names of tools.

The 'Lazy' login.cgi

If you have a lot of CGIs and want to direct-forward them all, you should put your CGIs in one folder, and use their filename for the tool name. Then you can use this variation of login.cgi:

#!/usr/bin/perl

use CGI qw(:standard);
use ODP::Passport;

my $client = new ODP::Passport ();
$client -> login('your secret',param('user'),param('key'),param('issued'),param('privs'));

if (substr(param('tool'),0,9) eq 'ppstatic:')
{
   my $url = substr(param('tool'),9);

   print <<END_HTML;
<head>
<meta http-equiv="refresh" content="0;URL=$url">
</head>
<body></body>
END_HTML
exit;
}

my $params;
foreach my $param (param())
{
   if ($param =~ /^pp_p_(.*)$/)
   {
      if ($params){$params.='&'}
      $params.="$1=".param($param);
   }
}

if ($params){$params="?$params"}

my $tool = param('tool');
   
print <<END_HTML;
<head>
<meta http-equiv="refresh" content="0;URL=http://pathwhereyour/cgis/are/$tool$params">
</head>
<body></body>
END_HTML

You just need to set http://pathwhereyour/cgis/are/ to the correct location, rather than adding an entry for every tool.


Restricting Scripts by Privileges:

To protect an entire script you can supply a code along with your checklogin call.

Example:

$client -> checklogin('your secret','your tool collection name','tool name',1,'j');

In this example the j indicates that the script requires the user to have jmeta privileges. (They are a root editor, meta, kmeta, catmod, or kcatmod.)

The list of possible privilege codes is as follows:

CodeDescriptionAllowed Access
cePEditall Readerskcateditall, cateditall, keditall, editall, kcatmod, catmod, kmeta, meta, admin, root
eEditall/Catmod+keditall, editall, kcatmod, catmod, kmeta, meta, admin, root
jJMeta Readerskcatmod, catmod, kmeta, meta, admin, root
k|mKMetas/Metaskmeta, meta, admin, root
kKMetaskmeta, admin, root
mMetasmeta, admin, root
aAdminsadmin, root

Note: The 1 in the example is not related to privileges, see 'godirect' under the 'Other Functions' section for more information about this option.

If you want your tool to show on the ODP::Passport index only for people with the required privileges to use it, you can set a privilege level on the 'Manage my Tools' screen.


Privilege Handling Within Scripts:

For finer granularity, there are two options.

ODP::Passport Provided Functions

ODP::Passport provides a collection of functions for analysing a user's privileges:

Reading the Raw Privilege String

You can read the raw privilege string either from the privileges() function, or the environment variable 'PP_PRIVS'.

Example privilege strings:


Other Functions, Functionality and Environment Variables Provided:

Current User

This is provided by the currentuser() function and the environment variable 'REMOTE_USER' (for compatability with Apache's basic authentication)

Key Issue Time

The time (in seconds since the epoch) the key was issued is available from the 'PP_ISSUED' environment variable.

User's Language

This is provided by the language() function and the environment variable 'PP_LANGUAGE'.

'Go Direct'

When a user tries to access your tool, and doesn't have a valid cookie key, there are two options:

To redirect the user straight to the ODP::Passport server to get a key:

$client -> checklogin('your secret','your tool collection name','tool name',1);

To display a message telling them they need to go to the ODP::Passport server to get a key:

$client -> checklogin('your secret','your tool collection name','tool name',0);

The Blocklist Extension:

Passport, in the nature of its design, allows editors whose accounts have expired access for up to 48 hours. Very, very occasionally, it may be desirable to revoke all Passport access immediately.

By supporting the blocklist extension, when an editor's Passport account is specially revoked, your tools will become inaccessible, immediately, to that editor.

To support the blocklist extension, all you need is to specify a location to store a blocklist, for the login, checklogin, and checklogin_nohalt calls. In the examples it is called pp_config as it is possible additional configuration values may be stored here in the future. Examples:

In login.cgi:

$client -> login('your secret',param('user'),param('key'),param('issued'),param('privs'),'','/home/user/data/pp_config');

In all tools that require Passport:

$client -> checklogin('your secret','your tool collection name','tool name',1,'j','/home/user/data/pp_config');

In all tools that optionally use Passport:

$client -> checklogin_nohalt('your secret','your tool collection name','tool name','/home/user/data/pp_config');

Once you have this setup, you will need to ask rpfuller to enable your collection for the blocklist extension, in order to receive updates.


Protecting Static HTML Pages and Other Files:

If you're using Apache, and have a sufficient level of access, you can install the passport-static wrapper, and configure Apache to use it, in order to protect static pages and files.

First you'll need to copy passport-static to a folder on your web server, for example your cgi-bin folder.

Next you should modify this file to insert the correct values of 'your secret', 'your tool collection', the path to your mime.types file, and whether you want go direct or not. e.g.

my $global_secret = 'mysecret';
my $global_toolcollection = 'mytoolcollection';
my $global_mimetypespath = '/etc/apache/mime.types';
my $global_godirect = 1;
my $ppconfig = '/home/user/data/ppconfig';

The value of ppconfig is optional, see the section on the blocklist extension for more information.

Now you'll need to tell Apache that this script is the correct handler.

Protecting .phtml Files Server-wide

You can do this by adding directives to your httpd.conf file:

AddHandler passport-static .phtml
Action passport-static /cgi-bin/passport-static
where /cgi-bin/passport-static is the webserver relative location you placed passport-static.

Protecting all .html and .phtml Files Server-wide

If you really want to do this:

AddHandler passport-static .html
AddHandler passport-static .phtml
Action passport-static /cgi-bin/passport-static

Protecting All Files Within a Folder

To the .htaccess file add:

SetHandler passport-static
Action passport-static /cgi-bin/passport-static

Note this will break CGIs/PHP scripts/etc. so only use it in a folder containing only static files. Also note that it will not protect directory indexes, so you should turn these off or use appropriate index.html pages.

Protecting Specific File-types Within a Folder

For example to protect .html, .phtml, .jpg, .jpeg and .png files within a folder add to .htaccess:

AddHandler passport-static .html
AddHandler passport-static .phtml
AddHandler passport-static .jpg
AddHandler passport-static .jpeg
AddHandler passport-static .png
Action passport-static /cgi-bin/passport-static

Mailing List:

If you are using ODP::Passport authentication on your site, plan to do so, or are just generally interested in the system, you may wish to sign up to the mailing list, where updates to the system will be announced. Contact rpfuller to join or leave.


Future Developments:

Clearly passport-static is a hack. Clearly I should write a proper Apache module. Clearly I need more hours in a day.

Last Updated: 21st October, 2004 Copyright © 2002-2004 Richard P. Fuller