From 3ae4dba8fea508083f010300deb077abb1425ebf Mon Sep 17 00:00:00 2001 From: Joern Muehlencord Date: Tue, 25 Jun 2019 13:06:53 +0200 Subject: [PATCH] added support for locked users --- .../shared/network/ldap/LDAPSearch.java | 135 +++++++++++------- 1 file changed, 86 insertions(+), 49 deletions(-) diff --git a/network/src/main/java/de/muehlencord/shared/network/ldap/LDAPSearch.java b/network/src/main/java/de/muehlencord/shared/network/ldap/LDAPSearch.java index e0fd998..3c76452 100644 --- a/network/src/main/java/de/muehlencord/shared/network/ldap/LDAPSearch.java +++ b/network/src/main/java/de/muehlencord/shared/network/ldap/LDAPSearch.java @@ -1,10 +1,17 @@ package de.muehlencord.shared.network.ldap; +import de.muehlencord.shared.util.DateUtil; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.BitSet; +import java.util.Date; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attributes; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Connection to ldap server to searh by different values @@ -13,6 +20,8 @@ import javax.naming.directory.SearchResult; */ public class LDAPSearch { + private static final Logger LOGGER = LoggerFactory.getLogger(LDAPSearch.class); + /** * the ldap connection to use */ @@ -26,22 +35,17 @@ public class LDAPSearch { * Creates a new instance of a ldap search. * *

- * Important:
If you want to use ldaps - usually port 636 make sure you - * provide a trustkeystore in case your ldap server does not use a - * certificate which can be trusted by the build in root certificates. (e.g. - * self signed certificates)

+ * Important:
If you want to use ldaps - usually port 636 make sure you provide a trustkeystore in case your ldap server does not use a certificate which can be trusted by the build in root + * certificates. (e.g. self signed certificates)

* *

- * To provide access to a trust center you can specify the following - * parameter to your application by providing the following parameter + * To provide access to a trust center you can specify the following parameter to your application by providing the following parameter *

      * -Djavax.net.ssl.trustStore=/path/to/truststore.keystore
      * 

* - * @param url the url of the ldap server to connect to like - * ldap://ldapserver.your.domain:389 - * @param searchBase the search base to use - e.g. - * DC=wincor-nixdorf,DC=com + * @param url the url of the ldap server to connect to like ldap://ldapserver.your.domain:389 + * @param searchBase the search base to use - e.g. DC=wincor-nixdorf,DC=com * @param username the username to connect with * @param password the password to connect with */ @@ -57,24 +61,19 @@ public class LDAPSearch { * Creates a new instance of a ldap search. * *

- * Important:
If you want to use ldaps - usually port 636 make sure you - * provide a trustkeystore in case your ldap server does not use a - * certificate which can be trusted by the build in root certificates. (e.g. - * self signed certificates)

+ * Important:
If you want to use ldaps - usually port 636 make sure you provide a trustkeystore in case your ldap server does not use a certificate which can be trusted by the build in root + * certificates. (e.g. self signed certificates)

* *

- * To provide access to a trust center you can specify the following - * parameter to your application by providing the following parameter + * To provide access to a trust center you can specify the following parameter to your application by providing the following parameter *

      * -Djavax.net.ssl.trustStore=/path/to/truststore.keystore
      * 

* * @param authentication the authentification type to use -e.g. "SIMPLE" - * @param url the url of the ldap server to connect to like - * ldap://ldapserver.your.domain:389 - * @param securityProtoco the security protocol to use - e.g. SIMPLE - * @param searchBase the search base to use - e.g. - * DC=wincor-nixdorf,DC=com + * @param url the url of the ldap server to connect to like ldap://ldapserver.your.domain:389 + * @param securityProtocol the security protocol to use - e.g. SIMPLE + * @param searchBase the search base to use - e.g. DC=wincor-nixdorf,DC=com * @param username the username to connect with * @param password the password to connect with */ @@ -136,7 +135,7 @@ public class LDAPSearch { // prepare search parameters String[] resultattributes = {"objectClass", "name", "givenName", "sn", "department", "co", "telephoneNumber", "sAMAccountName", "c", - "userAccountControl", "managedBy", "distinguishedName", "mail"}; + "userAccountControl", "managedBy", "distinguishedName", "mail", "lastLogon", "lastLogonTimestamp"}; String searchfilter = "(" + searchField + "=" + searchValue + ")"; SearchControls searchcontrols = new SearchControls(); String[] resultAttributes = resultattributes; @@ -168,8 +167,7 @@ public class LDAPSearch { } /** - * Returns true, if the given email address can be found in the configured - * ldap + * Returns true, if the given email address can be found in the configured ldap * * @param email the emailaddress to search for * @return true, if the email address could be found; else false @@ -180,12 +178,10 @@ public class LDAPSearch { } /** - * Returns true, if the given email address is member of the given group, - * specified by the DN + * Returns true, if the given email address is member of the given group, specified by the DN * * @param email the email to validat - * @param groupDn the group search base - all members must be found as - * "member" in this group + * @param groupDn the group search base - all members must be found as "member" in this group * @return */ public boolean isMemberOfGroup(String email, String groupDn) throws LDAPException { @@ -231,11 +227,13 @@ public class LDAPSearch { private LDAPContact createLDAPContact(Attributes attributes) throws LDAPException { LDAPContact ldapContact = new LDAPContact(); + ldapContact.setEnabled(true); + long userAccountControlNumber = 0; if (attributes.get("mail") != null) { ldapContact.setEmailaddress(attributes.get("mail").toString()); } else { - ldapContact.setEmailaddress(""); + ldapContact.setEmailaddress(""); } if (attributes.get("objectClass") != null) { @@ -298,18 +296,19 @@ public class LDAPSearch { crmname = crmname.substring(crmname.indexOf(":") + 2); ldapContact.setCrmname(crmname); } + if (attributes.get("userAccountControl") != null) { String userAccountControl = attributes.get("userAccountControl").toString(); userAccountControl = userAccountControl.substring(userAccountControl.indexOf(":") + 2); - if (userAccountControl.equals("512")) { - ldapContact.setEnabled(true); - } else { - ldapContact.setEnabled(false); - } - } else { - ldapContact.setEnabled(false); + userAccountControlNumber = Long.parseLong(userAccountControl); + BitSet bitSet = BitSet.valueOf(new long[]{userAccountControlNumber}); + // bitSet.get(1) = binary value 2 = UF_ACCOUNT_DISABLE + // if bit is set, value is 2 + // if bit is set, account is disabled + ldapContact.setEnabled(!bitSet.get(1)); } break; + case LDAPContact.TYPE_GROUP: case LDAPContact.TYPE_PUBLICFOLDER: // handle groups @@ -342,19 +341,57 @@ public class LDAPSearch { } + // some workarounds - seems to bit 2 is set too often / too early + if (!ldapContact.isEnabled()) { + + if (userAccountControlNumber == 66050) { + // locked = 2 + // normal account = 512 + // password does not expire = 65536 + // service users / mailbox groups or similar created as normal user + ldapContact.setEnabled(true); + } else if ((attributes.get("lastLogon") == null) && (attributes.get("lastLogonTimestamp") == null)) { + // users without lastLogon date - probably distribution lists etc + ldapContact.setEnabled(true); + LOGGER.info("Found locked user without lastLogon - probably a shared mailbox - asuming user is still active"); + } else if ((attributes.get("lastLogon") != null) || (attributes.get("lastLogonTimestamp") != null)) { + // check if users have been logged in recently + String lastLogonDateString; + if (attributes.get("lastLogon") == null) { + lastLogonDateString = attributes.get("lastLogonTimestamp").toString(); + } else { + lastLogonDateString = attributes.get("lastLogon").toString(); + } + + if (lastLogonDateString.contains(":")) { + lastLogonDateString = lastLogonDateString.substring(lastLogonDateString.indexOf(":") + 1).trim(); + } + + long lastLogonDate = Long.parseLong(lastLogonDateString); + if (lastLogonDate == 0) { + ldapContact.setEnabled(true); + LOGGER.info("Found locked user without lastLogon - probably a shared mailbox - asuming user is still active"); + } else { + lastLogonDate = (lastLogonDate / 10000L) - +11644473600000L; + + Date date = new Date(lastLogonDate); + LocalDateTime lastLogonDateTime = DateUtil.getDateTime(date); + + LocalDateTime referenceDate = DateUtil.getUtcDateTime(DateUtil.getCurrentTimeInUTC()); + referenceDate = referenceDate.minus(120, ChronoUnit.DAYS); + + if (lastLogonDateTime.isAfter(referenceDate)) { + ldapContact.setEnabled(true); + LOGGER.info("Found locked user who recently logged in - asuming user is still active"); + } + } + } + } + + if (!ldapContact.isEnabled()) { + LOGGER.info("User {} is locked", ldapContact.getEmailaddress()); + } + return ldapContact; } } - -/** - * History: - * - * $$Log: src/main/java/com/wincornixdorf/shared/network/ldap/LDAPSearch.java $ - * Revision 1.1 2013/12/16 16:42:52MEZ Muehlencord, Joern (joern.muehlencord) - * Initial revision Member added to project - * m:/MKS/ESP_Tools/shared/shared-network/shared-network.pj $Revision 1.6 - * 2013/09/05 07:14:33 jomu $fixed ldap search if group setup is not complete. - * (1147451) $ $Revision 1.5 2013/09/04 15:07:26 jomu $fixed ldap search if - * group setup is not complete. (1147451) $$ - * - */