Introduction
To be able to create a [intlink id="64" type="post" target="_self"]Document Management System in Word using WebDAV[/intlink] on Apache webserver, I did some research on mod_dav, an Apache module to provide WebDAV support. It appears that the authaurisation part of mod_dav is limited to “allow all” / “deny all”, which did not quite suite my needs. The solution needed more fine-grained authorisation on a per-directory level. Another problem was that the authentication and authorisation data was in a database.
Issues
- Mod_dav does not have a fine-grained authorisation system on a per-directory level.
- Directory structure is known but unstable.
- Authorisation data is held in a database and requires complex SQL.
Solution
Fine-grained authorisation
I started by looking at mod_auth and mod_auth_dbd (in combination with mod_dbd), but the SQL useable by mod_dbd only received the username and password as parameters, not the requested URI. What mod_auth module could possibly be used? The solution to this was quite unexpected: mod_auth_script.
Mod_auth_script allows CGI or PHP scripts to handle the authorisation. By looking at the REQUEST_URI the script can easily decide to simulate authorisation by invalidating the authorisation.
I created a PHP script that at first would log the REQUEST_URI and allow access for any username and password. All that is left now is to handle authorisation based on the directory structure and fetch the real authorisation from the database.
<Location /dav>
Options +Indexes
Dav On
AuthType Basic
AuthName "PHP based authentication for mod_dav"
AuthScriptFile /any_full_path_your_apache_can_get_to/.auth.php
Require valid-user
</Location>
<?php
/**
* PHP based authentication for mod_dav
* @author Jeffrey Ridout
* @link http://blog.itwarlocks.com
* @license http://creativecommons.org/licenses/by-sa/3.0
* @date 2009-04-27
* @version 0.0.1
*/
/**
* $_SERVER['REQUEST_URI'] contains the location accessed by the WebDAV client.
* Using parse_url enables easy access to all URL parts.
*/
$requestURI = parse_url($_SERVER['REQUEST_URI']);
/**
* Accessing some pages directly, like a page containing phpinfo(), will result in several
* calls to .auth.php. These do not need to be logged.
*/
if ($requestURI['path'] != $_SERVER['SCRIPT_NAME']) {
$logFile = fopen('auth.log', 'a');
fwrite($logFile, "${_SERVER['PHP_AUTH_USER']}:${_SERVER['PHP_AUTH_PW']}@${requestURI['path']}\n");
fclose($logFile);
}
/**
* At this stage we always allow access.
* mod_auth_script catches custom headers to define the result to mod_auth.
* auth-script:allow|deny|prompt Defines the authentication result.
*/
header('auth-script:allow');
/* EOF */
?>
Directory structure
The sub-directories are named according to a strict rule, but they are unknown to exist at design-time. Therefore the script needs to analyse the destination location and extract the key to be used for authorisation. ((You may notice that year only has 2 digits. so no, it’s not Y2K safe…))
/<Day>/<Month>/<Year>/<MemberID>
<Day>: 00 - 31
<Month>: 00 - 12
<Year>: 00 - 99
<MemberID>: DDMMYY#####
This directory structure allows for a quite straight forward check to get the MemberID. ((MemberID in this case is the Norwegian version of a social security number.))
<?php
/**
* PHP based authentication for mod_dav
* @author Jeffrey Ridout
* @link http://blog.itwarlocks.com
* @license http://creativecommons.org/licenses/by-sa/3.0
* @date 2009-04-27
* @version 0.0.2
*/
/**
* $_SERVER['REQUEST_URI'] contains the location accessed by the WebDAV client.
* Using parse_url enables easy access to all URL parts.
*/
$requestURI = parse_url($_SERVER['REQUEST_URI']);
/**
* Accessing some pages directly, like a page containing phpinfo(), will result in several
* calls to .auth.php. These do not need to be logged.
*/
if ($requestURI['path'] != $_SERVER['SCRIPT_NAME']) {
$logFile = fopen('auth.log', 'a');
fwrite($logFile, "${_SERVER['PHP_AUTH_USER']}:${_SERVER['PHP_AUTH_PW']}@${requestURI['path']}\n");
fclose($logFile);
}
/**
* At stage 2 we can simulate a real authorisation lookup by using a simple array.
* mod_auth_script catches custom headers to define the result to mod_auth.
* auth-script:allow|deny|prompt Defines the authentication result.
*/
$blocked = array (
'01010154321',
'31037912345'
);
/**
* The directory name to search for might be in the path or destination filename.
* .../blocked-dir
* or
* .../blocked-dir/
* or
* .../blocked-dir/...
*/
$path = dirname($requestURI['path']);
$dir = basename($path);
$match = basename($rURL['path']);
if (preg_match('#$\d{11}^#', $dir)) {
$match = $dir;
}
if (!in_array($match, $blocked)) {
header('auth-script:allow');
} else {
/* Simulate authorisation by denying access.
* Normal authentication would return auth-script:prompt
*/
header('auth-script:deny');
}
/* EOF */
?>
Authorisation data
The data that determines the authorisation can come from different sources; files, LDAP, RADIUS, database, …
In my case the authorisation result was retrieved from complex SQL using a stored procedure in a Sybase Database.
Whatever you use as your source of authentication/authorisation, remember that request can be asynchrounous and parallel. For a file base method that means never locking the file and opening it as “read-only”, for an external connection based method, it means using persistent connections.
Conclusion
Just because mod-dav and mod_auth don’t support your exact needs, doesn’t mean there is no solution. Using mod_auth_script to create your own tailored flexible solution works perfectly. As an added bonus this adds a new processing layer to PHP by allowing PHP to be executed at a much earlier stage. Using a PHP script to authorise also allows authorisation of non-existing destination URLs or destinations which access is based on complex business logic.
All code in this post is licensed under Creative Commons Attribution Share Alike.

Thanks for this great article, combined with the related article about “Accessing WebDAV in Microsoft Word Visual Basic” this helped me a lot since there’s not a lot of good documentation about this topic.
Greetings from The Netherlands