Skip to content

Commit

Permalink
[AD-78] managed password not required flag on create and update (#29) (
Browse files Browse the repository at this point in the history
  • Loading branch information
andrea-patricelli authored Oct 9, 2024
1 parent 1a127c6 commit c877b29
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 26 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@
</bind>
</volumes>
<wait>
<log>samba version 4.19.5 started</log>
<log>samba version ((\d)+.(\d)+.(\d)+) started</log>
<time>60000</time>
</wait>
</run>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public final class ADConfiguration extends LdapConfiguration {
public static final String UCCP_FLAG = "userCannotChangePassword";

public static final String PNE_FLAG = "passwordNeverExpires";

public static final String PNR_FLAG = "passwordNotRequired";

public static final String CN_NAME = "CN";

Expand Down
16 changes: 16 additions & 0 deletions src/main/java/net/tirasa/connid/bundles/ad/crud/ADCreate.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ protected Uid executeImpl() throws NamingException {

Boolean uccp = null;
Boolean pne = null;
Boolean pnr = null;
Boolean status = null;

for (Attribute attr : attrs) {
Expand All @@ -148,6 +149,11 @@ protected Uid executeImpl() throws NamingException {
if (value != null && !value.isEmpty()) {
pne = (Boolean) value.get(0);
}
} else if (attr.is(ADConfiguration.PNR_FLAG)) {
final List<Object> value = attr.getValue();
if (value != null && !value.isEmpty()) {
pnr = (Boolean) value.get(0);
}
} else if (attr.is(ADConfiguration.PRIMARY_GROUP_DN_NAME)) {
final List<Object> value = attr.getValue();
primaryGroupDN = value == null || value.isEmpty() ? null : String.class.cast(value.get(0));
Expand Down Expand Up @@ -204,6 +210,16 @@ protected Uid executeImpl() throws NamingException {
}
}

if (pnr != null) {
if ((uacValue & ADConnector.UF_PASSWD_NOTREQD) == ADConnector.UF_PASSWD_NOTREQD && !pnr) {
uacValue -= ADConnector.UF_PASSWD_NOTREQD;
} else if ((uacValue & ADConnector.UF_PASSWD_NOTREQD) != ADConnector.UF_PASSWD_NOTREQD && pnr) {
uacValue = uacValue == -1
? ADConnector.UF_PASSWD_NOTREQD
: uacValue + ADConnector.UF_PASSWD_NOTREQD;
}
}

if (status != null) {
if ((uacValue & UF_ACCOUNTDISABLE) == UF_ACCOUNTDISABLE && status) {
uacValue -= UF_ACCOUNTDISABLE;
Expand Down
20 changes: 18 additions & 2 deletions src/main/java/net/tirasa/connid/bundles/ad/crud/ADUpdate.java
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ protected Pair<Attributes, GuardedPasswordAttribute> getAttributesToModify(
int newUACValue = -1;

Boolean pne = null;
Boolean pnr = null;
Boolean status = null;

for (Attribute attr : attrs) {
Expand Down Expand Up @@ -255,6 +256,11 @@ protected Pair<Attributes, GuardedPasswordAttribute> getAttributesToModify(
if (value != null && !value.isEmpty()) {
pne = (Boolean) value.get(0);
}
} else if (attr.is(ADConfiguration.PNR_FLAG)) {
final List<Object> value = attr.getValue();
if (value != null && !value.isEmpty()) {
pnr = (Boolean) value.get(0);
}
} else if (attr.is(ADConfiguration.PROMPT_USER_FLAG)) {
final List<Object> value = attr.getValue();
if (value != null && !value.isEmpty()) {
Expand Down Expand Up @@ -307,11 +313,21 @@ protected Pair<Attributes, GuardedPasswordAttribute> getAttributesToModify(
}
}

if (pnr != null) {
if ((currentUACValue & ADConnector.UF_PASSWD_NOTREQD)
== ADConnector.UF_PASSWD_NOTREQD && !pnr) {
newUACValue = (newUACValue == -1 ? currentUACValue : newUACValue) - ADConnector.UF_PASSWD_NOTREQD;
} else if ((currentUACValue & ADConnector.UF_PASSWD_NOTREQD)
!= ADConnector.UF_PASSWD_NOTREQD && pnr) {
newUACValue = (newUACValue == -1 ? currentUACValue : newUACValue) + ADConnector.UF_PASSWD_NOTREQD;
}
}

if (status != null) {
if ((currentUACValue & UF_ACCOUNTDISABLE) == UF_ACCOUNTDISABLE && status) {
newUACValue = currentUACValue - UF_ACCOUNTDISABLE;
newUACValue = (newUACValue == -1 ? currentUACValue : newUACValue) - UF_ACCOUNTDISABLE;
} else if ((currentUACValue & UF_ACCOUNTDISABLE) != UF_ACCOUNTDISABLE && !status) {
newUACValue = currentUACValue + UF_ACCOUNTDISABLE;
newUACValue = (newUACValue == -1 ? currentUACValue : newUACValue) + UF_ACCOUNTDISABLE;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ private void build(final String oname, final SchemaBuilder schemaBld) {
objClassBld.addAttributeInfo(AttributeInfoBuilder.build(ADConfiguration.LOCK_OUT_FLAG, Boolean.class));
objClassBld.addAttributeInfo(AttributeInfoBuilder.build(ADConfiguration.PROMPT_USER_FLAG, Boolean.class));
objClassBld.addAttributeInfo(AttributeInfoBuilder.build(ADConfiguration.PNE_FLAG, Boolean.class));
objClassBld.addAttributeInfo(AttributeInfoBuilder.build(ADConfiguration.PNR_FLAG, Boolean.class));
objClassBld.addAttributeInfo(AttributeInfoBuilder.build(ADConfiguration.PRIMARY_GROUP_DN_NAME, String.class));

final ObjectClassInfo oci = objClassBld.build();
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/net/tirasa/connid/bundles/ad/util/ADUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.PNR_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;
Expand All @@ -26,6 +27,7 @@
import static net.tirasa.connid.bundles.ad.ADConnector.UF_ACCOUNTDISABLE;
import static net.tirasa.connid.bundles.ad.ADConnector.ADDS2012_ATTRIBUTES_TO_BE_REMOVED;
import static net.tirasa.connid.bundles.ad.ADConnector.UF_DONT_EXPIRE_PASSWD;
import static net.tirasa.connid.bundles.ad.ADConnector.UF_PASSWD_NOTREQD;
import static net.tirasa.connid.bundles.ldap.commons.LdapUtil.escapeAttrValue;
import static org.identityconnectors.common.CollectionUtil.newCaseInsensitiveSet;
import static org.identityconnectors.common.CollectionUtil.newSet;
Expand Down Expand Up @@ -341,6 +343,23 @@ public ConnectorObject createConnectorObject(
} catch (NamingException e) {
LOG.error(e, "While fetching " + UACCONTROL_ATTR);
}
} else if (PNR_FLAG.equalsIgnoreCase(attributeName) && oclass.is(ObjectClass.ACCOUNT_NAME)) {
try {

final String uac =
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}", uac);
}

// disabled if PNR_FLAG is not included (0x0020)
attribute = uac == null || (Integer.parseInt(uac) & UF_PASSWD_NOTREQD) != UF_PASSWD_NOTREQD
? AttributeBuilder.build(PNR_FLAG, false) : AttributeBuilder.build(PNR_FLAG, true);
} catch (NamingException e) {
LOG.error(e, "While fetching " + UACCONTROL_ATTR);
}
} else if (UACCONTROL_ATTR.equalsIgnoreCase(attributeName) && oclass.is(ObjectClass.ACCOUNT_NAME)) {
attribute = manageUACAttribute(profile, oclass, entry, builder, attributeName);
} else if (OBJECTGUID.equalsIgnoreCase(attributeName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public void schema() {

boolean sddl = false;
boolean pne = false;
boolean pnr = false;
boolean givenname = false;

for (AttributeInfo attrInfo : info.getAttributeInfo()) {
Expand All @@ -59,13 +60,18 @@ public void schema() {
pne = true;
assertEquals(Boolean.class, attrInfo.getType());
}

if (ADConfiguration.PNR_FLAG.equals(attrInfo.getName())) {
pnr = true;
assertEquals(Boolean.class, attrInfo.getType());
}

if ("givenName".equalsIgnoreCase(attrInfo.getName())) {
givenname = true;
assertEquals(String.class, attrInfo.getType());
}
}

assertTrue(sddl && givenname && pne);
assertTrue(sddl && givenname && pne && pnr);
}
}
161 changes: 139 additions & 22 deletions src/test/java/net/tirasa/connid/bundles/ad/crud/UserCrudTestITCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ public void create() {
assertNull(connector.getObject(ObjectClass.ACCOUNT, new Uid(ids.getValue()), null));

final Set<Attribute> attributes = util.getSimpleProfile(ids);
attributes.add(AttributeBuilder.build(ADConfiguration.PNE_FLAG, true));
attributes.add(AttributeBuilder.build(ADConfiguration.PNR_FLAG, true));

final Uid uid = connector.create(ObjectClass.ACCOUNT, attributes, null);
assertNotNull(uid);
Expand Down Expand Up @@ -1768,40 +1770,155 @@ public void issueAD58WithoutDN() {
assertNull(connector.getObject(ObjectClass.ACCOUNT, uid, null));
}

@Test
public void issueAD61() {
assertNotNull(connector);
assertNotNull(conf);

final Map.Entry<String, String> ids = util.getEntryIDs("pne");
final Set<Attribute> attributes = util.getSimpleProfile(ids, false);
attributes.add(AttributeBuilder.build(ADConfiguration.PNE_FLAG, true));
final Map.Entry<String, String> ids = util.getEntryIDs("AD61NN");
Uid uid = new Uid(ids.getValue());
try {
assertNull(connector.getObject(ObjectClass.ACCOUNT, uid, null));

final Uid uid = connector.create(ObjectClass.ACCOUNT, attributes, null);
final Set<Attribute> attributes = util.getSimpleProfile(ids);
attributes.add(AttributeBuilder.build(ADConfiguration.PNE_FLAG, true));

// Ask just for memberOf
final OperationOptionsBuilder oob = new OperationOptionsBuilder();
oob.setAttributesToGet(UACCONTROL_ATTR, ADConfiguration.PNE_FLAG);
uid = connector.create(ObjectClass.ACCOUNT, attributes, null);

// retrieve created object
ConnectorObject object = connector.getObject(ObjectClass.ACCOUNT, uid, oob.build());
assertTrue(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNE_FLAG).getValue().get(0)));
int uac_before = Integer.parseInt(object.getAttributeByName(UACCONTROL_ATTR).getValue().get(0).toString());
// Ask just for memberOf
final OperationOptionsBuilder oob = new OperationOptionsBuilder();
oob.setAttributesToGet(UACCONTROL_ATTR, ADConfiguration.PNE_FLAG);

// remove password never expire flag
List<Attribute> attrToReplace = Arrays.asList(new Attribute[] {
AttributeBuilder.build(ADConfiguration.PNE_FLAG, false) });
// retrieve created object
ConnectorObject object = connector.getObject(ObjectClass.ACCOUNT, uid, oob.build());
assertTrue(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNE_FLAG).getValue().get(0)));
int uac_before = Integer.parseInt(object.getAttributeByName(UACCONTROL_ATTR).getValue().get(0).toString());

connector.update(ObjectClass.ACCOUNT, uid, new HashSet<>(attrToReplace), null);
// remove password never expire flag and add password not required one
List<Attribute> attrToReplace = Arrays.asList(
new Attribute[] { AttributeBuilder.build(ADConfiguration.PNE_FLAG, false) });

object = connector.getObject(ObjectClass.ACCOUNT, uid, oob.build());
assertFalse(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNE_FLAG).getValue().get(0)));
int uac_after = Integer.parseInt(object.getAttributeByName(UACCONTROL_ATTR).getValue().get(0).toString());
connector.update(ObjectClass.ACCOUNT, uid, new HashSet<>(attrToReplace), null);

assertNotEquals(uac_before, uac_after);
object = connector.getObject(ObjectClass.ACCOUNT, uid, oob.build());
assertFalse(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNE_FLAG).getValue().get(0)));
int uac_after = Integer.parseInt(object.getAttributeByName(UACCONTROL_ATTR).getValue().get(0).toString());

// remove user
connector.delete(ObjectClass.ACCOUNT, uid, null);
assertNull(connector.getObject(ObjectClass.ACCOUNT, uid, null));
assertNotEquals(uac_before, uac_after);
} finally {
// remove user
connector.delete(ObjectClass.ACCOUNT, uid, null);
assertNull(connector.getObject(ObjectClass.ACCOUNT, uid, null));
}
}

@Test
public void issueAD78() {
// test update of both PNR and PNE
assertNotNull(connector);
assertNotNull(conf);

final Map.Entry<String, String> ids = util.getEntryIDs("AD78UPD");
Uid uid = new Uid(ids.getValue());
try {
assertNull(connector.getObject(ObjectClass.ACCOUNT, uid, null));

final Set<Attribute> attributes = util.getSimpleProfile(ids);
attributes.add(AttributeBuilder.build(ADConfiguration.PNE_FLAG, true));
attributes.add(AttributeBuilder.build(ADConfiguration.PNR_FLAG, false));

uid = connector.create(ObjectClass.ACCOUNT, attributes, null);

// Ask just for uac, pne and pnr
final OperationOptionsBuilder oob = new OperationOptionsBuilder();
oob.setAttributesToGet(UACCONTROL_ATTR, ADConfiguration.PNE_FLAG, ADConfiguration.PNR_FLAG);

// retrieve created object
ConnectorObject object = connector.getObject(ObjectClass.ACCOUNT, uid, oob.build());
assertTrue(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNE_FLAG).getValue().get(0)));
assertFalse(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNR_FLAG).getValue().get(0)));
int uac_before = Integer.parseInt(object.getAttributeByName(UACCONTROL_ATTR).getValue().get(0).toString());

// remove password never expire flag and add password not required one
List<Attribute> attrToReplace = Arrays.asList(
new Attribute[] { AttributeBuilder.build(ADConfiguration.PNE_FLAG, false),
AttributeBuilder.build(ADConfiguration.PNR_FLAG, true) });

connector.update(ObjectClass.ACCOUNT, uid, new HashSet<>(attrToReplace), null);

object = connector.getObject(ObjectClass.ACCOUNT, uid, oob.build());
assertFalse(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNE_FLAG).getValue().get(0)));
assertTrue(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNR_FLAG).getValue().get(0)));
int uac_after = Integer.parseInt(object.getAttributeByName(UACCONTROL_ATTR).getValue().get(0).toString());

