Commit 6992f2c8 authored by Sven Feyerabend's avatar Sven Feyerabend
Browse files

Allow ldap bind with authenticating user

parent 2688db1d
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ The inital idea for this implementation was taken from


### Limitations:
NEW: This version does use a separate ldap bind user, but just to find the proper BIND DN and record for the provided email, so it is possible that users from different groups / OUs can login.
NEW: This version provides the possibility to use a separate ldap bind user. It does this just to find the proper BIND DN and record for the provided email, so it is possible that users from different groups / OUs can login.
Afterwards it tries to bind to the ldap (using ldapts) with the user DN and credentials of the user which tries to login. No hassle of password hashing for LDAP pwds!

Only valid LDAP users or email users registered by an admin can login. 
@@ -77,16 +77,23 @@ Edit [docker-compose.treafik.yml](docker-compose.traefik.yml) or [docker-compose
```
LDAP_SERVER: ldaps://LDAPSERVER:636
LDAP_BASE: dc=DOMAIN,dc=TLD
# If LDAP_BINDDN is set, the ldap bind happens directly by using the provided DN
# All occurrences of `%u` get replaced by the entered uid.
# All occurrences of `%m`get replaced by the entered mail.
LDAP_BINDDN: uid=%u,ou=people,dc=DOMAIN,dc=TLD
LDAP_BIND_USER: cn=ldap_reader,dc=DOMAIN,dc=TLS
LDAP_BIND_PW: TopSecret
# users need to match this filter to login.
# All occurrences of `%u` get replaced by the entered uid.
# All occurrences of `%m`get replaced by the entered mail.
LDAP_USER_FILTER: '(&(memberof=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)(uid=%u))'

# If user is in ADMIN_GROUP on user creation (first login) isAdmin is set to true. 
# Admin Users can invite external (non ldap) users. This feature makes only sense 
# when ALLOW_EMAIL_LOGIN is set to 'true'. Additionally admins can send 
# system wide messages.
# All occurrences of `%u` get replaced by the entered uid.
# All occurrences of `%m`get replaced by the entered mail.
#LDAP_ADMIN_GROUP_FILTER: '(memberof=cn=ADMINGROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
ALLOW_EMAIL_LOGIN: 'false'

+36 −20
Original line number Diff line number Diff line
@@ -269,28 +269,40 @@ const AuthenticationManager = {
    const client = new Client({
      url: process.env.LDAP_SERVER,
    });
    //const bindDn = process.env.LDAP_BIND_USER
    //const bindPassword = process.env.LDAP_BIND_PW

    const ldap_reader = process.env.LDAP_BIND_USER
    const ldap_reader_pass = process.env.LDAP_BIND_PW
    const ldap_base = process.env.LDAP_BASE
    var uid = query.email
    const replacer = new RegExp("%u", "g")
    const filterstr = process.env.LDAP_USER_FILTER.replace(replacer, ldapEscape.filter`${uid}`) //replace all appearances
    console.log("filterstr:" + filterstr)
    var userDn = "" //ldapEscape.filter`uid=${uid}` + ',' + ldap_bd;
    var mail = ""

    var mail = query.email
    var uid = query.email.split('@')[0]
    var firstname = ""
    var lastname = ""
    var isAdmin = false
    var userDn = ""

    //replace all appearences of %u with uid and all %m with mail:
    const replacerUid = new RegExp("%u", "g")
    const replacerMail = new RegExp("%m","g")
    const filterstr = process.env.LDAP_USER_FILTER.replace(replacerUid, ldapEscape.filter`${uid}`).replace(replacerMail, ldapEscape.filter`${mail}`) //replace all appearances

    // check bind
    try {
      if(process.env.LDAP_BINDDN){ //try to bind directly with the user trying to log in
        userDn = process.env.LDAP_BINDDN.replace(replacerUid,ldapEscape.filter`${uid}`).replace(replacerMail, ldapEscape.filter`${mail}`);
        await client.bind(userDn,password);
      }else{// use fixed bind user
        await client.bind(ldap_reader, ldap_reader_pass);
      //await client.bind(userDn,password);
      }
    } catch (ex) {
      if(process.env.LDAP_BINDDN){
        console.log("Could not bind user: " + userDn);
      }else{
        console.log("Could not bind LDAP reader: " + ldap_reader + " err: " + String(ex))
      }
      return callback(null, null)
    }

    // get user data
    try {
      const {searchEntries, searchRef,} = await client.search(ldap_base, {
@@ -304,7 +316,9 @@ const AuthenticationManager = {
        uid = searchEntries[0].uid
        firstname = searchEntries[0].givenName
        lastname = searchEntries[0].sn
        if(!process.env.LDAP_BINDDN){ //dn is already correctly assembled
        userDn = searchEntries[0].dn
        }
        console.log("Found user: " + mail + " Name: " + firstname + " " + lastname + " DN: " + userDn)
      }
    } catch (ex) {
@@ -317,7 +331,7 @@ const AuthenticationManager = {
      // if admin filter is set - only set admin for user in ldap group
      // does not matter - admin is deactivated: managed through ldap
      if (process.env.LDAP_ADMIN_GROUP_FILTER) {
        const adminfilter = process.env.LDAP_ADMIN_GROUP_FILTER.replace(replacer, ldapEscape.filter`${uid}`)
        const adminfilter = process.env.LDAP_ADMIN_GROUP_FILTER.replace(replacerUid, ldapEscape.filter`${uid}`).replace(replacerMail, ldapEscape.filter`${mail}`)
        adminEntry = await client.search(ldap_base, {
          scope: 'sub',
          filter: adminfilter,
@@ -339,6 +353,8 @@ const AuthenticationManager = {
      console.log("Mail / userDn not set - exit. This should not happen - please set mail-entry in ldap.")
      return callback(null, null)
    }

    if(!process.env.BINDDN){//since we used a fixed bind user to obtain the correct userDn we need to bind again to authenticate
      try {
        await client.bind(userDn, password);
      } catch (ex) {
@@ -347,7 +363,7 @@ const AuthenticationManager = {
      } finally{
        await client.unbind()
      }

    }
    //console.log("Logging in user: " + mail + " Name: " + firstname + " " + lastname + " isAdmin: " + String(isAdmin))
    // we are authenticated now let's set the query to the correct mail from ldap
    query.email = mail