From 2e6229ca0cf4b6706cc2b65ca048e74472b439ff Mon Sep 17 00:00:00 2001 From: Andrea Patricelli Date: Tue, 18 Jun 2024 17:27:03 +0200 Subject: [PATCH] [AD-77] adding full support for objectGUID as key --- .gitignore | 1 + .../connid/bundles/ad/crud/ADUpdate.java | 19 +- .../connid/bundles/ad/search/ADSearch.java | 4 +- .../bundles/ad/sync/USNSyncStrategy.java | 18 +- .../connid/bundles/ad/util/ADUtilities.java | 253 ++++++++++++------ .../connid/bundles/ad/util/DirSyncUtils.java | 8 +- .../bundles/ad/crud/UserCrudTestITCase.java | 34 ++- 7 files changed, 232 insertions(+), 105 deletions(-) diff --git a/.gitignore b/.gitignore index c507849..612c5bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target .idea +*.iml diff --git a/src/main/java/net/tirasa/connid/bundles/ad/crud/ADUpdate.java b/src/main/java/net/tirasa/connid/bundles/ad/crud/ADUpdate.java index e81fcc2..f15d46a 100644 --- a/src/main/java/net/tirasa/connid/bundles/ad/crud/ADUpdate.java +++ b/src/main/java/net/tirasa/connid/bundles/ad/crud/ADUpdate.java @@ -23,6 +23,7 @@ import static net.tirasa.connid.bundles.ad.util.ADUtilities.getPrimaryGroupSID; import static org.identityconnectors.common.CollectionUtil.isEmpty; import static org.identityconnectors.common.CollectionUtil.newSet; + import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -39,6 +40,7 @@ import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import net.tirasa.adsddl.ntsd.SID; +import net.tirasa.adsddl.ntsd.utils.GUID; import net.tirasa.adsddl.ntsd.utils.Hex; import net.tirasa.adsddl.ntsd.utils.NumberFacility; import net.tirasa.connid.bundles.ad.ADConfiguration; @@ -166,7 +168,18 @@ public Uid update(final Set attrs) { modifyMemberships(entryDN, attrsToBeUpdated); modifyPrimaryGroupID(entryDN, attrsToBeUpdated); - return conn.getSchema().createUid(oclass, entryDN); + if (OBJECTGUID.equals(conn.getSchema().getLdapUidAttribute(oclass))) { + final Attributes profile; + try { + profile = conn.getInitialContext().getAttributes(entryDN, new String[] { OBJECTGUID }); + return new Uid(GUID.getGuidAsString((byte[]) profile.get(OBJECTGUID).get())); + } catch (NamingException e) { + LOG.error("Error managing objectGUID after update", e); + throw new ConnectorException("Error managing objectGUID after update", e); + } + } else { + return conn.getSchema().createUid(oclass, entryDN); + } } @Override @@ -231,8 +244,8 @@ protected Pair getAttributesToModify( } else if (attr.is(ADConfiguration.UCCP_FLAG)) { final List value = attr.getValue(); if (value != null && !value.isEmpty()) { - javax.naming.directory.Attribute ntSecurityDescriptor - = utils.userCannotChangePassword(obj, (Boolean) value.get(0)); + javax.naming.directory.Attribute ntSecurityDescriptor = + utils.userCannotChangePassword(obj, (Boolean) value.get(0)); if (ntSecurityDescriptor != null) { ldapAttrs.put(ntSecurityDescriptor); } diff --git a/src/main/java/net/tirasa/connid/bundles/ad/search/ADSearch.java b/src/main/java/net/tirasa/connid/bundles/ad/search/ADSearch.java index f6e7533..dd3ff3e 100644 --- a/src/main/java/net/tirasa/connid/bundles/ad/search/ADSearch.java +++ b/src/main/java/net/tirasa/connid/bundles/ad/search/ADSearch.java @@ -149,7 +149,7 @@ protected LdapInternalSearch getInternalSearch(final Set attrsToGet) { final SearchControls controls = LdapInternalSearch.createDefaultSearchControls(); final Set ldapAttrsToGet = utils.getLdapAttributesToGet(attrsToGet, oclass); - controls.setReturningAttributes(ldapAttrsToGet.toArray(new String[ldapAttrsToGet.size()])); + controls.setReturningAttributes(ldapAttrsToGet.toArray(new String[0])); controls.setSearchScope(searchScope); final String optionsFilter = LdapConstants.getSearchFilter(options); @@ -177,7 +177,7 @@ protected LdapInternalSearch getInternalSearch(final Set attrsToGet) { } final String membershipSearchFilter = oclass.equals(ObjectClass.ACCOUNT) - ? utils.getMembershipSearchFilter(((ADConfiguration) conn.getConfiguration())) + ? ADUtilities.getMembershipSearchFilter(((ADConfiguration) conn.getConfiguration())) : null; if (LOG.isOk()) { diff --git a/src/main/java/net/tirasa/connid/bundles/ad/sync/USNSyncStrategy.java b/src/main/java/net/tirasa/connid/bundles/ad/sync/USNSyncStrategy.java index 280aaec..df184ea 100644 --- a/src/main/java/net/tirasa/connid/bundles/ad/sync/USNSyncStrategy.java +++ b/src/main/java/net/tirasa/connid/bundles/ad/sync/USNSyncStrategy.java @@ -60,7 +60,7 @@ public class USNSyncStrategy extends ADSyncStrategy { private static final Log LOG = Log.getLog(USNSyncStrategy.class); - private static String USN = "uSNChanged"; + private static final String USN = "uSNChanged"; private String deleteTokenValue; @@ -132,9 +132,13 @@ public void sync( // get lastest sync token before start pulling objects latestSyncToken = token; - if ((oclass.is(ObjectClass.ACCOUNT_NAME) && ((ADConfiguration) conn.getConfiguration()).isRetrieveDeletedUser()) - || (oclass.is(ObjectClass.GROUP_NAME) && ((ADConfiguration) conn.getConfiguration()).isRetrieveDeletedGroup()) - || (oclass.is(LdapSchema.ANY_OBJECT_NAME) && ((ADConfiguration) conn.getConfiguration()).isRetrieveDeletedAnyObject())) { + if ((oclass.is(ObjectClass.ACCOUNT_NAME) + && ((ADConfiguration) conn.getConfiguration()).isRetrieveDeletedUser()) + || (oclass.is(ObjectClass.GROUP_NAME) + && ((ADConfiguration) conn.getConfiguration()).isRetrieveDeletedGroup()) + || (oclass.is(LdapSchema.ANY_OBJECT_NAME) + && ((ADConfiguration) conn.getConfiguration()).isRetrieveDeletedAnyObject())) { + syncDeletedObjects(token, handler, options, oclass); } @@ -299,7 +303,7 @@ private void syncCurrentObjects( // ----------------------------------- String filter = oclass.is(ObjectClass.ACCOUNT_NAME) ? // get user filter - createDirSyncUFilter((ADConfiguration) conn.getConfiguration(), utils) + createDirSyncUFilter((ADConfiguration) conn.getConfiguration()) : oclass.is(ObjectClass.GROUP_NAME) // get group filter ? createDirSyncGFilter() : DirSyncUtils.createDirSyncAOFilter((ADConfiguration) conn.getConfiguration(), false); @@ -502,10 +506,10 @@ protected void handleSyncGDelta( } } - private static String createDirSyncUFilter(final ADConfiguration conf, final ADUtilities utils) { + private static String createDirSyncUFilter(final ADConfiguration conf) { StringBuilder filter = new StringBuilder(); - filter.append("(&(objectClass=user)").append(utils.getMembershipSearchFilter(conf)). + filter.append("(&(objectClass=user)").append(ADUtilities.getMembershipSearchFilter(conf)). append("(! (isDeleted=TRUE))").append(")"); return filter.toString(); diff --git a/src/main/java/net/tirasa/connid/bundles/ad/util/ADUtilities.java b/src/main/java/net/tirasa/connid/bundles/ad/util/ADUtilities.java index 46ea4d9..340cee4 100644 --- a/src/main/java/net/tirasa/connid/bundles/ad/util/ADUtilities.java +++ b/src/main/java/net/tirasa/connid/bundles/ad/util/ADUtilities.java @@ -16,6 +16,7 @@ package net.tirasa.connid.bundles.ad.util; import static net.tirasa.connid.bundles.ad.ADConfiguration.PNE_FLAG; +import static net.tirasa.connid.bundles.ad.ADConfiguration.PRIMARY_GROUP_DN_NAME; import static net.tirasa.connid.bundles.ad.ADConfiguration.UCCP_FLAG; import static net.tirasa.connid.bundles.ad.ADConnector.OBJECTGUID; import static net.tirasa.connid.bundles.ad.ADConnector.OBJECTSID; @@ -34,6 +35,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.TreeSet; import javax.naming.InvalidNameException; @@ -62,6 +64,7 @@ import net.tirasa.connid.bundles.ldap.schema.LdapSchema; import net.tirasa.connid.bundles.ldap.search.LdapFilter; import net.tirasa.connid.bundles.ldap.search.LdapInternalSearch; +import net.tirasa.connid.bundles.ldap.search.LdapSearch; import net.tirasa.connid.bundles.ldap.search.LdapSearches; import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.StringUtil; @@ -76,6 +79,7 @@ import org.identityconnectors.framework.common.objects.Name; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.ObjectClassInfo; +import org.identityconnectors.framework.common.objects.OperationOptionsBuilder; import org.identityconnectors.framework.common.objects.OperationalAttributes; import org.identityconnectors.framework.common.objects.Uid; @@ -123,7 +127,6 @@ public javax.naming.directory.Attribute getGroupID(final String dn) throws Inval } public String getPrimaryGroupDN(final LdapEntry entry, final Attributes profile) throws NamingException { - final javax.naming.directory.Attribute primaryGroupID = profile.get(PRIMARYGROUPID); final javax.naming.directory.Attribute objectSID = profile.get(OBJECTSID); @@ -175,8 +178,7 @@ public Set getAttributesToGet(final String[] attributesToGet, final Obje // ------------------------------------------------- // AD-52 (paged membership retrieving: 1k a time) // ------------------------------------------------- - final String memberships - = ADConfiguration.class.cast(connection.getConfiguration()).getGroupMemberAttribute(); + final String memberships = connection.getConfiguration().getGroupMemberAttribute(); if (oclass.is(ObjectClass.GROUP_NAME) && result.contains(memberships)) { // AD specific, for checking wether a user is enabled or not @@ -270,6 +272,22 @@ public ConnectorObject createConnectorObject( return createConnectorObject(baseDN, result.getAttributes(), attrsToGet, oclass); } + private void initConnectorObjectBuilder( + final LdapEntry entry, + final ObjectClass oclass, + final ConnectorObjectBuilder builder) throws NamingException { + + builder.setObjectClass(oclass); + + if (OBJECTGUID.equals(connection.getSchema().getLdapUidAttribute(oclass))) { + builder.setUid(GUID.getGuidAsString((byte[]) entry.getAttributes().get(OBJECTGUID).get())); + } else { + builder.setUid(connection.getSchema().createUid(oclass, entry)); + } + + builder.setName(connection.getSchema().createName(oclass, entry)); + } + public ConnectorObject createConnectorObject( final String baseDN, final Attributes profile, @@ -280,20 +298,12 @@ public ConnectorObject createConnectorObject( final LdapEntry entry = LdapEntry.create(baseDN, profile); final ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); - builder.setObjectClass(oclass); - if (OBJECTGUID.equals(connection.getSchema().getLdapUidAttribute(oclass))) { - builder.setUid(GUID.getGuidAsString((byte[]) entry.getAttributes().get(OBJECTGUID).get())); - } else { - builder.setUid(connection.getSchema().createUid(oclass, entry)); - } - - builder.setName(connection.getSchema().createName(oclass, entry)); + initConnectorObjectBuilder(entry, oclass, builder); String pgDN = null; for (String attributeName : attrsToGet) { - Attribute attribute = null; if (LdapConstants.isLdapGroups(attributeName) || attributeName.equals(ADConnector.MEMBEROF)) { @@ -306,8 +316,8 @@ public ConnectorObject createConnectorObject( } attribute = AttributeBuilder.build(attributeName, ldapGroups); } else if (LdapConstants.isPosixGroups(attributeName)) { - final Set posixRefAttrs = LdapUtil.getStringAttrValues(entry.getAttributes(), GroupHelper. - getPosixRefAttribute()); + final Set posixRefAttrs = LdapUtil.getStringAttrValues( + entry.getAttributes(), GroupHelper.getPosixRefAttribute()); final List posixGroups = groupHelper.getPosixGroups(posixRefAttrs); attribute = AttributeBuilder.build(LdapConstants.POSIX_GROUPS_NAME, posixGroups); } else if (LdapConstants.PASSWORD.is(attributeName) && oclass.is(ObjectClass.ACCOUNT_NAME)) { @@ -332,28 +342,7 @@ public ConnectorObject createConnectorObject( LOG.error(e, "While fetching " + UACCONTROL_ATTR); } } else if (UACCONTROL_ATTR.equalsIgnoreCase(attributeName) && oclass.is(ObjectClass.ACCOUNT_NAME)) { - try { - - final String status = profile.get(UACCONTROL_ATTR) == null - || profile.get(UACCONTROL_ATTR).get() == null - ? null : profile.get(UACCONTROL_ATTR).get().toString(); - - if (LOG.isOk()) { - LOG.ok("User Account Control: {0}", status); - } - - // enabled if UF_ACCOUNTDISABLE is not included (0x00002) - builder.addAttribute( - status == null || Integer.parseInt( - profile.get(UACCONTROL_ATTR).get().toString()) - % 16 != UF_ACCOUNTDISABLE - ? AttributeBuilder.buildEnabled(true) - : AttributeBuilder.buildEnabled(false)); - - attribute = connection.getSchema().createAttribute(oclass, attributeName, entry, false); - } catch (NamingException e) { - LOG.error(e, "While fetching " + UACCONTROL_ATTR); - } + attribute = manageUACAttribute(profile, oclass, entry, builder, attributeName); } else if (OBJECTGUID.equalsIgnoreCase(attributeName)) { attribute = AttributeBuilder.build( attributeName, GUID.getGuidAsString((byte[]) profile.get(OBJECTGUID).get())); @@ -364,25 +353,23 @@ public ConnectorObject createConnectorObject( UCCP_FLAG, SDDLHelper.isUserCannotChangePassword(new SDDL(((byte[]) sddl.get())))); } - } else if (ADConfiguration.PRIMARY_GROUP_DN_NAME.equalsIgnoreCase(attributeName)) { + } else if (PRIMARY_GROUP_DN_NAME.equalsIgnoreCase(attributeName)) { if (StringUtil.isBlank(pgDN)) { pgDN = getPrimaryGroupDN(entry, profile); } - attribute = AttributeBuilder.build(ADConfiguration.PRIMARY_GROUP_DN_NAME, pgDN); + attribute = AttributeBuilder.build(PRIMARY_GROUP_DN_NAME, pgDN); } else if (oclass.is(ObjectClass.GROUP_NAME) - && String.format("%s;range=%d-%d", ADConfiguration.class.cast(connection.getConfiguration()). + && String.format("%s;range=%d-%d", connection.getConfiguration(). getGroupMemberAttribute(), 0, 999).equalsIgnoreCase(attributeName)) { - // loop on membership ranges and populate member attribute - final String membAttrPrefix - = ADConfiguration.class.cast(connection.getConfiguration()). - getGroupMemberAttribute(); + // loop on membership ranges and populate member attribute + final String membAttrPrefix = connection.getConfiguration().getGroupMemberAttribute(); // search for less than 1k memberships String membAttrName = String.format("%s;range=0-*", membAttrPrefix); attribute = connection.getSchema().createAttribute(oclass, membAttrName, entry, true); - final ArrayList values = new ArrayList(attribute.getValue()); + final ArrayList values = new ArrayList<>(attribute.getValue()); if (values.isEmpty()) { // loop among ranges @@ -430,6 +417,44 @@ public ConnectorObject createConnectorObject( return builder.build(); } + /** + * This utility method is meant to build a minimal connector object without any other parsed attribute but + * userPrincipalName and objectGUID. + * This implementation is useful when searching for entries to be updated in getEntryToBeUpdated that require + * original values in the {@link org.identityconnectors.framework.common.objects.ConnectorObject} + */ + private ConnectorObject createMinimalConnectorObject( + final String baseDN, + final Attributes profile, + final Collection attrsToGet, + final ObjectClass oclass) + throws NamingException { + + final LdapEntry entry = LdapEntry.create(baseDN, profile); + + final ConnectorObjectBuilder builder = new ConnectorObjectBuilder(); + + initConnectorObjectBuilder(entry, oclass, builder); + + for (String attributeName : attrsToGet) { + Attribute attribute = null; + + if (UACCONTROL_ATTR.equalsIgnoreCase(attributeName) && oclass.is(ObjectClass.ACCOUNT_NAME)) { + attribute = manageUACAttribute(profile, oclass, entry, builder, attributeName); + } else if (OBJECTGUID.equalsIgnoreCase(attributeName)) { + attribute = AttributeBuilder.build( + attributeName, GUID.getGuidAsString((byte[]) profile.get(OBJECTGUID).get())); + } else if (profile.get(attributeName) != null) { + attribute = connection.getSchema().createAttribute(oclass, attributeName, entry, false); + } + + // Avoid attribute adding in case of attribute name not found + Optional.ofNullable(attribute).ifPresent(builder::addAttribute); + } + + return builder.build(); + } + /** * Create a DN string starting from a set attributes and a default people container. This method has to be used * if @@ -441,7 +466,6 @@ public ConnectorObject createConnectorObject( * @return distinguished name string. */ public final String getDN(final ObjectClass oclass, final Name nameAttr, final Attribute cnAttr) { - String cn; if (cnAttr == null || cnAttr.getValue() == null @@ -483,21 +507,6 @@ public static boolean isDN(final String dn) { } } - public String getMembershipSearchFilter(final ADConfiguration conf) { - final StringBuilder ufilter = new StringBuilder(); - final String[] memberships = conf.getMemberships(); - if (memberships != null && memberships.length > 0) { - ufilter.append(conf.isMembershipsInOr() ? "(|" : "(&"); - - for (String group : memberships) { - ufilter.append("(").append(ADConnector.MEMBEROF).append("=").append(group).append(")"); - } - - ufilter.append(")"); - } - return ufilter.toString(); - } - public LdapEntry getEntryToBeUpdated(final String entryDN) { LdapEntry obj = null; try { @@ -519,16 +528,41 @@ public LdapEntry getEntryToBeUpdated(final String entryDN) { return obj; } + private String getEntryToBeUpdatedQuery(final Uid uid, final ObjectClass oclass) { + return connection.getSchema().getLdapUidAttribute(oclass) + "=" + + (OBJECTGUID.equals(connection.getSchema().getLdapUidAttribute(oclass)) + ? getEscapedGUID(uid.getUidValue()) + : uid.getUidValue()); + } + public ConnectorObject getEntryToBeUpdated(final Uid uid, final ObjectClass oclass) { - final String filter = connection.getSchema().getLdapUidAttribute(oclass) + "=" + uid.getUidValue(); + OperationOptionsBuilder builder = new OperationOptionsBuilder(); + builder.setAttributesToGet(Arrays.asList(UACCONTROL_ATTR, SDDL_ATTR, OBJECTSID, PRIMARYGROUPID)); - final ConnectorObject obj = LdapSearches.findObject( - connection, oclass, - LdapFilter.forNativeFilter(filter), - UACCONTROL_ATTR, - SDDL_ATTR, - OBJECTSID, - PRIMARYGROUPID); + LdapFilter filter = LdapFilter.forNativeFilter(getEntryToBeUpdatedQuery(uid, oclass)); + + LOG.ok("Searching for object of class {0} with filter {1}", oclass.getObjectClassValue(), filter); + + final ConnectorObject obj = new LdapSearch(connection, oclass, filter, null, builder.build()) { + + @Override + protected ConnectorObject createConnectorObject(final String baseDN, + final SearchResult result, + final Set attrsToGet, + final boolean emptyAttrWhenNotFound) { + + try { + // cannot use default createConnectorObject, since payload may contain Active Directory binary + // and/or special attributes + return createMinimalConnectorObject(result.getNameInNamespace(), + result.getAttributes(), + attrsToGet, + oclass); + } catch (NamingException e) { + throw new ConnectorException("Error while creating connector object", e); + } + } + }.getSingleResult(); if (obj == null) { throw new ConnectorException("Entry not found"); @@ -537,7 +571,7 @@ public ConnectorObject getEntryToBeUpdated(final Uid uid, final ObjectClass ocla return obj; } - public Attributes getAttributes(final String entryDN, final String... attributes) { + private Attributes getAttributes(final String entryDN, final String... attributes) { try { return connection.getInitialContext().getAttributes(entryDN, attributes); } catch (NamingException e) { @@ -569,8 +603,7 @@ public javax.naming.directory.Attribute userCannotChangePassword(final Connector return userCannotChangePassword((byte[]) ntSecurityDescriptor.getValue().get(0), cannot); } - public javax.naming.directory.Attribute userCannotChangePassword(final byte[] obj, final Boolean cannot) { - + private static javax.naming.directory.Attribute userCannotChangePassword(final byte[] obj, final Boolean cannot) { if (obj == null) { return null; } @@ -579,7 +612,6 @@ public javax.naming.directory.Attribute userCannotChangePassword(final byte[] ob } public Set basicLdapSearch(final String filter, final String... baseContextDNs) { - final LdapContext ctx = connection.getInitialContext(); // ----------------------------------- @@ -590,10 +622,9 @@ public Set basicLdapSearch(final String filter, final String... ba searchCtls.setReturningAttributes(new String[0]); // ----------------------------------- - final Set result = new HashSet(); + final Set result = new HashSet<>(); for (String baseContextDn : baseContextDNs) { - if (LOG.isOk()) { LOG.ok("Searching from " + baseContextDn); } @@ -618,10 +649,9 @@ public Set getGroups(final String entryDN) { } public Set getGroups(final String entryDN, final String... baseContexts) { - final String member = ((ADConfiguration) connection.getConfiguration()). - getGroupMemberAttribute(); + final String member = connection.getConfiguration().getGroupMemberAttribute(); - final Set ldapGroups = new TreeSet(String.CASE_INSENSITIVE_ORDER); + final Set ldapGroups = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); for (SearchResult res : basicLdapSearch(filterInOr(member, entryDN), baseContexts)) { ldapGroups.add(res.getNameInNamespace()); } @@ -629,22 +659,77 @@ public Set getGroups(final String entryDN, final String... baseContexts) return ldapGroups; } - private String filterInOr(final String attr, final String... values) { + private static String getEscapedGUID(final String unescapedGUID) { + return Hex.getEscaped(GUID.getGuidAsByteArray(unescapedGUID)); + } + + private Attribute manageUACAttribute(final Attributes profile, + final ObjectClass oclass, + final LdapEntry entry, + final ConnectorObjectBuilder builder, + final String attributeName) { + + Attribute attribute = null; + try { + final String status = profile.get(UACCONTROL_ATTR) == null + || profile.get(UACCONTROL_ATTR).get() == null + ? null : profile.get(UACCONTROL_ATTR).get().toString(); + + if (LOG.isOk()) { + LOG.ok("User Account Control: {0}", status); + } + + // enabled if UF_ACCOUNTDISABLE is not included (0x00002) + builder.addAttribute( + status == null || Integer.parseInt( + profile.get(UACCONTROL_ATTR).get().toString()) + % 16 != UF_ACCOUNTDISABLE + ? AttributeBuilder.buildEnabled(true) + : AttributeBuilder.buildEnabled(false)); + + attribute = connection.getSchema().createAttribute(oclass, attributeName, entry, false); + } catch (NamingException e) { + LOG.error(e, "While fetching " + UACCONTROL_ATTR); + } + return attribute; + } + + private static String filterInOr(final String attr, final String... values) { final StringBuilder builder = new StringBuilder(); boolean multi = values != null && values.length > 1; if (multi) { builder.append("(|"); } - for (String memberValue : values) { - builder.append('('); - builder.append(attr); - builder.append('='); - escapeAttrValue(memberValue, builder); - builder.append(')'); + + if (values != null) { + for (String memberValue : values) { + builder.append('('); + builder.append(attr); + builder.append('='); + escapeAttrValue(memberValue, builder); + builder.append(')'); + } } + if (multi) { builder.append(")"); } + return builder.toString(); } + + public static String getMembershipSearchFilter(final ADConfiguration conf) { + final StringBuilder ufilter = new StringBuilder(); + final String[] memberships = conf.getMemberships(); + if (memberships != null && memberships.length > 0) { + ufilter.append(conf.isMembershipsInOr() ? "(|" : "(&"); + + for (String group : memberships) { + ufilter.append("(").append(ADConnector.MEMBEROF).append("=").append(group).append(")"); + } + + ufilter.append(")"); + } + return ufilter.toString(); + } } diff --git a/src/main/java/net/tirasa/connid/bundles/ad/util/DirSyncUtils.java b/src/main/java/net/tirasa/connid/bundles/ad/util/DirSyncUtils.java index 142bef4..e1d0c1c 100644 --- a/src/main/java/net/tirasa/connid/bundles/ad/util/DirSyncUtils.java +++ b/src/main/java/net/tirasa/connid/bundles/ad/util/DirSyncUtils.java @@ -41,7 +41,7 @@ public static String createDirSyncUFilter(final ADConfiguration conf, final ADUt mfilter.append("(objectClass=group)"); - ufilter.append(utils.getMembershipSearchFilter(conf)); + ufilter.append(ADUtilities.getMembershipSearchFilter(conf)); ufilter.insert(0, "(&(objectClass=user)").append(")"); @@ -52,7 +52,6 @@ public static String createDirSyncUFilter(final ADConfiguration conf, final ADUt } public static String createDirSyncGFilter(final ADConfiguration conf) { - final StringBuilder filter = new StringBuilder(); if (conf.isRetrieveDeletedGroup()) { @@ -73,10 +72,9 @@ public static String createDirSyncAOFilter(final ADConfiguration conf, boolean i final StringBuilder oclassFilter = new StringBuilder(); String[] anyObjectClasses = conf.getAnyObjectClasses(); if (anyObjectClasses.length > 1) { - if (conf.isFilterWithOrInsteadOfAnd()){ + if (conf.isFilterWithOrInsteadOfAnd()) { oclassFilter.append("(|"); - } - else { + } else { oclassFilter.append("(&"); } } diff --git a/src/test/java/net/tirasa/connid/bundles/ad/crud/UserCrudTestITCase.java b/src/test/java/net/tirasa/connid/bundles/ad/crud/UserCrudTestITCase.java index f659cc4..a6f95c8 100644 --- a/src/test/java/net/tirasa/connid/bundles/ad/crud/UserCrudTestITCase.java +++ b/src/test/java/net/tirasa/connid/bundles/ad/crud/UserCrudTestITCase.java @@ -484,7 +484,7 @@ public void updateDeltaNoValuePresent() { } catch (ConnectorException ignore) { // ignore } - + // updateDelta with values to add and to remove AttributeDelta delta = AttributeDeltaBuilder.build( "givenName", "gnupdate"); @@ -558,7 +558,8 @@ public void updateDelta() { assertTrue(numberAttr.contains(NUMBER3)); assertDoesNotThrow(() -> connector.authenticate(ObjectClass.ACCOUNT, ids.getValue(), newPwd, null)); - connector.removeAttributeValues(ObjectClass.ACCOUNT, new Uid(ids.getValue()), Collections.singleton(telephoneAttr), null); + connector.removeAttributeValues(ObjectClass.ACCOUNT, + new Uid(ids.getValue()), Collections.singleton(telephoneAttr), null); } @Test @@ -1216,7 +1217,7 @@ public void searchByObjectGUID() { // create options for returning attributes OperationOptionsBuilder oob = new OperationOptionsBuilder(); - oob.setAttributesToGet("sAMAccountName", ADConnector.OBJECTGUID); + oob.setAttributesToGet("sAMAccountName", ADConnector.OBJECTGUID, "givenName", "sn"); // ----------------------------------------------------- // Create new user @@ -1240,7 +1241,7 @@ public void searchByObjectGUID() { attributes.add(AttributeBuilder.build("givenName", Collections.singletonList("gntest"))); attributes.add(AttributeBuilder.build("displayName", Collections.singletonList("dntest"))); - final Uid uid = newConnector.create(ObjectClass.ACCOUNT, attributes, null); + Uid uid = newConnector.create(ObjectClass.ACCOUNT, attributes, null); assertNotNull(uid); // ----------------------------------------------------- @@ -1268,6 +1269,31 @@ public void searchByObjectGUID() { assertEquals(objectguid.getValue(), results.get(0).getUid().getValue()); // ----------------------------------------------------- + // ----------------------------------------------------- + // Update user + // ----------------------------------------------------- + List attrToReplace = Arrays.asList(new Attribute[] { + AttributeBuilder.build("givenName", "gnupdate"), + AttributeBuilder.build("sn", "snupdate"), + AttributeBuilder.buildPassword( + new GuardedString("Password321".toCharArray())) }); + + uid = newConnector.update( + ObjectClass.ACCOUNT, + uid, + new HashSet<>(attrToReplace), + null); + + assertNotNull(uid); + + final ConnectorObject object = newConnector.getObject(ObjectClass.ACCOUNT, uid, oob.build()); + + assertNotNull(object); + assertNotNull(object.getAttributes()); + assertNotNull(object.getAttributeByName("givenName")); + assertEquals(Collections.singletonList("gnupdate"), object.getAttributeByName("givenName").getValue()); + assertNotNull(object.getAttributeByName("sn")); + assertEquals(Collections.singletonList("snupdate"), object.getAttributeByName("sn").getValue()); } finally { newConnector.delete(ObjectClass.ACCOUNT, uid, null); }