Archive

Posts Tagged ‘ldap’

Export contacts from a Windows AddressBook (.wab) to LDAP

July 18, 2008 Leave a comment
This article was first written in December 2004 for the BeezNest technical
website (http://glasnost.beeznest.org/articles/193).

Convert your .wab file with libwab – turns Windows Address Book (WAB) files into ldif.

Then, import the newly-created ldif file into your LDAP server, after having adapted the entries if needed.

See also the Libpst project.

Advertisements
Categories: English, Tech Crunch Tags: ,

OpenLDAP

July 14, 2008 Leave a comment
This article was first written in August 2004 for the BeezNest technical
website (http://glasnost.beeznest.org/articles/153).

OpenLDAP is an open source implementation of the Lightweight Directory Access Protocol. The suite includes:

  • slapd – stand-alone LDAP server
  • slurpd – stand-alone LDAP replication server
  • libraries implementing the LDAP protocol, and
  • utilities, tools, and sample clients.

The documentation can be found here: http://www.openldap.org/doc/.

Categories: English, OSS Solutions Tags: ,

Export contacts from Exchange server to LDAP

July 8, 2008 Leave a comment
This article was first written in June 2004 for the BeezNest technical
website (http://glasnost.beeznest.org/articles/132).

This article explains how I managed to export addressbook contact information from a Microsoft Exchange server, to a LDAP server. (The final purpose was to enable all users to use a shared addressbook in Evolution).

I had first to ask the technical employees from the migrated company to export contact information as a set of CSV (Comma-Separated Values) files.

Then I wrote a small Pythin script to convert them to LDIF. The LDIF format can be used with command-line tools to insert/edit the records in a LDAP database, like this:

$ ldapadd -P 2 -x -D cn=myuser,dc=test,dc=be -w password -h ldap.test.be -f test.ldif

Here is the script I used:

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Converts .csv output from Outlook Express addressbook to .grcd GNOME adressbook

import os,sys,csv,ldif,string,re

filename = sys.argv[1]

#
#note: it seems that vcard data must be encoded as CHARSET=UTF-8;QUOTED-PRINTABLE: when they contain data other than ASCII...
#TODO

#note: there seems to be a bug in python csv module...if you don't insert all first fields,
#it doesn't detect them: if you skip field 2, it won't detect field 3, 4, etc.
#--> to report!
# see http://www.python.org/doc/2.3.2/lib/csv-contents.html
#note: we could deduce the fields dictionnary from the first line ;-)
reader = csv.DictReader(file(filename),["Titel","Voornaam","Middelste naam","Achternaam","Achtervoegsel","Bedrijf","Afdeling","Functie","Werkadres, straat","Werkadres 2, straat","Werkadres 3, straat","Werkadres, plaats","Werkadres, provincie","Werkadres, postcode","Werkadres, land","Huisadres, straat","Huisadres, straat 2","Huisadres, straat 3","Huisadres, plaats","Huisadres, provincie","Huisadres, postcode","Huisadres, land","Ander adres, straat","Ander adres, straat 2","Ander adres, straat 3","Ander adres, plaats","Ander adres, provincie","Ander adres, postcode","Ander adres, land","Telefoon assistent","Fax op werk","Telefoon op werk","Telefoon op werk 2","Terugbellen","Autotelefoon","Hoofdtelefoon bedrijf","Fax thuis","Telefoon thuis","Telefoon thuis 2","ISDN","Mobiele telefoon","Andere fax","Andere telefoon","Pager","Hoofdtelefoon","Radiotelefoon","Teksttelefoon","Telex","Account","Beroep","Categorieën","Directory-server","E-mailadres","E-mail, weergegeven naam ","E-mailadres 2","E-mail, weergegeven naam 2","E-mailadres 3","E-mail, weergegeven naam 3","Factuurinformatie","Gebruiker 1","Gebruiker 2","Gebruiker 3","Gebruiker 4","Geslacht","Gevoeligheid","Hobby's","Initialen","Kantoorlocatie","Kinderen","Locatie","Naam assistent","Naam manager","Notities","Organisatie-id","Partner","Postbus","Prioriteit","Privé","Referentie van","Reisafstand","Sofi-nummer","Speciale datum","Taal","Trefwoorden","Verjaardag","Vrije/bezette tijden voor Internet-gebruik","Webpagina"])

xlate = {
 u'\N{ACUTE ACCENT}': "'",
 u'\N{BROKEN BAR}': '|',
 u'\N{CEDILLA}': '{cedilla}',
 u'\N{CENT SIGN}': '{cent}',
 u'\N{COPYRIGHT SIGN}': '{C}',
 u'\N{CURRENCY SIGN}': '{currency}',
 u'\N{DEGREE SIGN}': '{degrees}',
 u'\N{DIAERESIS}': '{umlaut}',
 u'\N{DIVISION SIGN}': '/',
 u'\N{FEMININE ORDINAL INDICATOR}': '{^a}',
 u'\N{INVERTED EXCLAMATION MARK}': '!',
 u'\N{INVERTED QUESTION MARK}': '?',
 u'\N{LATIN CAPITAL LETTER A WITH ACUTE}': 'A',
 u'\N{LATIN CAPITAL LETTER A WITH CIRCUMFLEX}': 'A',
 u'\N{LATIN CAPITAL LETTER A WITH DIAERESIS}': 'A',
 u'\N{LATIN CAPITAL LETTER A WITH GRAVE}': 'A',
 u'\N{LATIN CAPITAL LETTER A WITH RING ABOVE}': 'A',
 u'\N{LATIN CAPITAL LETTER A WITH TILDE}': 'A',
 u'\N{LATIN CAPITAL LETTER AE}': 'Ae',
 u'\N{LATIN CAPITAL LETTER C WITH CEDILLA}': 'C',
 u'\N{LATIN CAPITAL LETTER E WITH ACUTE}': 'E',
 u'\N{LATIN CAPITAL LETTER E WITH CIRCUMFLEX}': 'E',
 u'\N{LATIN CAPITAL LETTER E WITH DIAERESIS}': 'E',
 u'\N{LATIN CAPITAL LETTER E WITH GRAVE}': 'E',
 u'\N{LATIN CAPITAL LETTER ETH}': 'Th',
 u'\N{LATIN CAPITAL LETTER I WITH ACUTE}': 'I',
 u'\N{LATIN CAPITAL LETTER I WITH CIRCUMFLEX}': 'I',
 u'\N{LATIN CAPITAL LETTER I WITH DIAERESIS}': 'I',
 u'\N{LATIN CAPITAL LETTER I WITH GRAVE}': 'I',
 u'\N{LATIN CAPITAL LETTER N WITH TILDE}': 'N',
 u'\N{LATIN CAPITAL LETTER O WITH ACUTE}': 'O',
 u'\N{LATIN CAPITAL LETTER O WITH CIRCUMFLEX}': 'O',
 u'\N{LATIN CAPITAL LETTER O WITH DIAERESIS}': 'O',
 u'\N{LATIN CAPITAL LETTER O WITH GRAVE}': 'O',
 u'\N{LATIN CAPITAL LETTER O WITH STROKE}': 'O',
 u'\N{LATIN CAPITAL LETTER O WITH TILDE}': 'O',
 u'\N{LATIN CAPITAL LETTER THORN}': 'th',
 u'\N{LATIN CAPITAL LETTER U WITH ACUTE}': 'U',
 u'\N{LATIN CAPITAL LETTER U WITH CIRCUMFLEX}': 'U',
 u'\N{LATIN CAPITAL LETTER U WITH DIAERESIS}': 'U',
 u'\N{LATIN CAPITAL LETTER U WITH GRAVE}': 'U',
 u'\N{LATIN CAPITAL LETTER Y WITH ACUTE}': 'Y',
 u'\N{LATIN SMALL LETTER A WITH ACUTE}': 'a',
 u'\N{LATIN SMALL LETTER A WITH CIRCUMFLEX}': 'a',
 u'\N{LATIN SMALL LETTER A WITH DIAERESIS}': 'a',
 u'\N{LATIN SMALL LETTER A WITH GRAVE}': 'a',
 u'\N{LATIN SMALL LETTER A WITH RING ABOVE}': 'a',
 u'\N{LATIN SMALL LETTER A WITH TILDE}': 'a',
 u'\N{LATIN SMALL LETTER AE}': 'ae',
 u'\N{LATIN SMALL LETTER C WITH CEDILLA}': 'c',
 u'\N{LATIN SMALL LETTER E WITH ACUTE}': 'e',
 u'\N{LATIN SMALL LETTER E WITH CIRCUMFLEX}': 'e',
 u'\N{LATIN SMALL LETTER E WITH DIAERESIS}': 'e',
 u'\N{LATIN SMALL LETTER E WITH GRAVE}': 'e',
 u'\N{LATIN SMALL LETTER ETH}': 'th',
 u'\N{LATIN SMALL LETTER I WITH ACUTE}': 'i',
 u'\N{LATIN SMALL LETTER I WITH CIRCUMFLEX}': 'i',
 u'\N{LATIN SMALL LETTER I WITH DIAERESIS}': 'i',
 u'\N{LATIN SMALL LETTER I WITH GRAVE}': 'i',
 u'\N{LATIN SMALL LETTER N WITH TILDE}': 'n',
 u'\N{LATIN SMALL LETTER O WITH ACUTE}': 'o',
 u'\N{LATIN SMALL LETTER O WITH CIRCUMFLEX}': 'o',
 u'\N{LATIN SMALL LETTER O WITH DIAERESIS}': 'o',
 u'\N{LATIN SMALL LETTER O WITH GRAVE}': 'o',
 u'\N{LATIN SMALL LETTER O WITH STROKE}': 'o',
 u'\N{LATIN SMALL LETTER O WITH TILDE}': 'o',
 u'\N{LATIN SMALL LETTER SHARP S}': 'ss',
 u'\N{LATIN SMALL LETTER THORN}': 'th',
 u'\N{LATIN SMALL LETTER U WITH ACUTE}': 'u',
 u'\N{LATIN SMALL LETTER U WITH CIRCUMFLEX}': 'u',
 u'\N{LATIN SMALL LETTER U WITH DIAERESIS}': 'u',
 u'\N{LATIN SMALL LETTER U WITH GRAVE}': 'u',
 u'\N{LATIN SMALL LETTER Y WITH ACUTE}': 'y',
 u'\N{LATIN SMALL LETTER Y WITH DIAERESIS}': 'y',
 u'\N{LEFT-POINTING DOUBLE ANGLE QUOTATION MARK}': '<<',
 u'\N{MACRON}': '_',
 u'\N{MASCULINE ORDINAL INDICATOR}': '{^o}',
 u'\N{MICRO SIGN}': '{micro}',
 u'\N{MIDDLE DOT}': '*',
 u'\N{MULTIPLICATION SIGN}': '*',
 u'\N{NOT SIGN}': '{not}',
 u'\N{PILCROW SIGN}': '{paragraph}',
 u'\N{PLUS-MINUS SIGN}': '{+/-}',
 u'\N{POUND SIGN}': '{pound}',
 u'\N{REGISTERED SIGN}': '{R}',
 u'\N{RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK}': '>>',
 u'\N{SECTION SIGN}': '{section}',
 u'\N{SOFT HYPHEN}': '-',
 u'\N{SUPERSCRIPT ONE}': '{^1}',
 u'\N{SUPERSCRIPT THREE}': '{^3}',
 u'\N{SUPERSCRIPT TWO}': '{^2}',
 u'\N{VULGAR FRACTION ONE HALF}': '{1/2}',
 u'\N{VULGAR FRACTION ONE QUARTER}': '{1/4}',
 u'\N{VULGAR FRACTION THREE QUARTERS}': '{3/4}',
 u'\N{YEN SIGN}': '{yen}'
}

nonasciire = re.compile(u'([\x00-\x7f]+)|([^\x00-\x7f])', re.UNICODE).sub

def latin1_to_ascii (unicrap):
    return str(nonasciire(lambda x: x.group(1) or xlate.setdefault(ord(x.group(2)), ''), unicrap))

def addIfNonNullElements(dict, name, list):
    """
    adds non-null elements otf this list as value in the dictionnary
    """
    reduced = filter(lambda x: x, list)
    reduced = map(toUTF8, reduced)
    if len(reduced) > 0:
        dict[name] = reduced

def isAscii(ustring):
    """returns true if unicode string can be encoded as ascii string. false otherwise"""
    try:
        ustring.encode('ascii')
        #print "%s is ascii" % ustring
        return True
    except (UnicodeEncodeError,UnicodeDecodeError):
        #print "%s is not ascii" % ustring
        return False

def toUTF8(string):
    return string.decode('utf-8').encode('utf-8')

def cleanAddress(string):
    if not string.replace('$', '').replace(' ','') == '':
        return toUTF8(string)
    else:
        return None

def joinNonNulls(stringList, separator):
    return string.join(filter(lambda x: not x == '', stringList), separator)

#skip first line;
next = reader.next()
for next in reader:

    #hack to correct entries who have first name and family name under 'Voornaam'
    #or who do not have any name...;-)
    if not next['Achternaam']:
        list = string.split(next['Voornaam'])
        if len(list) == 2:
            next['Voornaam'], next['Achternaam'] = tuple(list)
        elif len(list) == 3:
            next['Voornaam'] = list[0]
            next['Achternaam'] = list[1] + ' ' + list[2]
        elif len(list) == 4:
            next['Voornaam'] = list[0]
            next['Achternaam'] = list[1] + ' ' + list[2] + ' ' + list[3]
        elif len(list) == 1:
            next['Voornaam'] = list[0]
            next['Achternaam'] = list[0]
        elif len(list) == 0:
            next['Voornaam'] = next['Bedrijf']
            next['Achternaam'] = next['Bedrijf']
        else:
            raise Exception('problem splitting name: ' + repr(list))

    cn = toUTF8(next['Voornaam'] + ' ' + next['Achternaam'])
    #if not isAscii(cn):
    #  cn = toUTF8(cn)
    dn = toUTF8('cn=' + cn + ',ou=People,dc=profondo-brussels,dc=be')
    print "#%s" % dn  
    entry={'objectClass':['top','person', 'organizationalPerson', 'inetOrgPerson', 'evolutionPerson']}
    addIfNonNullElements(entry,'cn', [cn])
    addIfNonNullElements(entry, 'sn', [next['Achternaam']])

    #additional hack: who would use non-ascii chars in e-mail? ;-)
    emails =  map(latin1_to_ascii, filter(lambda x: x, [next['E-mailadres'], next['E-mailadres 2'], next['E-mailadres 3']]))

    addIfNonNullElements(entry, 'mail', emails)
    addIfNonNullElements(entry, 'fileAs', [next['Achternaam'] + ', ' + next['Voornaam']])
    addIfNonNullElements(entry,'category',[sys.argv[2]])
    addIfNonNullElements(entry,'o',[next['Bedrijf']])
    addIfNonNullElements(entry,'mobile',[next['Mobiele telefoon']])
    addIfNonNullElements(entry,'note',[next['Notities']])
    addIfNonNullElements(entry,'telephoneNumber',[next['Telefoon op werk']])
    addIfNonNullElements(entry,'homePhone',[next['Telefoon thuis']])
    addIfNonNullElements(entry,'facsimileTelephoneNumber',[next['Fax op werk']])
    #entry['ou'] =
    #entry['roomNumber'] =
    addIfNonNullElements(entry,'title',[next['Functie']])
    #entry['businessRole'] =
    #entry['managerName'] =
    #entry['assistantName'] =
    #strange format to store addresses...
    #see http://www.faqs.org/rfcs/rfc2252.html --> 6.27. Postal Address
    addIfNonNullElements(entry,'postalAddress', [cleanAddress(joinNonNulls([next['Werkadres, straat'], next['Werkadres 2, straat'], next['Werkadres, plaats'] + ' ' + next['Werkadres, provincie'] + ' ' + next['Werkadres, postcode'], next['Werkadres, land']], '\n'))])
    addIfNonNullElements(entry,'homePostalAddress', [cleanAddress(joinNonNulls([next['Huisadres, straat'], next['Huisadres, straat 2'], next['Huisadres, plaats'] + ' ' + next['Huisadres, provincie'] + ' ' + next['Huisadres, postcode'], next['Huisadres, land']], '\n'))])
    addIfNonNullElements(entry,'otherPostalAddress', [cleanAddress(joinNonNulls([next['Ander adres, straat'], next['Ander adres, straat 2'], next['Ander adres, plaats'] + ' ' + next['Ander adres, provincie'] + ' ' + next['Ander adres, postcode'], next['Ander adres, land']], '\n'))])

    ldif_writer=ldif.LDIFWriter(sys.stdout)
    ldif_writer.unparse(dn,entry)

LDAP, Active Directory and LDAP/SSO

December 14, 2007 13 comments

I’ve had to reply to part of a call for tender today. The interesting question was “Can Dokeos interact with our LDAP/SSO system?”.

So first I should review the vocabulary a bit. After searching the web for a while, I can most probably say that LDAP and LDAP/SSO are actually the same thing.

  • LDAP goes for Lightweight Directory Access Protocol (there is actually a heavyweight DAP protocol)
  • SSO goes for Single Sign-On (which means it’s a system by which a user only has to sign-in once to access multiple applications, for example)

Basically, the implemented result of LDAP is that one server has the credentials of a lot of users in a structured data tree, and that anybody using an application connected to that LDAP server can say “I’m xyz” and the authentication is then made by contacting the LDAP server to ask if the user is really who he says he is, and what information we can get.

So this is a Single Sign-On technology, which means LDAP/SSO is a redundant acronym.

Active Directory is the Microsoft’s home-made system that has the same features as LDAP but is not LDAP, so you have to do an implementation just for them (I’m being told the rules also change from one version of Windows server to the other, which makes implementations dependent on versions, which is not practical).

Luckily, Microsoft (or other people actually, I don’t know) realised that this was not practical, so they offered a translation system from Active Directory to LDAP, which makes it easily possible to use an Active Directory server as a LDAP server.

Now, let’s talk a bit about Dokeos and LDAP

Dokeos offers an LDAP extension which provides it with an almost-easy way to connect to an LDAP server and get authentication data from there. It’s almost easy because:

  • it’s shipped by default with all versions of Dokeos (from 1.6.0 at least)
  • it’s configurable via only one file (but it’s not configurable via the web interface)
  • there is a different login page for LDAP which takes the login in charge

The LDAP extension had been originally contributed by Evie (R.) Embrechts around 2003.
Just recently, a contribution (to be integrated by me in the coming weeks) has been shared by Mustapha Alouani, which eases greatly the use of the LDAP extension by providing a web interface to do various administrative tasks.

This is a massive improvement regarding LDAP integration. You can tell that over 4 years of development, this is the first big step in that direction.

On another bright side, I’ve just integrated OpenID login support into Dokeos 1.8.5 using the Drupal code for OpenID, which means it’s now possible to use another, very recent, practical and easy, Single Sign-On method in Dokeos.

Categories: Development, English Tags: , ,

OpenLDAP online backups

June 6, 2005 Leave a comment
This article was first written in June 2005 for the BeezNest technical
website (http://glasnost.beeznest.org/articles/269).

For BDB and HDB backends only, online backups (ie without stopping slapd to backup) are possible with slapcat.

For example, to backup the directory “dc=example,dc=com” to /tmp/ldap-backup.ldif:

slapcat -f /etc/ldap/slapd.conf -b "dc=example,dc=com" -l /tmp/ldap-backup.ldif

For other backends, the best solution is to replicate to another LDAP server and backup this one.

See also: How do I backup my directory?

Categories: English, Tech Crunch Tags: , ,

HOWTO Use Samba as PDC using LDAP on Debian

October 15, 2004 Leave a comment
This article was first written in October 2004 for the BeezNest technical
website (http://glasnost.beeznest.org/articles/180).

Starting from Debian Sarge, the Samba version which ship with Debian is 3.0.x. This is the first real version to support well being a PDC while using LDAP as backend.

Here is how to use Samba as PDC with LDAP backend for authentication on Debian.

Install the following packages, which are all part of Samba

  • samba: The server itself
  • samba-doc: The documentation (very complete)
  • smbclient: FTP-like client for SMB/CiFS
  • swat: Samba Web Administration Tool (web interface to configure Samba and access the full documentation through a browser, if installed)

Aside, we will install LDAP

  • slapd: the server itself

Some companies provide useful tools to help in the setting up of a Samba server with LDAP as PDC:

  • smbldap-tools: IDEALX tools for Samba use to ease installation and migration when using together with LDAP

Configure the Name Service Switch to use LDAP

Install package libnss-ldap and configure it according to the LDAP configuration [1].

Edit /etc/nsswitch.conf by adding a mention ldap to the end of the three following lines: passwd:, group:, shadow: and a mention wins to the end of the line hosts:.

Configure PAM to also use LDAP

Install package libpam-ldap and configure it according to the LDAP configuration.

Edit /etc/pam.d/common-account and add the following line before the existing second line:

account  sufficient     pam_ldap.so
account required        pam_unix.so

Edit /etc/pam.d/common-auth and add the following line before the existing second line:

auth    sufficient      pam_ldap.so
auth    required        pam_unix.so nullok_secure

Edit /etc/pam.d/common-password and add the following line before the existing second line:

password   sufficient pam_ldap.so
password   required   pam_unix.so nullok obscure min=4 max=8 md5

Set the LDAP password in Samba

Samba stores its passwords in /var/lib/samba/secrets.tdb, and also stores there the LDAP admin password to use to connect to OpenLDAP. To set/change the password:

smbpasswd -w MySecretPassword

where MySecretPassword is LDAP’s admin password.

WARNING: with that configuration, it asks twice to type password to authenticate anyone! TODO

Add the Samba schema to OpenLDAP’s list of schemas

An example that you can copy “as is” (well, you just need to unzip it first) is available in /usr/share/doc/samba-doc/examples/LDAP/samba.schema.gz in /etc/ldap/schemas and edit /etc/ldap/slapd.conf to use it.

Configure /etc/ldap/ldap.conf and /etc/ldap/slapd.conf.

Tools to manage it afterwards

LDAP Account Manager (LAM) (web-based frontend to Samba accounts for machines, users and groups in LDAP) or phpLDAPadmin (which is not specific for managing Samba, but can also manage addressbooks, UNIX authentication, …).

Create your machines in the Domain

To be continued…


[1] it will configure /etc/libnss-ldap.conf

Cross-platform file and print server running GNU/Linux

October 25, 2003 Leave a comment

GNU/Linux is particularly adapted as file server for Microsoft Windows, MacOS (any version) and UNIX workstations. It is adapted to serving printers to them as well and, as every UNIX, is well-suited as application server (mail, web, DHCP, …).

Users and groups are stored on the server for centralized and common access from all the client types.

For the management of all that, we use Webmin, a web interface that allows a lot of things, even remote and shared management between several administrators.

To achieve this, some well-known free softwares are used: Samba, Netatalk, FTP (File Transfer Protocol), CUPS, NFS (or yet other means, Linux is able to use many).

Samba is a free CiFS (also known as SMB, a file and print server and client for UNIX) implementation, known as performing faster than the original Microsoft implementation. It can integrate into an existing Windows-based network, or completely replace it for every service the Windows version can provide. By integration, I mean be client or server or both at the same time.

Netatalk is a free Appletalk implementation for UNIX. It allows Mac users to access the files created by the users of the other systems.

A FTP server is sometimes the best way to transfer files betweens computers, for example from the outside of the local network. It must be secured, however, to only allow access to people who need it.

CUPS is a free implementation of the widely supported (Microsoft, HP, …) IPP (Internet Printing Protocol) which removes most of the burden associated to networked printers. It allows, with various interfaces (web, GTK+, …), to install and configure all kinds of printers. It also allows printers autodiscovery on your network.

To manage users and groups accross the network, various solutions exist, depending on the existing infrastructure. If you do not already run an NT Domain (or Active Directory) or do not want to use it, we use NIS or LDAP to store, retrieve and manage users, passwords and groups. Otherwise, Samba is also able to integrate your GNU/Linux to the existing Domain or replace it completely.

This article was first written in October 2003 for
the BeezNest technical website (http://glasnost.beeznest.org/articles/74)
%d bloggers like this: