ldap-client

Pure Haskell LDAP client library

Installation

It should be possible to install ldap-client from Hackage:

$ cabal install ldap-client

or git:

$ git clone https://github.com/supki/ldap-client
$ cabal install ldap-client/ldap-client.cabal

if you use GHC 7.6.1 or a newer version.

Example

To get a feeling of ldap-client we will try and log in into LDAP and change our password.

Before all that, though, here's the obligatory module header describing the language extensions and imports used later, so that you can follow along:

{-# LANGUAGE OverloadedStrings #-}
module Example where

import Ldap.Client as Ldap

LDAP Authentication

The typical LDAP authentication routine has three steps:

  1. Try and bind with the manager's DN and password. Manager here means any account that is able to look up information in the Directory.
  2. Find the DN of the user using manager's powers.
  3. Try and bind with the user's newly found DN and their password.

We will encapsulate the first two steps in a separate function as it's a fairly self-contained operation:

findUser :: Ldap -> (Dn, Password) -> AttrValue -> IO (Maybe Dn)
findUser l (managerDn, managerPassword) userName = do
  Ldap.bind l managerDn managerPassword
  users <- Ldap.search l (Dn "dc=com,dc=example")
                         (typesOnly True)
                         (Attr "uid" := userName)
                         []
  case users of
    SearchEntry userDn _ : _ -> return (Just userDn)
    _ -> return Nothing

The third step uses the Dn we've got from findUser; we will pass it through since it will be useful to changePassword.

login :: Ldap -> (Dn, Password) -> (AttrValue, Password) -> IO Dn
login l (managerDn, managerPassword) (userName, userPassword) = do
  maybeUserDn <- findUser l (managerDn, managerPassword) userName
  case maybeUserDn of
    Nothing     -> error "User not found!"
    Just userDn -> do Ldap.bind l userDn userPassword
                      return userDn

Changing user's password

Because we have been already bound as userName, we can simply modify the relevant attribute in the Directory to change the password.

changePassword :: Ldap -> Dn -> AttrValue -> IO ()
changePassword l userDn userNewPassword =
  Ldap.modify l userDn [Replace (Attr "userPassword") [userNewPassword]]

Finishing up

The only remaining task is to wrap login and changePassword in Ldap.with.

loginAndChangePassword
  :: (Dn, Password)
  -> (AttrValue, Password, AttrValue)
  -> IO (Either LdapError ())
loginAndChangePassword (managerDn, managerPassword)
                       (userName, userPassword, userNewPassword) =
  Ldap.with (Secure "ldap.example.com") 636 $ \l -> do
    dn <- login l (managerDn, managerPassword) (userName, userPassword)
    changePassword l dn userNewPassword