diff --git a/CHANGELOG.md b/CHANGELOG.md index 41aaaa30..3f69b4a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## 21.0-SNAPSHOT - unreleased +### 🎁 New Features + +* LdapService `searchOne` and `searchMany` methods have been made public. +* LdapPerson class now includes `displayName`, `givenname`, and `sn` fields. + +### 🐞 Bug Fixes + +* LdapPerson class `email` key changed to `mail` to match LDAP attribute. + ## 20.0.2 - 2024-06-05 ### 🐞 Bug Fixes diff --git a/grails-app/services/io/xh/hoist/ldap/LdapService.groovy b/grails-app/services/io/xh/hoist/ldap/LdapService.groovy index 2e467951..b0bf26b9 100644 --- a/grails-app/services/io/xh/hoist/ldap/LdapService.groovy +++ b/grails-app/services/io/xh/hoist/ldap/LdapService.groovy @@ -84,20 +84,14 @@ class LdapService extends BaseService { .collectEntries { [it.key, it.value.get()]} } - - //---------------------- - // Implementation - //---------------------- - private LdapGroup lookupGroupInternal(String dn, boolean strictMode) { - searchOne("(distinguishedName=$dn)", LdapGroup, strictMode) - } - - private List lookupGroupMembersInternal(String dn, boolean strictMode) { - // See class-level comment regarding this AD-specific query - searchMany("(|(member0f=$dn) (memberOf:1.2.840.113556.1.4.1941:=$dn))", LdapPerson, strictMode) - } - - private T searchOne(String baseFilter, Class objType, boolean strictMode) { + /** + * Search for a single object, returning the first match found. + * @param baseFilter - an LDAP filter to be appended to the objectCategory filter. + * @param objType - type of Hoist-Core LdapObject to search for - must be or extend LdapObject, LdapPerson, or LdapGroup + * @param strictMode - if true, this method will throw if any lookups fail + * @return first match found in the form of objType + */ + T searchOne(String baseFilter, Class objType, boolean strictMode) { for (server in config.servers) { List matches = doQuery(server, baseFilter, objType, strictMode) if (matches) return matches.first() @@ -105,7 +99,14 @@ class LdapService extends BaseService { return null } - private List searchMany(String baseFilter, Class objType, boolean strictMode) { + /** + * Search for multiple objects, returning all matches found. + * @param baseFilter - an LDAP filter to be appended to the objectCategory filter. + * @param objType - type of Hoist-Core LdapObject to search for - must be or extend LdapObject, LdapPerson, or LdapGroup + * @param strictMode - if true, this method will throw if any lookups fail + * @return list of all matches found in the form of objType + */ + List searchMany(String baseFilter, Class objType, boolean strictMode) { List ret = [] for (server in config.servers) { List matches = doQuery(server, baseFilter, objType, strictMode) @@ -114,10 +115,22 @@ class LdapService extends BaseService { return ret } + //---------------------- + // Implementation + //---------------------- + private LdapGroup lookupGroupInternal(String dn, boolean strictMode) { + searchOne("(distinguishedName=$dn)", LdapGroup, strictMode) + } + + private List lookupGroupMembersInternal(String dn, boolean strictMode) { + // See class-level comment regarding this AD-specific query + searchMany("(|(member0f=$dn) (memberOf:1.2.840.113556.1.4.1941:=$dn))", LdapPerson, strictMode) + } + private List doQuery(Map server, String baseFilter, Class objType, boolean strictMode) { if (!enabled) throw new RuntimeException('LdapService is not enabled.') - boolean isPerson = objType == LdapPerson + boolean isPerson = LdapPerson.class.isAssignableFrom(objType) String host = server.host, filter = "(&(objectCategory=${isPerson ? 'Person' : 'Group'})$baseFilter)", key = server.toString() + filter diff --git a/src/main/groovy/io/xh/hoist/ldap/LdapPerson.groovy b/src/main/groovy/io/xh/hoist/ldap/LdapPerson.groovy index d466abb0..04f56da5 100644 --- a/src/main/groovy/io/xh/hoist/ldap/LdapPerson.groovy +++ b/src/main/groovy/io/xh/hoist/ldap/LdapPerson.groovy @@ -2,10 +2,20 @@ package io.xh.hoist.ldap import javax.management.Attribute -class LdapPerson extends LdapObject{ +/** + * Minimal set of attributes returned by a search for objectCategory=Person in Microsoft's AD LDAP implementation. + * If you need more attributes, you can extend this class and add them. + * + * Available attributes can be found here: + * https://learn.microsoft.com/en-us/archive/technet-wiki/12037.active-directory-get-aduser-default-and-extended-properties + */ +class LdapPerson extends LdapObject { String name - String email + String displayname + String givenname + String sn + String mail static LdapPerson create(Collection atts) { def ret = new LdapPerson() @@ -14,6 +24,6 @@ class LdapPerson extends LdapObject{ } static List getKeys() { - LdapObject.keys + ['name', 'email'] + LdapObject.keys + ['name', 'displayname', 'givenname', 'sn', 'mail'] } }