Skip to content

Commit

Permalink
[AD-77] adding full support for objectGUID
Browse files Browse the repository at this point in the history
  • Loading branch information
andrea-patricelli committed Jun 19, 2024
1 parent 6448655 commit 41880b3
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 48 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target
.idea
*.iml
14 changes: 13 additions & 1 deletion src/main/java/net/tirasa/connid/bundles/ad/crud/ADUpdate.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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;
Expand Down Expand Up @@ -177,7 +178,18 @@ public Uid update(final Set<Attribute> attrs) {
modifyMemberships(entryDN, attrsToBeUpdated);
modifyPrimaryGroupID(entryDN, attrsToBeUpdated);

return conn.getSchemaMapping().createUid(oclass, entryDN);
if (OBJECTGUID.equals(conn.getSchemaMapping().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.getSchemaMapping().createUid(oclass, entryDN);
}
}

public Uid addAttributeValues(final Set<Attribute> attrs) {
Expand Down
30 changes: 20 additions & 10 deletions src/main/java/net/tirasa/connid/bundles/ad/search/ADSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import javax.naming.InvalidNameException;
import javax.naming.NamingException;
Expand All @@ -44,6 +45,8 @@
import net.tirasa.connid.bundles.ldap.search.LdapSearchStrategy;
import net.tirasa.connid.bundles.ldap.search.LdapSearches;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.QualifiedUid;
Expand Down Expand Up @@ -102,21 +105,28 @@ public ADSearch(
}

public final void executeADQuery(final ResultsHandler handler) {
executeADQuery(handler, (result, attrsToGet) -> {
try {
return utils.createConnectorObject(result.getNameInNamespace(),
result,
attrsToGet,
oclass);
} catch (NamingException e) {
throw new ConnectorException(e);
}
});
}

public final void executeADQuery(final ResultsHandler handler,
final BiFunction<SearchResult, Set<String>, ConnectorObject> connObjSupplier) {
final String[] attrsToGetOption = options.getAttributesToGet();
final Set<String> attrsToGet = utils.getAttributesToGet(attrsToGetOption, oclass);

final LdapInternalSearch search = getInternalSearch(attrsToGet);

search.execute(new LdapSearchResultsHandler() {
getInternalSearch(attrsToGet).execute(new LdapSearchResultsHandler() {

@Override
public boolean handle(final String baseDN, final SearchResult result)
throws NamingException {
return handler.handle(utils.createConnectorObject(
result.getNameInNamespace(),
result,
attrsToGet,
oclass));
public boolean handle(final String baseDN, final SearchResult result) {
return handler.handle(connObjSupplier.apply(result, attrsToGet));
}
});
}
Expand Down
156 changes: 121 additions & 35 deletions src/main/java/net/tirasa/connid/bundles/ad/util/ADUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,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;
Expand All @@ -54,6 +55,7 @@
import net.tirasa.connid.bundles.ad.ADConfiguration;
import net.tirasa.connid.bundles.ad.ADConnection;
import net.tirasa.connid.bundles.ad.ADConnector;
import net.tirasa.connid.bundles.ad.search.ADSearch;
import net.tirasa.connid.bundles.ldap.LdapConnection;
import net.tirasa.connid.bundles.ldap.commons.GroupHelper;
import net.tirasa.connid.bundles.ldap.commons.LdapConstants;
Expand All @@ -76,7 +78,9 @@
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.ResultsHandler;
import org.identityconnectors.framework.common.objects.Uid;

public class ADUtilities {
Expand Down Expand Up @@ -270,7 +274,13 @@ public ConnectorObject createConnectorObject(
return createConnectorObject(baseDN, result.getAttributes(), attrsToGet, oclass);
}

public ConnectorObject createConnectorObject(
/**
* 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<String> attrsToGet,
Expand All @@ -280,15 +290,40 @@ public ConnectorObject createConnectorObject(
final LdapEntry entry = LdapEntry.create(baseDN, profile);

final ConnectorObjectBuilder builder = new ConnectorObjectBuilder();
builder.setObjectClass(oclass);

if (OBJECTGUID.equals(connection.getSchemaMapping().getLdapUidAttribute(oclass))) {
builder.setUid(GUID.getGuidAsString((byte[]) entry.getAttributes().get(OBJECTGUID).get()));
} else {
builder.setUid(connection.getSchemaMapping().createUid(oclass, entry));
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.getSchemaMapping().createAttribute(oclass, attributeName, entry, false);
}

// Avoid attribute adding in case of attribute name not found
Optional.ofNullable(attribute).ifPresent(builder::addAttribute);
}

builder.setName(connection.getSchemaMapping().createName(oclass, entry));
return builder.build();
}

public ConnectorObject createConnectorObject(
final String baseDN,
final Attributes profile,
final Collection<String> attrsToGet,
final ObjectClass oclass)
throws NamingException {

final LdapEntry entry = LdapEntry.create(baseDN, profile);

final ConnectorObjectBuilder builder = new ConnectorObjectBuilder();

initConnectorObjectBuilder(entry, oclass, builder);

String pgDN = null;

Expand Down Expand Up @@ -332,28 +367,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.getSchemaMapping().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()));
Expand Down Expand Up @@ -516,15 +530,40 @@ public LdapEntry getEntryToBeUpdated(final String entryDN) {
}

public ConnectorObject getEntryToBeUpdated(final Uid uid, final ObjectClass oclass) {
OperationOptionsBuilder builder = new OperationOptionsBuilder();
builder.setAttributesToGet(Arrays.asList(UACCONTROL_ATTR, SDDL_ATTR, OBJECTSID, PRIMARYGROUPID));

final String filter = connection.getSchemaMapping().getLdapUidAttribute(oclass) + "=" + uid.getUidValue();

final ConnectorObject obj = LdapSearches.findObject(
connection, oclass,
LOG.ok("Searching for object of class {0} with filter {1}", oclass.getObjectClassValue(), filter);

final ConnectorObject[] results = new ConnectorObject[] { null };
ResultsHandler handler = new ResultsHandler() {

@Override
public boolean handle(final ConnectorObject connectorObject) {
results[0] = connectorObject;
return false;
}
};
new ADSearch(connection,
oclass,
LdapFilter.forNativeFilter(filter),
UACCONTROL_ATTR,
SDDL_ATTR,
OBJECTSID,
PRIMARYGROUPID);
handler,
builder.build()).executeADQuery(
handler,
(result, attrsToGet) -> {
try {
return createMinimalConnectorObject(result.getNameInNamespace(),
result.getAttributes(),
attrsToGet,
oclass);
} catch (NamingException e) {
throw new ConnectorException(e);
}
});

ConnectorObject obj = results[0];

if (obj == null) {
throw new ConnectorException("Entry not found");
Expand Down Expand Up @@ -643,4 +682,51 @@ private String filterInOr(final String attr, final String... values) {
}
return builder.toString();
}

private void initConnectorObjectBuilder(
final LdapEntry entry,
final ObjectClass oclass,
final ConnectorObjectBuilder builder) throws NamingException {

builder.setObjectClass(oclass);

if (OBJECTGUID.equals(connection.getSchemaMapping().getLdapUidAttribute(oclass))) {
builder.setUid(GUID.getGuidAsString((byte[]) entry.getAttributes().get(OBJECTGUID).get()));
} else {
builder.setUid(connection.getSchemaMapping().createUid(oclass, entry));
}

builder.setName(connection.getSchemaMapping().createName(oclass, entry));
}

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.getSchemaMapping().createAttribute(oclass, attributeName, entry, false);
} catch (NamingException e) {
LOG.error(e, "While fetching " + UACCONTROL_ATTR);
}
return attribute;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,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
Expand All @@ -1127,7 +1127,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);
// -----------------------------------------------------

Expand Down Expand Up @@ -1155,6 +1155,33 @@ public void searchByObjectGUID() {
assertEquals(objectguid.getValue(), results.get(0).getUid().getValue());

// -----------------------------------------------------

// -----------------------------------------------------
// -----------------------------------------------------
// Update user
// -----------------------------------------------------
List<Attribute> 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);
}
Expand Down

0 comments on commit 41880b3

Please sign in to comment.