Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AD-77] adding full support for objectGUID #27

Merged
merged 1 commit into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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