Merge branch 'master' of https://code.google.com/p/plexi.ad
diff --git a/src/com/google/enterprise/adaptor/ad/AdAdaptor.java b/src/com/google/enterprise/adaptor/ad/AdAdaptor.java
index a0bb712..532170a 100644
--- a/src/com/google/enterprise/adaptor/ad/AdAdaptor.java
+++ b/src/com/google/enterprise/adaptor/ad/AdAdaptor.java
@@ -246,7 +246,8 @@
/*deleted=*/ false,
new String[] { "uSNChanged", "sAMAccountName", "objectGUID;binary",
"objectSid;binary", "userPrincipalName", "primaryGroupId",
- "member" });
+ "member", "userAccountControl" });
+ // disabled groups handled later, in makeDefs()
log.log(Level.FINE, "received {0} entities from server", entities.size());
for (AdEntity e : entities) {
bySid.put(e.getSid(), e);
@@ -366,6 +367,12 @@
continue;
}
+ if (entity.isDisabled()) {
+ log.log(Level.FINE, "Skipping {0} members from disabled group {1}",
+ new Object[]{entity.getMembers().size(), group});
+ groups.put(group, def);
+ continue;
+ }
for (String memberDn : allMembers.get(entity)) {
AdEntity member = byDn.get(memberDn);
if (member == null) {
diff --git a/src/com/google/enterprise/adaptor/ad/AdEntity.java b/src/com/google/enterprise/adaptor/ad/AdEntity.java
index 90fce2f..f1e60c2 100644
--- a/src/com/google/enterprise/adaptor/ad/AdEntity.java
+++ b/src/com/google/enterprise/adaptor/ad/AdEntity.java
@@ -1,5 +1,7 @@
package com.google.enterprise.adaptor.ad;
+import com.google.common.annotations.VisibleForTesting;
+
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@@ -27,6 +29,7 @@
private long uSNChanged;
private boolean wellKnown;
private boolean allMembershipsRetrieved;
+ private long userAccountControl; // determines whether user/group is disabled
private final Pattern attrMemberPattern =
Pattern.compile("member;range=[0-9]+-.*", Pattern.CASE_INSENSITIVE);
@@ -78,6 +81,12 @@
}
primaryGroupId = (String) getAttribute(attrs, "primaryGroupId");
userPrincipalName = (String) getAttribute(attrs, "userPrincipalName");
+ s = (String) getAttribute(attrs, "userAccountControl");
+ if (s == null) {
+ userAccountControl = 0; // not disabled - any value where value & 2 == 0
+ } else {
+ userAccountControl = Long.parseLong(s);
+ }
members = new HashSet<String>();
if (isGroup()) {
@@ -232,6 +241,7 @@
result.append("objectGUID", objectGUID);
result.append("sid", sid);
result.append("uSNChanged", uSNChanged);
+ result.append("userAccountControl", userAccountControl);
result.append("allMembershipsRetrieved", allMembershipsRetrieved);
return result.toString();
}
@@ -262,6 +272,19 @@
}
/**
+ * @return if current entity is disabled
+ */
+ public boolean isDisabled() {
+ //TODO(myk): see if there's any reason to check ADS_UF_LOCKOUT [16]
+ return ((userAccountControl & 2) != 0);
+ }
+
+ @VisibleForTesting
+ void setUserAccountControl(long userAccountControl) {
+ this.userAccountControl = userAccountControl;
+ }
+
+ /**
* @return if we need to retrieve further memberships for this group
*/
public boolean areAllMembershipsRetrieved() {
@@ -299,14 +322,17 @@
}
AdEntity other = (AdEntity) o;
return dn.equals(other.dn)
- && sAMAccountName.equals(other.sAMAccountName)
+ && ((sAMAccountName == null) ? (other.sAMAccountName == null) :
+ sAMAccountName.equals(other.sAMAccountName))
&& ((userPrincipalName == null) ? (other.userPrincipalName == null) :
userPrincipalName.equals(other.userPrincipalName))
&& ((primaryGroupId == null) ? (other.primaryGroupId == null) :
primaryGroupId.equals(other.primaryGroupId))
&& ((sid == null) ? (other.sid == null) : sid.equals(other.sid))
- && members.equals(other.members)
- && uSNChanged == other.uSNChanged;
+ && ((members == null) ? (other.members == null) :
+ members.equals(other.members))
+ && uSNChanged == other.uSNChanged
+ && userAccountControl == other.userAccountControl;
// note: 3 fields (objectGUID, wellKnown, and allMembershipsRetrieved)
// are intentionally skipped - we'd need a setter method to make the
// "golden" values correct.
@@ -316,7 +342,7 @@
public int hashCode() {
// same 3 fields as above are excluded here.
return Arrays.hashCode(new Object[] {dn, sAMAccountName, userPrincipalName,
- primaryGroupId, sid, members, uSNChanged});
+ primaryGroupId, sid, members, uSNChanged, userAccountControl});
}
/**
diff --git a/test/com/google/enterprise/adaptor/ad/AdAdaptorTest.java b/test/com/google/enterprise/adaptor/ad/AdAdaptorTest.java
index 79d7574..6115d08 100644
--- a/test/com/google/enterprise/adaptor/ad/AdAdaptorTest.java
+++ b/test/com/google/enterprise/adaptor/ad/AdAdaptorTest.java
@@ -160,11 +160,75 @@
}
@Test
+ public void testGroupCatalogReadFromReturnsDisabledGroup() throws Exception {
+ Map<String, String> strings = defaultLocalizedStringMap();
+
+ AdAdaptor.GroupCatalog groupCatalog = new AdAdaptor.GroupCatalog(
+ strings, "example.com", /*feedBuiltinGroups=*/ false);
+ MockLdapContext ldapContext = defaultMockLdapContext();
+ // add a disabled group
+ String filter = "(|(&(objectClass=group)"
+ + "(groupType:1.2.840.113556.1.4.803:=2147483648))"
+ + "(&(objectClass=user)(objectCategory=person)))";
+ String searchDn = "DN_for_default_naming_context";
+ List<String> members = Arrays.asList("dn_for_user_1", "dn_for_user_2");
+ ldapContext.addSearchResult(filter, "cn", searchDn, "group_name")
+ .addSearchResult(filter, "objectSid;binary", searchDn,
+ hexStringToByteArray("010100000000000000000000")) // S-1-0-0
+ .addSearchResult(filter, "objectGUID;binary", searchDn,
+ hexStringToByteArray("000102030405060708090a0b0e"))
+ .addSearchResult(filter, "member", searchDn, members)
+ .addSearchResult(filter, "userAccountControl", searchDn, "514")
+ .addSearchResult(filter, "sAMAccountName", searchDn,
+ "name under");
+
+ AdServer adServer = new AdServer("localhost", ldapContext);
+ adServer.initialize();
+
+ groupCatalog.readFrom(adServer);
+
+ AdEntity[] groupEntity = groupCatalog.entities.toArray(new AdEntity[0]);
+ final AdEntity goldenEntity = groupEntity[0];
+ final Map<AdEntity, Set<String>> goldenMembers =
+ new HashMap<AdEntity, Set<String>>();
+ goldenMembers.put(goldenEntity, goldenEntity.getMembers());
+ final Map<String, AdEntity> goldenSid =
+ new HashMap<String, AdEntity>();
+ goldenSid.put(goldenEntity.getSid(), goldenEntity);
+ AdEntity everyone = groupCatalog.bySid.get("S-1-1-0");
+ AdEntity authUsers = groupCatalog.bySid.get("S-1-5-11");
+ AdEntity interactive = groupCatalog.bySid.get("S-1-5-4");
+ goldenSid.put("S-1-1-0", everyone);
+ goldenSid.put("S-1-5-11", authUsers);
+ goldenSid.put("S-1-5-4", interactive);
+ final Map<String, AdEntity> goldenDn =
+ new HashMap<String, AdEntity>();
+ goldenDn.put(goldenEntity.getDn(), goldenEntity);
+ goldenDn.put(everyone.getDn(), everyone);
+ goldenDn.put(authUsers.getDn(), authUsers);
+ goldenDn.put(interactive.getDn(), interactive);
+ final Map<AdEntity, String> goldenDomain = new HashMap<AdEntity, String>();
+ goldenDomain.put(goldenEntity, "GSA-CONNECTORS");
+ goldenDomain.put(interactive, "NT Authority");
+ goldenDomain.put(authUsers, "NT Authority");
+
+ final AdAdaptor.GroupCatalog golden = new AdAdaptor.GroupCatalog(
+ strings, "example.com", /*feedBuiltinGroups=*/ true,
+ /*entities*/ Sets.newHashSet(goldenEntity),
+ /*members*/ goldenMembers,
+ /*bySid*/ goldenSid,
+ /*byDn*/ goldenDn,
+ /*domain*/ goldenDomain);
+
+ assertTrue(golden.equals(groupCatalog));
+ }
+
+ @Test
public void testGroupCatalogReadFromReturnsUser() throws Exception {
AdAdaptor.GroupCatalog groupCatalog = new AdAdaptor.GroupCatalog(
defaultLocalizedStringMap(), "example", /*feedBuiltinGroups=*/ false);
MockLdapContext ldapContext = defaultMockLdapContext();
- // add a group
+ // add a user
String filter = "(|(&(objectClass=group)"
+ "(groupType:1.2.840.113556.1.4.803:=2147483648))"
+ "(&(objectClass=user)(objectCategory=person)))";
@@ -378,14 +442,14 @@
AdAdaptor.GroupCatalog groupCatalog = new AdAdaptor.GroupCatalog(
strings, "example.com", /*feedBuiltinGroups=*/ false);
- MockLdapContext ldapContext = mockLdapContextForMakeDefs();
+ MockLdapContext ldapContext = mockLdapContextForMakeDefs(false);
AdServer adServer = new AdServer("localhost", ldapContext);
adServer.initialize();
groupCatalog.readFrom(adServer);
- tweakGroupCatalogForMakeDefs(groupCatalog, adServer);
+ tweakGroupCatalogForMakeDefs(groupCatalog, adServer, false);
final Map<GroupPrincipal, List<Principal>> golden =
new HashMap<GroupPrincipal, List<Principal>>();
@@ -401,6 +465,33 @@
}
@Test
+ public void testGroupCatalogMakeDefsWithDisabledGroup() throws Exception {
+ Map<String, String> strings = defaultLocalizedStringMap();
+
+ AdAdaptor.GroupCatalog groupCatalog = new AdAdaptor.GroupCatalog(
+ strings, "example.com", /*feedBuiltinGroups=*/ false);
+
+ MockLdapContext ldapContext = mockLdapContextForMakeDefs(true);
+
+ AdServer adServer = new AdServer("localhost", ldapContext);
+ adServer.initialize();
+
+ groupCatalog.readFrom(adServer);
+
+ tweakGroupCatalogForMakeDefs(groupCatalog, adServer, true);
+
+ final Map<GroupPrincipal, List<Principal>> golden =
+ new HashMap<GroupPrincipal, List<Principal>>();
+ {
+ golden.put(new GroupPrincipal("sam@GSA-CONNECTORS", "example.com"),
+ Collections.<Principal>emptyList());
+ golden.put(new GroupPrincipal("known_group", "example.com"),
+ Collections.<Principal>emptyList());
+ }
+ assertEquals(golden, groupCatalog.makeDefs());
+ }
+
+ @Test
public void testGroupCatalogMakeDefsWellKnownParent() throws Exception {
Map<String, String> strings = defaultLocalizedStringMap();
@@ -410,7 +501,7 @@
Level oldLevel = log.getLevel();
log.setLevel(Level.FINER);
- MockLdapContext ldapContext = mockLdapContextForMakeDefs();
+ MockLdapContext ldapContext = mockLdapContextForMakeDefs(false);
String searchDn = "DN_for_default_naming_context";
String filter = "(objectCategory=person)";
ldapContext.addSearchResult(filter, "objectSid;binary", searchDn,
@@ -426,7 +517,7 @@
groupCatalog.readFrom(adServer);
- tweakGroupCatalogForMakeDefs(groupCatalog, adServer);
+ tweakGroupCatalogForMakeDefs(groupCatalog, adServer, false);
// now replace the parent group with a well-known one
AdEntity replacementGroup = new AdEntity("S-1-0-0", "dn=new_parent");
AdEntity groupWithNoName = new AdEntity("", "dn=");
@@ -610,7 +701,7 @@
String principal, String passwd) {
MockLdapContext ldapContext = null;
try {
- ldapContext = mockLdapContextForMakeDefs();
+ ldapContext = mockLdapContextForMakeDefs(false);
} catch (Exception e) {
fail("Could not create LdapContext:" + e);
}
@@ -689,7 +780,8 @@
return ldapContext;
}
- private MockLdapContext mockLdapContextForMakeDefs() throws Exception {
+ private MockLdapContext mockLdapContextForMakeDefs(boolean disableSamGroup)
+ throws Exception {
MockLdapContext ldapContext = defaultMockLdapContext();
// add a group
String filter = "(|(&(objectClass=group)"
@@ -706,6 +798,8 @@
.addSearchResult(filter, "objectGUID;binary", searchDn,
hexStringToByteArray("000102030405060708090a0b0e"))
.addSearchResult(filter, "member", searchDn, members)
+ .addSearchResult(filter, "userAccountControl", searchDn,
+ (disableSamGroup ? "514" : "512"))
.addSearchResult(filter, "sAMAccountName", searchDn, "sam");
// and a user (under another filter)
String filter2 = "(&(objectClass=user)(objectCategory=person))";
@@ -722,7 +816,7 @@
}
private void tweakGroupCatalogForMakeDefs(AdAdaptor.GroupCatalog groupCatalog,
- AdServer adServer) throws Exception {
+ AdServer adServer, boolean disableSamGroup) throws Exception {
// add two additional entities to test all branches of our method.
assertEquals(1, groupCatalog.entities.size());
// first -- a user
@@ -887,7 +981,7 @@
String principal, String passwd) {
MockLdapContext ldapContext = null;
try {
- ldapContext = mockLdapContextForMakeDefs();
+ ldapContext = mockLdapContextForMakeDefs(false);
} catch (Exception e) {
fail("Could not create LdapContext:" + e);
}
diff --git a/test/com/google/enterprise/adaptor/ad/AdEntityTest.java b/test/com/google/enterprise/adaptor/ad/AdEntityTest.java
index cfca6d4..73256bc 100644
--- a/test/com/google/enterprise/adaptor/ad/AdEntityTest.java
+++ b/test/com/google/enterprise/adaptor/ad/AdEntityTest.java
@@ -33,6 +33,7 @@
AdServerTest.hexStringToByteArray("010100000000000000000000"));
attrs.put("uSNChanged", "12345678");
attrs.put("primaryGroupId", "users");
+ attrs.put("userAccountControl", "512"); // standard, enabled, user
SearchResult sr = new SearchResult("SR name", attrs, attrs);
sr.setNameInNamespace("cn=user,ou=Users,dc=example,dc=com");
@@ -43,18 +44,62 @@
assertFalse(adEntity.isWellKnown());
assertEquals(0, adEntity.getMembers().size());
assertEquals("S-1-0-users", adEntity.getPrimaryGroupSid());
+ assertFalse(adEntity.isDisabled());
}
@Test
public void testWellKnownConstructor() throws Exception {
AdEntity adEntity = new AdEntity("S-1-1-1",
"dn=escaped\\,cn=users,ou=Users,dc=example,dc=com");
+ adEntity.setUserAccountControl(514); // disabled user
assertEquals("escaped,cn=users", adEntity.getCommonName());
assertEquals("S-1-1-1", adEntity.getSid());
assertEquals("dn=escaped\\,cn=users,ou=Users,dc=example,dc=com",
adEntity.getDn());
assertTrue(adEntity.isWellKnown());
assertEquals(0, adEntity.getMembers().size());
+ assertTrue(adEntity.isDisabled());
+ }
+
+ @Test
+ public void testEquals() throws Exception {
+ AdEntity one = one = new AdEntity("foo", "bar");
+ String nonAdEntity = new String("bogus");
+ assertFalse(one.equals(nonAdEntity));
+ AdEntity two = new AdEntity("foo", "baz");
+ assertFalse(one.equals(nonAdEntity));
+ two = new AdEntity("baz", "bar");
+ assertFalse(one.equals(nonAdEntity));
+
+ Attributes attrs = new BasicAttributes();
+ attrs.put("objectGUID;binary",
+ AdServerTest.hexStringToByteArray("000102030405060708090a0b0c"));
+ attrs.put("objectSid;binary", // S-1-0-0
+ AdServerTest.hexStringToByteArray("010100000000000000000000"));
+ attrs.put("uSNChanged", "12345678");
+ attrs.put("primaryGroupId", "users");
+ attrs.put("userPrincipalName", "user");
+ attrs.put("sAMAccountName", "sam");
+ attrs.put("userAccountControl", "512"); // standard, enabled, user
+
+ SearchResult sr = new SearchResult("SR name", attrs, attrs);
+ sr.setNameInNamespace("cn=user,ou=Users,dc=example,dc=com");
+ one = new AdEntity(sr);
+ attrs.put("primaryGroupId", "another group");
+ sr = new SearchResult("SR name", attrs, attrs);
+ sr.setNameInNamespace("cn=user,ou=Users,dc=example,dc=com");
+ two = new AdEntity(sr);
+ assertFalse(one.equals(two));
+ // TODO(myk): additional equality tests for other fields, if deemed useful
+
+ // test userAccountControl field for equality
+ one = new AdEntity("dn1", "dn=user,ou=Users,dc=example,dc=com");
+ two = new AdEntity("dn1", "dn=user,ou=Users,dc=example,dc=com");
+ assertEquals(one, two);
+ two.setUserAccountControl(514); // disabled user
+ assertFalse(one.equals(two));
+ two.setUserAccountControl(0);
+ assertEquals(one, two);
}
@Test