Merge branch 'master' of https://code.google.com/p/plexi.sharepoint
diff --git a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
index f692a68..78b6fbf 100644
--- a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
+++ b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
@@ -367,11 +367,6 @@
*/
private boolean isSp2007;
private NtlmAuthenticator ntlmAuthenticator;
- /**
- * Lock for refreshing MemberIdMapping. We use a unique lock because it is
- * held while waiting on I/O.
- */
- private final Object refreshMemberIdMappingLock = new Object();
private FormsAuthenticationHandler authenticationHandler;
private static final TimeZone gmt = TimeZone.getTimeZone("GMT");
@@ -1304,6 +1299,18 @@
private final Callable<MemberIdMapping> memberIdMappingCallable;
private final Callable<MemberIdMapping> siteUserIdMappingCallable;
+ /**
+ * Lock for refreshing MemberIdMapping. We use a unique lock because it is
+ * held while waiting on I/O.
+ */
+ private final Object refreshMemberIdMappingLock = new Object();
+
+ /**
+ * Lock for refreshing SiteUserMapping. We use a unique lock because it is
+ * held while waiting on I/O.
+ */
+ private final Object refreshSiteUserMappingLock = new Object();
+
public SiteAdaptor(String site, String web, SiteDataSoap siteDataSoap,
UserGroupSoap userGroupSoap, PeopleSoap people,
Callable<MemberIdMapping> memberIdMappingCallable,
@@ -1361,6 +1368,27 @@
return getMemberIdMapping();
}
+ /**
+ * Provide a more recent SiteUserMapping than {@code mapping}, because the
+ * mapping is known to be out-of-date.
+ */
+ private MemberIdMapping refreshSiteUserMapping(MemberIdMapping mapping)
+ throws IOException {
+ // Synchronize callers to prevent a rush of invalidations due to multiple
+ // callers noticing that the map was out of date at the same time.
+ synchronized (refreshSiteUserMappingLock) {
+ // NOTE: This may block on I/O, so we must be wary of what locks are
+ // held.
+ MemberIdMapping maybeNewMapping = getSiteUserMapping();
+ if (mapping != maybeNewMapping) {
+ // The map has already been refreshed.
+ return maybeNewMapping;
+ }
+ siteUserCache.invalidate(siteUrl);
+ }
+ return getSiteUserMapping();
+ }
+
private MemberIdMapping getSiteUserMapping() throws IOException {
try {
return siteUserIdMappingCallable.call();
@@ -1924,7 +1952,9 @@
final long necessaryPermissionMask) throws IOException {
List<Principal> permits = new LinkedList<Principal>();
MemberIdMapping mapping = getMemberIdMapping();
- MemberIdMapping newMapping = null;
+ boolean memberIdMappingRefreshed = false;
+ MemberIdMapping siteUserMapping = null;
+ boolean siteUserMappingRefreshed = false;
for (Permission permission : permissions) {
// Although it is named "mask", this is really a bit-field of
// permissions.
@@ -1935,13 +1965,31 @@
Integer id = permission.getMemberid();
Principal principal = mapping.getPrincipal(id);
if (principal == null) {
- if (newMapping == null) {
- newMapping = refreshMemberIdMapping(mapping);
- }
- principal = newMapping.getPrincipal(id);
+ log.log(Level.FINE, "Member id {0} is not available in memberid"
+ + " mapping for Web [{1}] under Site Collection [{2}].",
+ new Object[] {id, webUrl, siteUrl});
+ if (siteUserMapping == null) {
+ siteUserMapping = getSiteUserMapping();
+ }
+ principal = siteUserMapping.getPrincipal(id);
}
+ if (principal == null && !memberIdMappingRefreshed) {
+ // Try to refresh member id mapping and check again.
+ mapping = refreshMemberIdMapping(mapping);
+ memberIdMappingRefreshed = true;
+ principal = mapping.getPrincipal(id);
+ }
+ if (principal == null && !siteUserMappingRefreshed) {
+ // Try to refresh site user mapping and check again.
+ siteUserMapping = refreshSiteUserMapping(siteUserMapping);
+ siteUserMappingRefreshed = true;
+ principal = siteUserMapping.getPrincipal(id);
+ }
+
if (principal == null) {
- log.log(Level.WARNING, "Could not resolve member id {0}", id);
+ log.log(Level.WARNING, "Could not resolve member id {0} for Web "
+ + "[{1}] under Site Collection [{2}].",
+ new Object[] {id, webUrl, siteUrl});
continue;
}
permits.add(principal);
diff --git a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
index 9ce1fb3..ae5d523 100644
--- a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
+++ b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
@@ -836,6 +836,74 @@
assertEquals(URI.create("http://localhost:1/sites/SiteCollection"),
response.getDisplayUrl());
}
+
+ @Test
+ public void testGetDocContentSubSiteUniquePermissions() throws Exception {
+ String subSiteUrl = "http://localhost:1/sites/SiteCollection/SubSite";
+ Users users = new Users();
+ users.getUser().add(createUserGroupUser(1, "GDC-PSL\\administrator",
+ "S-1-5-21-7369146", "Administrator", "admin@domain.com", false, true));
+ users.getUser().add(createUserGroupUser(7, "GDC-PSL\\User1",
+ "S-1-5-21-736911", "User1", "User1@domain.com", false, false));
+ users.getUser().add(createUserGroupUser(500, "GDC-PSL\\User500",
+ "S-1-5-21-7369500", "User500", "User11@domain.com", false, false));
+ users.getUser().add(createUserGroupUser(1073741823, "System.Account",
+ "S-1-5-21-7369343", "System Account", "System.Account@domain.com",
+ false, true));
+
+ MockUserGroupSoap mockUserGroupSoap = new MockUserGroupSoap(users);
+ SoapFactory siteDataFactory = MockSoapFactory.blank()
+ .endpoint(VS_ENDPOINT, MockSiteData.blank()
+ .register(VS_CONTENT_EXCHANGE)
+ .register(CD_CONTENT_EXCHANGE)
+ .register(SITES_SITECOLLECTION_SAW_EXCHANGE)
+ .register(new SiteAndWebExchange(subSiteUrl, 0,
+ "http://localhost:1/sites/SiteCollection", subSiteUrl)))
+ .endpoint(SITES_SITECOLLECTION_ENDPOINT, MockSiteData.blank()
+ .register(SITES_SITECOLLECTION_URLSEG_EXCHANGE)
+ .register(SITES_SITECOLLECTION_S_CONTENT_EXCHANGE)
+ .register(SITES_SITECOLLECTION_SC_CONTENT_EXCHANGE))
+ .endpoint(subSiteUrl + "/_vti_bin/SiteData.asmx", MockSiteData.blank()
+ .register(new URLSegmentsExchange(
+ subSiteUrl, true, "WebId", null, null, null))
+ .register(SITES_SITECOLLECTION_S_CONTENT_EXCHANGE
+ .replaceInContent("/SiteCollection",
+ "/SiteCollection/SubSite")
+ .replaceInContent(
+ "ScopeID=\"{01abac8c-66c8-4fed-829c-8dd02bbf40dd}\"",
+ "ScopeID=\"{O7ac581ea-fdd1-4b0d-a5de-fc1b69e57a8d}\"")
+ .replaceInContent(
+ "<permission memberid='4' mask='756052856929' />",
+ "<permission memberid='4' mask='0' />")
+ .replaceInContent("</permissions>",
+ "<permission memberid='500' mask='756052856929' />"
+ + "</permissions>"))
+ .register(SITES_SITECOLLECTION_SC_CONTENT_EXCHANGE))
+ .endpoint("http://localhost:1/sites/SiteCollection/"
+ + "_vti_bin/UserGroup.asmx", mockUserGroupSoap);
+
+ adaptor = new SharePointAdaptor(siteDataFactory,
+ new UnsupportedHttpClient(), executorFactory,
+ new MockAuthenticationClientFactoryForms());
+ adaptor.init(new MockAdaptorContext(config, pusher));
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ GetContentsRequest request = new GetContentsRequest(
+ new DocId("http://localhost:1/sites/SiteCollection/SubSite"));
+ GetContentsResponse response = new GetContentsResponse(baos);
+ adaptor.getDocContent(request, response);
+
+ assertEquals(new Acl.Builder()
+ .setEverythingCaseInsensitive()
+ .setInheritFrom(new DocId("http://localhost:1/sites/SiteCollection"),
+ "admin")
+ .setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
+ .setPermitGroups(Arrays.asList(
+ SITES_SITECOLLECTION_MEMBERS,
+ SITES_SITECOLLECTION_OWNERS))
+ .setPermitUsers(Arrays.asList(GDC_PSL_SPUSER1,
+ new UserPrincipal("GDC-PSL\\User500", DEFAULT_NAMESPACE))).build(),
+ response.getAcl());
+ }
@Test
public void testGetDocContentSiteCollectionWithAdGroup() throws Exception {
@@ -931,12 +999,16 @@
public void testGetDocContentSiteCollectionWithOutOfDateMemberCache()
throws Exception {
ReferenceSiteData siteData = new ReferenceSiteData();
+ Users users = new Users();
+ MockUserGroupSoap mockUserGroupSoap = new MockUserGroupSoap(users);
SoapFactory siteDataFactory = MockSoapFactory.blank()
.endpoint(VS_ENDPOINT, MockSiteData.blank()
.register(VS_CONTENT_EXCHANGE)
.register(CD_CONTENT_EXCHANGE)
.register(SITES_SITECOLLECTION_SAW_EXCHANGE))
- .endpoint(SITES_SITECOLLECTION_ENDPOINT, siteData);
+ .endpoint(SITES_SITECOLLECTION_ENDPOINT, siteData)
+ .endpoint("http://localhost:1/sites/SiteCollection/"
+ + "_vti_bin/UserGroup.asmx", mockUserGroupSoap);
SiteDataSoap siteDataState1 = MockSiteData.blank()
.register(SITES_SITECOLLECTION_URLSEG_EXCHANGE)
.register(SITES_SITECOLLECTION_S_CONTENT_EXCHANGE)