assertNotEquals(uac_before, uac_after);

uac_before = uac_after;

// remove password not required
attrToReplace = Arrays.asList(new Attribute[] { AttributeBuilder.build(ADConfiguration.PNR_FLAG, false) });

connector.update(ObjectClass.ACCOUNT, uid, new HashSet<>(attrToReplace), null);

object = connector.getObject(ObjectClass.ACCOUNT, uid, oob.build());
assertFalse(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNE_FLAG).getValue().get(0)));
assertFalse(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNR_FLAG).getValue().get(0)));
uac_after = Integer.parseInt(object.getAttributeByName(UACCONTROL_ATTR).getValue().get(0).toString());

assertNotEquals(uac_before, uac_after);

uac_before = uac_after;

// add password never expires
attrToReplace = Arrays.asList(new Attribute[] { AttributeBuilder.build(ADConfiguration.PNE_FLAG, true) });

connector.update(ObjectClass.ACCOUNT, uid, new HashSet<>(attrToReplace), null);

object = connector.getObject(ObjectClass.ACCOUNT, uid, oob.build());
assertTrue(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNE_FLAG).getValue().get(0)));
assertFalse(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNR_FLAG).getValue().get(0)));
uac_after = Integer.parseInt(object.getAttributeByName(UACCONTROL_ATTR).getValue().get(0).toString());

