Skip to content
Browse files

mod_auth_ldap, non-anon binding and openxchange support

The attached patch builds on your work in the following ways:
1. adds support for non-anonymous binding.  Leaving the extra config
variables empty results in an anonymous bind.  I haven't fully tested
this (I cannot test the anonymous case), but any problems should be
apparent immediately.

2. adds support for httpd authentication.  I use mod_auth_ldap to
authenticate access to the site.  This prevents the user having to login
twice.

3. adds support for parts of the openxchange schema
(http://www.openxchange.org). Only the parts I found useful are added.
The openxchange.schema is not included as the user that enables this is
expected to have already installed openxchange.

darcs-hash:20060117111459-bb1f7-cc6f1f6e943f5c6ffa8f393f68c32500505dae67.gz
  • Loading branch information...
1 parent c40429e commit 77d53c7d27865772028df4c45ee81b38ac13f0e7 Daniel Tarbuck committed
Showing with 380 additions and 5 deletions.
  1. +5 −2 README
  2. +27 −0 _htaccess
  3. +19 −0 config.php
  4. +7 −0 entry.php
  5. +21 −2 functions.php
  6. +7 −0 index.php
  7. +13 −1 lang/en.php
  8. +121 −0 template.php
  9. +3 −0 templates/entry_edit.tpl
  10. +3 −0 templates/entry_show.tpl
  11. +11 −0 templates/list_filter.tpl
  12. +71 −0 templates/openxchange_edit.tpl
  13. +72 −0 templates/openxchange_show.tpl
View
7 README
@@ -2,7 +2,7 @@
LDAPab is a webbased address book for small companies. It features a public
address book which is writable for all company staff and a personal address
-book for each staff member. LDAPab require an already setup LDAP server to
+book for each staff member. LDAPab requires an already setup LDAP server to
authenticate users.
=== REQUIREMENTS ===
@@ -18,11 +18,14 @@ with LDAP support (compiled in or as extension)
Copy the contents of the .tgz file to a directory below your webserver document
root. Make sure the webserver is able to write to the cache directory which is
-used for the template engines cache files. A 'chmod 777 cache' will do.
+used for the template engine's cache files. A 'chmod 777 cache' will do.
Open the config.php file in your favourite editor and edit the options
according to your needs. Some knowlege about LDAP may come in handy.
+If HTTP auth is wanted have a look at the provided _htaccess file, edit it as
+needed and rename it back to .htaccess
+
=== SETUP THE LDAP SERVER ===
The following only describes what to do for open-ldap 2.0! If you use any
View
27 _htaccess
@@ -0,0 +1,27 @@
+#
+# Example .htaccess to use with mod_authldap
+#
+
+AuthName "LDAPab"
+AuthType basic
+
+AuthLDAPURL "ldap://ldap/ou=people,o=cosmocode,c=de?uid?one"
+
+### LDAP Bind information
+#AuthLDAPBindDN cn=NonAnon,o=cosmocode,c=de
+#AuthLDAPBindPassword secret
+
+### For LDAP group authentication
+#AuthLDAPGroupAttribute memberUid
+#AuthLDAPGroupAttributeIsDN off
+#require group cn=Users,ou=Groups,o=cosmocode,c=de
+
+### Authorize users individually
+#require user myusername
+
+### Authorize any authenticated user
+require valid-user
+
+### Require SSL for access
+#SSLRequireSSL
+
View
19 config.php
@@ -3,6 +3,9 @@
// Which LDAP Server to use?
$conf[ldapserver] = 'ldap';
+ // Which LDAP Port Server to use? (389 is standard, 636 for ssl)
+ $conf[ldapport] = 389;
+
// What is the root dn on this Server?
$conf[ldaprootdn] = 'o=cosmocode, c=de';
@@ -12,6 +15,10 @@
// How to match users? %u is replaced by the given login
$conf[userfilter] = '(&(uid=%u)(objectClass=posixAccount))';
+ // Use these values to bind to the ldap directory when not logged in (leave blank for anonymous bind)
+ $conf[anonbinddn] = '';
+ $conf[anonbindpw] = '';
+
// Which language to use (see lang directory)
$conf[lang] = 'de';
@@ -22,6 +29,17 @@
$conf[privatebook] = 'ou=contacts';
// Should the additional schema ldapab.schema be used? (0|1)
+ // Note: openxchange and extended are currently exclusive, do not use both at the same time!
+ $conf[extended] = 0;
+
+ // Should we use some parts of the openxchange.schema? (0|1)
+ // Note: openxchange and extended are currently exclusive, do not use both at the same time!
+ $conf[openxchange] = 1;
+
+ // Should we try to login using the username and password provided by httpd? (0|1)
+ $conf[httpd_auth] = 1;
+
+ // Should the additional schema ldapab.schema be used? (0|1)
$conf[extended] = 1;
// Dateformat for birthdays when using extended schema
@@ -31,4 +49,5 @@
// Force recompilation of smarty templates?
$conf[smartycompile] = 0;
+
?>
View
7 entry.php
@@ -41,6 +41,9 @@
tpl_std();
tpl_orgs();
tpl_markers();
+ tpl_categories();
+ tpl_timezone();
+ tpl_country();
//display templates
if($_REQUEST[mode]=='vcf'){
$entry = $smarty->get_template_vars('entry');
@@ -132,6 +135,10 @@ function _saveData(){
if($conf[extended]){
ldap_store_objectclasses($dn,array('inetOrgPerson','contactPerson'));
}
+ // in openxchange mode we have to make sure the right classes are set
+ if ($conf[openxchange]){
+ ldap_store_objectclasses($dn,array('inetOrgPerson','OXUserObject'));
+ }
//modify entry (touches only our attributes)
foreach (array_keys($entries) as $key){
if($key == 'dn'){
View
23 functions.php
@@ -13,6 +13,7 @@ function smarty_std(){
* If it fails it redirects to login.php
*/
function ldap_login(){
+ global $conf;
if(!empty($_SESSION[ldapab][username])){
//existing session! Check if valid
if($_COOKIE[ldapabconid] != $_SESSION[ldapab][conid]){
@@ -20,6 +21,9 @@ function ldap_login(){
header('Location: login.php?username=');
exit;
}
+ } elseif ($conf[httpd_auth] && !empty($_SERVER[PHP_AUTH_USER])) {
+ $_SESSION[ldapab][username] = $_SERVER[PHP_AUTH_USER];
+ $_SESSION[ldapab][password] = $_SERVER[PHP_AUTH_PW];
}
if(!do_ldap_bind($_SESSION[ldapab][username],
@@ -39,7 +43,7 @@ function do_ldap_bind($user,$pass,$dn=""){
//create global connection to LDAP if nessessary
if(!$LDAP_CON){
- $LDAP_CON = ldap_connect($conf[ldapserver]);
+ $LDAP_CON = ldap_connect($conf[ldapserver],$conf[ldapport]);
if(!$LDAP_CON){
die("couldn't connect to LDAP server");
}
@@ -47,7 +51,8 @@ function do_ldap_bind($user,$pass,$dn=""){
if(empty($dn)){
//anonymous bind to lookup users
- if(!ldap_bind($LDAP_CON)){
+ //blank binddn or blank bindpw will result in anonymous bind
+ if(!ldap_bind($LDAP_CON,$conf[anonbinddn],$conf[anonbindpw])){
die("can not bind anonymously");
}
@@ -152,6 +157,17 @@ function namedentries($flip=false){
if($conf[extended]){
$entries[anniversary] = 'anniversary';
}
+ if($conf[openxchange]){
+ $entries[mailDomain] = 'domain';
+ $entries[userCountry] = 'country';
+ $entries[birthDay] = 'birthday';
+ $entries[IPPhone] = 'ipphone';
+ $entries[OXUserCategories] = 'categories';
+ $entries[OXUserInstantMessenger] = 'instantmessenger';
+ $entries[OXTimeZone] = 'timezone';
+ $entries[OXUserPosition] = 'position';
+ $entries[relClientCert] = 'certificate';
+ }
if($flip){
$entries = array_reverse($entries);
@@ -190,6 +206,9 @@ function prepare_ldap_entry($in){
if($conf[extended]){
$out[objectclass][] = 'contactPerson';
}
+ if($conf[openxchange]){
+ $out[objectclass][] = 'OXUserObject';
+ }
utf8_encode_array($out);
View
7 index.php
@@ -42,6 +42,9 @@
//prepare templates
tpl_std();
tpl_markers();
+ tpl_categories();
+ tpl_timezone();
+ tpl_country();
$smarty->assign('list',$list);
$smarty->assign('filter',$_REQUEST['filter']);
$smarty->assign('marker',$_REQUEST['marker']);
@@ -84,12 +87,16 @@ function _makeldapfilter(){
$search = $_REQUEST['search'];
$org = $_REQUEST['org'];
$marker = $_REQUEST['marker'];
+ $categories = $_REQUEST['categories'];
$_SESSION[ldapab][filter] = $filter;
if(empty($filter)) $filter='a';
if(!empty($marker)){
$marker = utf8_encode($marker);
$ldapfilter = "(&(objectClass=contactPerson)(marker=$marker))";
+ }elseif(!empty($categories)){
+ $categories = utf8_encode($categories);
+ $ldapfilter = "(&(objectClass=OXUserObject)(OXUserCategories=$categories))";
}elseif(!empty($search)){
$search = trim($search);
$words=preg_split('/\s+/',$search);
View
14 lang/en.php
@@ -2,7 +2,7 @@
$lang[ldapab] = 'The LDAP address book';
-$lang[name] = 'Name';
+$lang[name] = 'Last Name';
$lang[givenname] = 'First Name';
$lang[title] = 'Title';
$lang[organization] = 'Company';
@@ -66,4 +66,16 @@
$lang[err_noentries] = 'No entries';
$lang[err_ldap] = 'The LDAP server returned the following errors';
+
+$lang[openxchange] = 'Xchange Information';
+$lang[moreopenxchange] = 'Xchange Information Cont\'d';
+$lang[instantmessenger] = 'Inst Msg';
+$lang[categories] = 'Categories';
+$lang[birthday] = 'Birthday';
+$lang[domain] = 'Mail Domain';
+$lang[country] = 'Country';
+$lang[certificate] = 'x.509 Cert';
+$lang[timezone] = 'Time Zone';
+$lang[position] = 'Position';
+$lang[ipphone] = 'IP Phone';
?>
View
121 template.php
@@ -58,6 +58,10 @@ function tpl_entry($in){
//handle marker special in extended mode
$out['marker'] = $in['marker'];
}
+ if ($conf[openxchange]){
+ //handle categories special in openxchange mode
+ $out['categories'] = $in['OXUserCategories'];
+ }
//decode array
utf8_decode_array($out);
@@ -160,4 +164,121 @@ function tpl_orgs(){
$smarty->assign('orgs',$orgs);
}
+/**
+ * assigns all categories to the template
+ */
+function tpl_categories(){
+ global $conf;
+ global $LDAP_CON;
+ global $smarty;
+
+ if(!$conf[openxchange]) return;
+
+ $categories = array();
+
+ $sr = ldap_list($LDAP_CON,$conf[publicbook],"ObjectClass=OXUserObject",array("OXUserCategories"));
+ $result1 = ldap_get_binentries($LDAP_CON, $sr);
+ //check users private addressbook
+ if(!empty($_SESSION[ldapab][binddn])){
+ $sr = @ldap_list($LDAP_CON,
+ $conf[privatebook].','.$_SESSION[ldapab][binddn],
+ "ObjectClass=OXUserObject",array("OXUserCategories"));
+ $result2 = ldap_get_binentries($LDAP_CON, $sr);
+ }
+ $result = array_merge($result1,$result2);
+
+ if(count($result)){
+ foreach ($result as $entry){
+ if(count($entry['OXUserCategories'])){
+ foreach($entry['OXUserCategories'] as $category){
+ array_push($categories, $category);
+ }
+ }
+ }
+ }
+ $categories = array_unique($categories);
+ sort($categories,SORT_STRING);
+
+ utf8_decode_array($categories);
+ $smarty->assign('categories',$categories);
+}
+
+/**
+ * assigns all timezones to the template
+ */
+function tpl_timezone(){
+ global $conf;
+ global $LDAP_CON;
+ global $smarty;
+
+ if(!$conf[openxchange]) return;
+
+ $timezone = array();
+
+ $sr = ldap_list($LDAP_CON,$conf[publicbook],"ObjectClass=OXUserObject",array("OXTimeZone"));
+ $result1 = ldap_get_binentries($LDAP_CON, $sr);
+ //check users private addressbook
+ if(!empty($_SESSION[ldapab][binddn])){
+ $sr = @ldap_list($LDAP_CON,
+ $conf[privatebook].','.$_SESSION[ldapab][binddn],
+ "ObjectClass=OXUserObject",array("OXTimeZone"));
+ $result2 = ldap_get_binentries($LDAP_CON, $sr);
+ }
+ $result = array_merge($result1,$result2);
+
+ if(count($result)){
+ foreach ($result as $entry){
+ if(count($entry['OXTimeZone'])){
+ foreach($entry['OXTimeZone'] as $tz){
+ array_push($timezone, $tz);
+ }
+ }
+ }
+ }
+ $timezone = array_unique($timezone);
+ sort($timezone,SORT_STRING);
+
+ utf8_decode_array($timezone);
+ $smarty->assign('timezone',$timezone);
+}
+
+/**
+ * assigns all countries to the template
+ */
+function tpl_country(){
+ global $conf;
+ global $LDAP_CON;
+ global $smarty;
+
+ if(!$conf[openxchange]) return;
+
+ $country = array();
+
+ $sr = ldap_list($LDAP_CON,$conf[publicbook],"ObjectClass=OXUserObject",array("userCountry"));
+ $result1 = ldap_get_binentries($LDAP_CON, $sr);
+ //check users private addressbook
+ if(!empty($_SESSION[ldapab][binddn])){
+ $sr = @ldap_list($LDAP_CON,
+ $conf[privatebook].','.$_SESSION[ldapab][binddn],
+ "ObjectClass=OXUserObject",array("userCountry"));
+ $result2 = ldap_get_binentries($LDAP_CON, $sr);
+ }
+ $result = array_merge($result1,$result2);
+
+ if(count($result)){
+ foreach ($result as $entry){
+ if(count($entry['userCountry'])){
+ foreach($entry['userCountry'] as $c){
+ array_push($country, $c);
+ }
+ }
+ }
+ }
+ $country = array_unique($country);
+ sort($country,SORT_STRING);
+
+ utf8_decode_array($country);
+ $smarty->assign('country',$country);
+}
+
?>
View
3 templates/entry_edit.tpl
@@ -169,6 +169,9 @@
</td>
</tr>
{/if}
+ {if $conf.openxchange}
+ {include file="openxchange_edit.tpl"}
+ {/if}
<tr>
<td colspan="2" align="center"><br><input type="submit" class="input" value="{$lang.submit}"></td>
</tr>
View
3 templates/entry_show.tpl
@@ -116,5 +116,8 @@
</td>
</tr>
</table>
+ {if $conf.openxchange}
+ {include file="openxchange_show.tpl"}
+ {/if}
<br><br><br>
View
11 templates/list_filter.tpl
@@ -30,6 +30,17 @@
<a href="index.php?filter=other">#</a>
<a href="index.php?filter=*">*</a>
</td>
+{if $conf.openxchange}
+<td class="filterrow" align="right">
+ <form method="get" action="index.php" style="display:inline">
+ <select name="categories" class="searchfield">
+ <option value="">--- {$lang.categories} ---</option>
+ {html_options values=$categories output=$categories selected=$smarty.request.categories}
+ </select>
+ <input type="submit" value="{$lang.search}" class="searchbutton">
+ </form>
+</td>
+{/if}
{if $conf.extended}
<td class="filterrow" align="right">
<form method="get" action="index.php" style="display:inline">
View
71 templates/openxchange_edit.tpl
@@ -0,0 +1,71 @@
+<tr>
+<td width="50%" valign="top" align="center">
+ <table>
+ <tr>
+ <td colspan="2"><b>{$lang.openxchange}</b></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.birthday}:</td>
+ <td><input type="text" class="input" name="entry[birthday]" value="{$entry.birthday|escape}"></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.ipphone}:</td>
+ <td><input type="text" class="input" name="entry[ipphone]" value="{$entry.ipphone|escape}"></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.instantmessenger}:</td>
+ <td><input type="text" class="input" name="entry[instantmessenger]" value="{$entry.instantmessenger|escape}"></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.domain}:</td>
+ <td><input type="text" class="input" name="entry[domain]" value="{$entry.domain|escape}"></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.certificate}:</td>
+ <td>
+ <textarea name="entry[certificate]" rows=6 cols=28 onClick="this.form.elements['entry[certificate]'].select();">{$entry.certificate|escape}</textarea>
+ <br>
+ <input type=button name=clearCert value="Clear" onClick="if (confirm('Are you sure?')) this.form.elements['entry[certificate]'].value='';">
+ </td>
+ </tr>
+ </table>
+</td>
+<td width="50%" valign="top" align="center">
+ <table>
+ <tr>
+ <td colspan="2"><b>{$lang.moreopenxchange}</b></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.country}:</td>
+ <td>
+ <select name="entry[country][]" class="input">
+ <option value="">--- {$lang.select} ---</option>
+ {html_options values=$country output=$country selected=$entry.country}
+ </select><br>
+ <input type="text" class="inputbr" name="entry[country][]"><br>
+ </td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.timezone}:</td>
+ <td>
+ <select name="entry[timezone][]" class="input">
+ <option value="">--- {$lang.select} ---</option>
+ {html_options values=$timezone output=$timezone selected=$entry.timezone}
+ </select><br>
+ <input type="text" class="inputbr" name="entry[timezone][]"><br>
+ </td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.categories}:</td>
+ <td>
+ <select name="entry[categories][]" size="5" class="input" multiple="multiple">
+ {html_options values=$categories output=$categories selected=$entry.categories}
+ </select><br>
+ <input type="text" class="inputbr" name="entry[categories][]"><br>
+ <input type="text" class="inputbr" name="entry[categories][]"><br>
+ </td>
+
+ </tr>
+</table>
+</td>
+</tr>
View
72 templates/openxchange_show.tpl
@@ -0,0 +1,72 @@
+<table width="100%">
+ <tr>
+ <td width="50%" valign="top">
+ <b>{$lang.openxchange}</b>
+<dl><dd>
+ <table>
+ {if $entry.birthday}
+ <tr>
+ <td align="right">{$lang.birthday}:</td>
+ <td>{$entry.birthday}</td>
+ </tr>
+ {/if}
+ {if $entry.ipphone}
+ <tr>
+ <td align="right">{$lang.ipphone}:</td>
+ <td>{$entry.ipphone}</td>
+ </tr>
+ {/if}
+ {if $entry.instantmessenger}
+ <tr>
+ <td align="right">{$lang.instantmessenger}:</td>
+ <td>{$entry.instantmessenger}</td>
+ </tr>
+ {/if}
+ {if $entry.domain}
+ <tr>
+ <td align="right">{$lang.domain}:</td>
+ <td>{$entry.domain}</td>
+ </tr>
+ {/if}
+ {if $entry.certificate}
+ <tr>
+ <td align="right">{$lang.certificate}:</td>
+ <td><form>
+ <textarea rows=3 cols=28 name='certificate' onClick='this.form.certificate.select();'>{$entry.certificate}</textarea>
+ </form></td>
+ </tr>
+ {/if}
+ </table>
+</dd></dl>
+</td>
+ <td width="50%" valign="top">
+ <b>{$lang.moreopenxchange}</b>
+<dl><dd>
+<table >
+ {if $entry.country}
+ <tr>
+ <td align="right">{$lang.country}:</td>
+ <td>{$entry.country}</td>
+ </tr>
+ {/if}
+ {if $entry.timezone}
+ <tr>
+ <td align="right">{$lang.timezone}:</td>
+ <td>{$entry.timezone}</td>
+ </tr>
+ {/if}
+ {if $entry.categories}
+ <tr>
+ <td valign="top" align="right">{$lang.categories}:</td>
+ <td>
+ {foreach from=$entry.categories item=categories}
+ {$categories}<br>
+ {/foreach}
+ </td>
+ </tr>
+ {/if}
+</table>
+</dd></dl>
+ </td>
+ </tr>
+</table>

0 comments on commit 77d53c7

Please sign in to comment.
Something went wrong with that request. Please try again.