Left: | ||
Right: |
OLD | NEW |
---|---|
1 // Copyright 2012 Google Inc. All Rights Reserved. | 1 // Copyright 2012 Google Inc. All Rights Reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
(...skipping 15 matching lines...) Expand all Loading... | |
26 import com.google.enterprise.adaptor.DocIdPusher; | 26 import com.google.enterprise.adaptor.DocIdPusher; |
27 import com.google.enterprise.adaptor.GroupPrincipal; | 27 import com.google.enterprise.adaptor.GroupPrincipal; |
28 import com.google.enterprise.adaptor.IOHelper; | 28 import com.google.enterprise.adaptor.IOHelper; |
29 import com.google.enterprise.adaptor.PollingIncrementalAdaptor; | 29 import com.google.enterprise.adaptor.PollingIncrementalAdaptor; |
30 import com.google.enterprise.adaptor.Request; | 30 import com.google.enterprise.adaptor.Request; |
31 import com.google.enterprise.adaptor.Response; | 31 import com.google.enterprise.adaptor.Response; |
32 import com.google.enterprise.adaptor.UserPrincipal; | 32 import com.google.enterprise.adaptor.UserPrincipal; |
33 import com.google.enterprise.adaptor.sharepoint.RareModificationCache.CachedList ; | 33 import com.google.enterprise.adaptor.sharepoint.RareModificationCache.CachedList ; |
34 import com.google.enterprise.adaptor.sharepoint.RareModificationCache.CachedVirt ualServer; | 34 import com.google.enterprise.adaptor.sharepoint.RareModificationCache.CachedVirt ualServer; |
35 import com.google.enterprise.adaptor.sharepoint.RareModificationCache.CachedWeb; | 35 import com.google.enterprise.adaptor.sharepoint.RareModificationCache.CachedWeb; |
36 import com.google.enterprise.adaptor.sharepoint.SharePointAuthenticationHandler. AuthenticationFactoryImpl; | |
36 import com.google.enterprise.adaptor.sharepoint.SiteDataClient.CursorPaginator; | 37 import com.google.enterprise.adaptor.sharepoint.SiteDataClient.CursorPaginator; |
37 import com.google.enterprise.adaptor.sharepoint.SiteDataClient.Paginator; | 38 import com.google.enterprise.adaptor.sharepoint.SiteDataClient.Paginator; |
38 import com.google.enterprise.adaptor.sharepoint.SiteDataClient.XmlProcessingExce ption; | 39 import com.google.enterprise.adaptor.sharepoint.SiteDataClient.XmlProcessingExce ption; |
39 | 40 |
40 import com.microsoft.schemas.sharepoint.soap.ContentDatabase; | 41 import com.microsoft.schemas.sharepoint.soap.ContentDatabase; |
41 import com.microsoft.schemas.sharepoint.soap.ContentDatabases; | 42 import com.microsoft.schemas.sharepoint.soap.ContentDatabases; |
42 import com.microsoft.schemas.sharepoint.soap.Files; | 43 import com.microsoft.schemas.sharepoint.soap.Files; |
43 import com.microsoft.schemas.sharepoint.soap.FolderData; | 44 import com.microsoft.schemas.sharepoint.soap.FolderData; |
44 import com.microsoft.schemas.sharepoint.soap.Folders; | 45 import com.microsoft.schemas.sharepoint.soap.Folders; |
45 import com.microsoft.schemas.sharepoint.soap.GroupDescription; | 46 import com.microsoft.schemas.sharepoint.soap.GroupDescription; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
80 | 81 |
81 import java.io.*; | 82 import java.io.*; |
82 import java.net.*; | 83 import java.net.*; |
83 import java.nio.charset.Charset; | 84 import java.nio.charset.Charset; |
84 import java.util.*; | 85 import java.util.*; |
85 import java.util.concurrent.*; | 86 import java.util.concurrent.*; |
86 import java.util.logging.*; | 87 import java.util.logging.*; |
87 import java.util.regex.Pattern; | 88 import java.util.regex.Pattern; |
88 | 89 |
89 import javax.xml.namespace.QName; | 90 import javax.xml.namespace.QName; |
91 import javax.xml.ws.BindingProvider; | |
90 import javax.xml.ws.EndpointReference; | 92 import javax.xml.ws.EndpointReference; |
91 import javax.xml.ws.Holder; | 93 import javax.xml.ws.Holder; |
92 import javax.xml.ws.Service; | 94 import javax.xml.ws.Service; |
95 import javax.xml.ws.handler.MessageContext; | |
93 import javax.xml.ws.wsaddressing.W3CEndpointReferenceBuilder; | 96 import javax.xml.ws.wsaddressing.W3CEndpointReferenceBuilder; |
94 | 97 |
95 /** | 98 /** |
96 * SharePoint Adaptor for the GSA. | 99 * SharePoint Adaptor for the GSA. |
97 */ | 100 */ |
98 public class SharePointAdaptor extends AbstractAdaptor | 101 public class SharePointAdaptor extends AbstractAdaptor |
99 implements PollingIncrementalAdaptor { | 102 implements PollingIncrementalAdaptor { |
100 /** Charset used in generated HTML responses. */ | 103 /** Charset used in generated HTML responses. */ |
101 private static final Charset CHARSET = Charset.forName("UTF-8"); | 104 private static final Charset CHARSET = Charset.forName("UTF-8"); |
102 private static final String XMLNS_DIRECTORY | 105 private static final String XMLNS_DIRECTORY |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
232 * Cached value of whether we are talking to a SP 2010 server or not. This | 235 * Cached value of whether we are talking to a SP 2010 server or not. This |
233 * value is used in case of error in certain situations. | 236 * value is used in case of error in certain situations. |
234 */ | 237 */ |
235 private boolean isSp2010; | 238 private boolean isSp2010; |
236 private NtlmAuthenticator ntlmAuthenticator; | 239 private NtlmAuthenticator ntlmAuthenticator; |
237 /** | 240 /** |
238 * Lock for refreshing MemberIdMapping. We use a unique lock because it is | 241 * Lock for refreshing MemberIdMapping. We use a unique lock because it is |
239 * held while waiting on I/O. | 242 * held while waiting on I/O. |
240 */ | 243 */ |
241 private final Object refreshMemberIdMappingLock = new Object(); | 244 private final Object refreshMemberIdMappingLock = new Object(); |
245 ·· | |
246 private static SharePointAuthenticationHandler authenticationHandler; | |
ejona
2013/08/09 21:35:47
Remove static; it is per-instance (even though we
| |
242 | 247 |
243 public SharePointAdaptor() { | 248 public SharePointAdaptor() { |
244 this(new SiteDataClient.SiteDataFactoryImpl(), new UserGroupFactoryImpl(), | 249 this(new SiteDataClient.SiteDataFactoryImpl(), new UserGroupFactoryImpl(), |
245 new HttpClientImpl(), new CachedThreadPoolFactory()); | 250 new HttpClientImpl(), new CachedThreadPoolFactory()); |
246 } | 251 } |
247 | 252 |
248 @VisibleForTesting | 253 @VisibleForTesting |
249 SharePointAdaptor(SiteDataClient.SiteDataFactory siteDataFactory, | 254 SharePointAdaptor(SiteDataClient.SiteDataFactory siteDataFactory, |
250 UserGroupFactory userGroupFactory, HttpClient httpClient, | 255 UserGroupFactory userGroupFactory, HttpClient httpClient, |
251 Callable<ExecutorService> executorFactory) { | 256 Callable<ExecutorService> executorFactory) { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
297 maxIndexableSize = Integer.parseInt( | 302 maxIndexableSize = Integer.parseInt( |
298 config.getValue("sharepoint.maxIndexableSize")); | 303 config.getValue("sharepoint.maxIndexableSize")); |
299 | 304 |
300 log.log(Level.CONFIG, "VirtualServer: {0}", virtualServer); | 305 log.log(Level.CONFIG, "VirtualServer: {0}", virtualServer); |
301 log.log(Level.CONFIG, "Username: {0}", username); | 306 log.log(Level.CONFIG, "Username: {0}", username); |
302 log.log(Level.CONFIG, "Password: {0}", password); | 307 log.log(Level.CONFIG, "Password: {0}", password); |
303 | 308 |
304 ntlmAuthenticator = new NtlmAuthenticator(username, password); | 309 ntlmAuthenticator = new NtlmAuthenticator(username, password); |
305 // Unfortunately, this is a JVM-wide modification. | 310 // Unfortunately, this is a JVM-wide modification. |
306 Authenticator.setDefault(ntlmAuthenticator); | 311 Authenticator.setDefault(ntlmAuthenticator); |
307 | 312 URL virtualServerUrl = new URL(virtualServer); |
313 ntlmAuthenticator.addPermitForHost(virtualServerUrl); | |
314 authenticationHandler = new SharePointAuthenticationHandler(virtualServer,· | |
315 virtualServerUrl.getPort(),username, password,· | |
316 new ScheduledThreadPoolExecutor(1), new AuthenticationFactoryImpl()); | |
ejona
2013/08/09 21:35:47
The ScheduledThreadPoolExecutor has to be shutdown
| |
317 authenticationHandler.start(); | |
308 executor = executorFactory.call(); | 318 executor = executorFactory.call(); |
309 | |
310 try { | 319 try { |
311 SiteDataClient virtualServerSiteDataClient = | 320 SiteDataClient virtualServerSiteDataClient = |
312 getSiteAdaptor(virtualServer, virtualServer).getSiteDataClient(); | 321 getSiteAdaptor(virtualServer, virtualServer).getSiteDataClient(); |
313 rareModCache | 322 rareModCache |
314 = new RareModificationCache(virtualServerSiteDataClient, executor); | 323 = new RareModificationCache(virtualServerSiteDataClient, executor); |
315 | 324 |
316 // Test out configuration. | 325 // Test out configuration. |
317 virtualServerSiteDataClient.getContentVirtualServer(); | 326 virtualServerSiteDataClient.getContentVirtualServer(); |
318 } catch (Exception e) { | 327 } catch (Exception e) { |
319 // Don't leak the executor. | 328 // Don't leak the executor. |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
483 } | 492 } |
484 SiteAdaptor siteAdaptor = siteAdaptors.get(web); | 493 SiteAdaptor siteAdaptor = siteAdaptors.get(web); |
485 if (siteAdaptor == null) { | 494 if (siteAdaptor == null) { |
486 if (site.endsWith("/")) { | 495 if (site.endsWith("/")) { |
487 // Always end without a '/' for a canonical form. | 496 // Always end without a '/' for a canonical form. |
488 site = site.substring(0, site.length() - 1); | 497 site = site.substring(0, site.length() - 1); |
489 } | 498 } |
490 ntlmAuthenticator.addPermitForHost(new URL(web)); | 499 ntlmAuthenticator.addPermitForHost(new URL(web)); |
491 String endpoint = web + "/_vti_bin/SiteData.asmx"; | 500 String endpoint = web + "/_vti_bin/SiteData.asmx"; |
492 SiteDataSoap siteDataSoap = siteDataFactory.newSiteData(endpoint); | 501 SiteDataSoap siteDataSoap = siteDataFactory.newSiteData(endpoint); |
502 addFormsAuthenticationCookies((BindingProvider) siteDataSoap); | |
493 | 503 |
494 String endpointUserGroup = site + "/_vti_bin/UserGroup.asmx"; | 504 String endpointUserGroup = site + "/_vti_bin/UserGroup.asmx"; |
495 UserGroupSoap userGroupSoap | 505 UserGroupSoap userGroupSoap |
496 = userGroupFactory.newUserGroup(endpointUserGroup); | 506 = userGroupFactory.newUserGroup(endpointUserGroup); |
507 addFormsAuthenticationCookies((BindingProvider) userGroupSoap); | |
497 | 508 |
498 siteAdaptor = new SiteAdaptor(site, web, siteDataSoap, userGroupSoap, | 509 siteAdaptor = new SiteAdaptor(site, web, siteDataSoap, userGroupSoap, |
499 new MemberIdMappingCallable(site), | 510 new MemberIdMappingCallable(site), |
500 new SiteUserIdMappingCallable(site)); | 511 new SiteUserIdMappingCallable(site)); |
501 siteAdaptors.putIfAbsent(web, siteAdaptor); | 512 siteAdaptors.putIfAbsent(web, siteAdaptor); |
502 siteAdaptor = siteAdaptors.get(web); | 513 siteAdaptor = siteAdaptors.get(web); |
503 } | 514 } |
504 return siteAdaptor; | 515 return siteAdaptor; |
505 } | 516 } |
517 ·· | |
518 private void addFormsAuthenticationCookies(BindingProvider port) { | |
519 List<String> authenticationCookies· | |
520 = authenticationHandler.getAuthenticationCookies(); | |
521 if (authenticationCookies != null && !authenticationCookies.isEmpty()) { | |
ejona
2013/08/09 21:35:47
No, let's just add it outright and always. No need
| |
522 port.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, | |
523 Collections.singletonMap("Cookie", authenticationCookies)); | |
524 } | |
525 } | |
506 | 526 |
507 static URI spUrlToUri(String url) throws IOException { | 527 static URI spUrlToUri(String url) throws IOException { |
508 // Because SP is silly, the path of the URI is unencoded, but the rest of | 528 // Because SP is silly, the path of the URI is unencoded, but the rest of |
509 // the URI is correct. Thus, we split up the path from the host, and then | 529 // the URI is correct. Thus, we split up the path from the host, and then |
510 // turn them into URIs separately, and then turn everything into a | 530 // turn them into URIs separately, and then turn everything into a |
511 // properly-escaped string. | 531 // properly-escaped string. |
512 String[] parts = url.split("/", 4); | 532 String[] parts = url.split("/", 4); |
513 if (parts.length < 3) { | 533 if (parts.length < 3) { |
514 throw new IllegalArgumentException("Too few '/'s: " + url); | 534 throw new IllegalArgumentException("Too few '/'s: " + url); |
515 } | 535 } |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
692 final long necessaryPermissionMask = LIST_ITEM_MASK; | 712 final long necessaryPermissionMask = LIST_ITEM_MASK; |
693 // A PolicyUser is either a user or group, but we aren't provided with | 713 // A PolicyUser is either a user or group, but we aren't provided with |
694 // which. Thus, we treat PolicyUsers as both a user and a group in ACLs | 714 // which. Thus, we treat PolicyUsers as both a user and a group in ACLs |
695 // and understand that only one of the two entries will have an effect. | 715 // and understand that only one of the two entries will have an effect. |
696 List<UserPrincipal> permitUsers = new ArrayList<UserPrincipal>(); | 716 List<UserPrincipal> permitUsers = new ArrayList<UserPrincipal>(); |
697 List<GroupPrincipal> permitGroups = new ArrayList<GroupPrincipal>(); | 717 List<GroupPrincipal> permitGroups = new ArrayList<GroupPrincipal>(); |
698 List<UserPrincipal> denyUsers = new ArrayList<UserPrincipal>(); | 718 List<UserPrincipal> denyUsers = new ArrayList<UserPrincipal>(); |
699 List<GroupPrincipal> denyGroups = new ArrayList<GroupPrincipal>(); | 719 List<GroupPrincipal> denyGroups = new ArrayList<GroupPrincipal>(); |
700 for (PolicyUser policyUser : vs.getPolicies().getPolicyUser()) { | 720 for (PolicyUser policyUser : vs.getPolicies().getPolicyUser()) { |
701 // TODO(ejona): special case NT AUTHORITY\LOCAL SERVICE. | 721 // TODO(ejona): special case NT AUTHORITY\LOCAL SERVICE. |
702 String loginName = policyUser.getLoginName(); | 722 String loginName = decodeClaim(policyUser.getLoginName(), |
723 policyUser.getLoginName(), false); | |
Tanmay Vartak
2013/08/08 23:17:35
this one should be new CL for Claims with Policy U
ejona
2013/08/09 21:35:47
Agreed.
| |
724 log.log(Level.FINER, "Policy User Login Name = {0}", loginName); | |
703 long grant = policyUser.getGrantMask().longValue(); | 725 long grant = policyUser.getGrantMask().longValue(); |
704 if ((necessaryPermissionMask & grant) == necessaryPermissionMask) { | 726 if ((necessaryPermissionMask & grant) == necessaryPermissionMask) { |
705 permitUsers.add(new UserPrincipal(loginName)); | 727 permitUsers.add(new UserPrincipal(loginName)); |
706 permitGroups.add(new GroupPrincipal(loginName)); | 728 permitGroups.add(new GroupPrincipal(loginName)); |
707 } | 729 } |
708 long deny = policyUser.getDenyMask().longValue(); | 730 long deny = policyUser.getDenyMask().longValue(); |
709 // If at least one necessary bit is masked, then deny user. | 731 // If at least one necessary bit is masked, then deny user. |
710 if ((necessaryPermissionMask & deny) != 0) { | 732 if ((necessaryPermissionMask & deny) != 0) { |
711 denyUsers.add(new UserPrincipal(loginName)); | 733 denyUsers.add(new UserPrincipal(loginName)); |
712 denyGroups.add(new GroupPrincipal(loginName)); | 734 denyGroups.add(new GroupPrincipal(loginName)); |
(...skipping 932 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1645 , boolean isDomainGroup) { | 1667 , boolean isDomainGroup) { |
1646 if (!loginName.startsWith(IDENTITY_CLAIMS_PREFIX) | 1668 if (!loginName.startsWith(IDENTITY_CLAIMS_PREFIX) |
1647 && !loginName.startsWith(OTHER_CLAIMS_PREFIX)) { | 1669 && !loginName.startsWith(OTHER_CLAIMS_PREFIX)) { |
1648 return isDomainGroup ? name : loginName; | 1670 return isDomainGroup ? name : loginName; |
1649 } | 1671 } |
1650 // AD User | 1672 // AD User |
1651 if (loginName.startsWith("i:0#.w|")) { | 1673 if (loginName.startsWith("i:0#.w|")) { |
1652 return loginName.substring(7); | 1674 return loginName.substring(7); |
1653 // AD Group | 1675 // AD Group |
1654 } else if (loginName.startsWith("c:0+.w|")) { | 1676 } else if (loginName.startsWith("c:0+.w|")) { |
1655 return name; | 1677 return name.startsWith("c:0+.w|") ? name.substring(7) : name; |
Tanmay Vartak
2013/08/08 23:17:35
this one should be new CL for Claims with Policy U
ejona
2013/08/09 21:35:47
Agreed.
| |
1656 } else if (loginName.equals("c:0(.s|true")) { | 1678 } else if (loginName.equals("c:0(.s|true")) { |
1657 return "Everyone"; | 1679 return "Everyone"; |
1658 } else if (loginName.equals("c:0!.s|windows")) { | 1680 } else if (loginName.equals("c:0!.s|windows")) { |
1659 return "NT AUTHORITY\\authenticated users"; | 1681 return "NT AUTHORITY\\authenticated users"; |
1660 } | 1682 } |
1661 log.log(Level.WARNING, "Unsupported claims value {0}", loginName); | 1683 log.log(Level.WARNING, "Unsupported claims value {0}", loginName); |
1662 return null; | 1684 return null; |
1663 } | 1685 } |
1664 | 1686 |
1665 private MemberIdMapping retrieveMemberIdMapping() throws IOException { | 1687 private MemberIdMapping retrieveMemberIdMapping() throws IOException { |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1865 static class HttpClientImpl implements HttpClient { | 1887 static class HttpClientImpl implements HttpClient { |
1866 @Override | 1888 @Override |
1867 public FileInfo issueGetRequest(URL url) throws IOException { | 1889 public FileInfo issueGetRequest(URL url) throws IOException { |
1868 // Handle Unicode. Java does not properly encode the GET. | 1890 // Handle Unicode. Java does not properly encode the GET. |
1869 try { | 1891 try { |
1870 url = new URL(url.toURI().toASCIIString()); | 1892 url = new URL(url.toURI().toASCIIString()); |
1871 } catch (URISyntaxException ex) { | 1893 } catch (URISyntaxException ex) { |
1872 throw new IOException(ex); | 1894 throw new IOException(ex); |
1873 } | 1895 } |
1874 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); | 1896 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); |
1897 List<String> authenticationCookies· | |
1898 = authenticationHandler.getAuthenticationCookies(); | |
1899 for (String cookie : authenticationCookies) { | |
1900 conn.addRequestProperty("Cookie", cookie); | |
1901 } | |
1875 conn.setDoInput(true); | 1902 conn.setDoInput(true); |
1876 conn.setDoOutput(false); | 1903 conn.setDoOutput(false); |
1877 if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { | 1904 if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { |
1878 return null; | 1905 return null; |
1879 } | 1906 } |
1880 if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { | 1907 if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { |
1881 throw new IOException("Got status code: " + conn.getResponseCode()); | 1908 throw new IOException("Got status code: " + conn.getResponseCode()); |
1882 } | 1909 } |
1883 List<String> headers = new LinkedList<String>(); | 1910 List<String> headers = new LinkedList<String>(); |
1884 // Start at 1 since index 0 is special. | 1911 // Start at 1 since index 0 is special. |
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2036 } | 2063 } |
2037 | 2064 |
2038 private static class CachedThreadPoolFactory | 2065 private static class CachedThreadPoolFactory |
2039 implements Callable<ExecutorService> { | 2066 implements Callable<ExecutorService> { |
2040 @Override | 2067 @Override |
2041 public ExecutorService call() { | 2068 public ExecutorService call() { |
2042 return Executors.newCachedThreadPool(); | 2069 return Executors.newCachedThreadPool(); |
2043 } | 2070 } |
2044 } | 2071 } |
2045 } | 2072 } |
OLD | NEW |