assertNotEquals(uac_before, uac_after);
} finally {
// remove user
connector.delete(ObjectClass.ACCOUNT, uid, null);
assertNull(connector.getObject(ObjectClass.ACCOUNT, uid, null));
}
}

@Test
public void issueAD78_pne_pnr() {
// check whether both pne and pnr to true are supported
assertNotNull(connector);
assertNotNull(conf);

final Map.Entry<String, String> ids = util.getEntryIDs("AD78CR");
Uid uid = new Uid(ids.getValue());
try {
assertNull(connector.getObject(ObjectClass.ACCOUNT, uid, null));

final Set<Attribute> attributes = util.getSimpleProfile(ids);
attributes.add(AttributeBuilder.build(ADConfiguration.PNE_FLAG, true));
attributes.add(AttributeBuilder.build(ADConfiguration.PNR_FLAG, true));

uid = connector.create(ObjectClass.ACCOUNT, attributes, null);
assertNotNull(uid);
assertEquals(ids.getValue(), uid.getUidValue());

// Ask just for uac, pne and pnr
final OperationOptionsBuilder oob = new OperationOptionsBuilder();
oob.setAttributesToGet(UACCONTROL_ATTR, ADConfiguration.PNE_FLAG, ADConfiguration.PNR_FLAG);

// retrieve created object
ConnectorObject object = connector.getObject(ObjectClass.ACCOUNT, uid, oob.build());
assertTrue(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNE_FLAG).getValue().get(0)));
assertTrue(Boolean.class.cast(object.getAttributeByName(ADConfiguration.PNR_FLAG).getValue().get(0)));
} finally {
// remove user
connector.delete(ObjectClass.ACCOUNT, uid, null);
assertNull(connector.getObject(ObjectClass.ACCOUNT, uid, null));
}
}

@Test
Expand Down

0 comments on commit c877b29

Please sign in to comment.