Add Claims support to web-app-policy-ACL and use of people.asmx to resolve policy users
Code review : https://codereview.appspot.com/12892043/
diff --git a/People.spec.wsdl b/People.spec.wsdl
new file mode 100644
index 0000000..42c5ab2
--- /dev/null
+++ b/People.spec.wsdl
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="utf-8"?>
+<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
+xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
+xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
+xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
+xmlns:tns="http://schemas.microsoft.com/sharepoint/soap/" 
+xmlns:s="http://www.w3.org/2001/XMLSchema" 
+xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
+xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
+targetNamespace="http://schemas.microsoft.com/sharepoint/soap/" 
+xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
+  <wsdl:types>
+    <s:schema elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/sharepoint/soap/">
+      <s:element name="IsClaimsMode">
+        <s:complexType />
+      </s:element>
+      <s:element name="IsClaimsModeResponse">
+        <s:complexType>
+          <s:sequence>
+            <s:element minOccurs="1" maxOccurs="1" name="IsClaimsModeResult" type="s:boolean" />
+          </s:sequence>
+        </s:complexType>
+      </s:element>
+      <s:element name="ResolvePrincipals">
+        <s:complexType>
+          <s:sequence>
+            <s:element minOccurs="0" maxOccurs="1" name="principalKeys" type="tns:ArrayOfString" />
+            <s:element minOccurs="1" maxOccurs="1" name="principalType" type="tns:SPPrincipalType" />
+            <s:element minOccurs="1" maxOccurs="1" name="addToUserInfoList" type="s:boolean" />
+          </s:sequence>
+        </s:complexType>
+      </s:element>
+      <s:complexType name="ArrayOfString">
+        <s:sequence>
+          <s:element minOccurs="0" maxOccurs="unbounded" name="string" nillable="true" type="s:string" />
+        </s:sequence>
+      </s:complexType>
+      <s:simpleType name="SPPrincipalType">
+        <s:list>
+          <s:simpleType>
+            <s:restriction base="s:string">
+              <s:enumeration value="None" />
+              <s:enumeration value="User" />
+              <s:enumeration value="DistributionList" />
+              <s:enumeration value="SecurityGroup" />
+              <s:enumeration value="SharePointGroup" />
+              <s:enumeration value="All" />
+            </s:restriction>
+          </s:simpleType>
+        </s:list>
+      </s:simpleType>
+      <s:element name="ResolvePrincipalsResponse">
+        <s:complexType>
+          <s:sequence>
+            <s:element minOccurs="0" maxOccurs="1" name="ResolvePrincipalsResult" type="tns:ArrayOfPrincipalInfo" />
+          </s:sequence>
+        </s:complexType>
+      </s:element>
+      <s:complexType name="ArrayOfPrincipalInfo">
+        <s:sequence>
+          <s:element minOccurs="0" maxOccurs="unbounded" name="PrincipalInfo" type="tns:PrincipalInfo" />
+        </s:sequence>
+      </s:complexType>
+      <s:complexType name="PrincipalInfo">
+        <s:sequence>
+          <s:element minOccurs="0" maxOccurs="1" name="AccountName" type="s:string" />
+          <s:element minOccurs="1" maxOccurs="1" name="UserInfoID" type="s:int" />
+          <s:element minOccurs="0" maxOccurs="1" name="DisplayName" type="s:string" />
+          <s:element minOccurs="0" maxOccurs="1" name="Email" type="s:string" />
+          <s:element minOccurs="0" maxOccurs="1" name="Department" type="s:string" />
+          <s:element minOccurs="0" maxOccurs="1" name="Title" type="s:string" />
+          <s:element minOccurs="1" maxOccurs="1" name="IsResolved" type="s:boolean" />
+          <s:element minOccurs="0" maxOccurs="1" name="MoreMatches" type="tns:ArrayOfPrincipalInfo" />
+          <s:element minOccurs="1" maxOccurs="1" name="PrincipalType" type="tns:SPPrincipalType" />
+        </s:sequence>
+      </s:complexType>
+      <s:element name="SearchPrincipals">
+        <s:complexType>
+          <s:sequence>
+            <s:element minOccurs="0" maxOccurs="1" name="searchText" type="s:string" />
+            <s:element minOccurs="1" maxOccurs="1" name="maxResults" type="s:int" />
+            <s:element minOccurs="1" maxOccurs="1" name="principalType" type="tns:SPPrincipalType" />
+          </s:sequence>
+        </s:complexType>
+      </s:element>
+      <s:element name="SearchPrincipalsResponse">
+        <s:complexType>
+          <s:sequence>
+            <s:element minOccurs="0" maxOccurs="1" name="SearchPrincipalsResult" type="tns:ArrayOfPrincipalInfo" />
+          </s:sequence>
+        </s:complexType>
+      </s:element>
+    </s:schema>
+  </wsdl:types>
+  <wsdl:message name="IsClaimsModeSoapIn">
+    <wsdl:part name="parameters" element="tns:IsClaimsMode" />
+  </wsdl:message>
+  <wsdl:message name="IsClaimsModeSoapOut">
+    <wsdl:part name="parameters" element="tns:IsClaimsModeResponse" />
+  </wsdl:message>
+  <wsdl:message name="ResolvePrincipalsSoapIn">
+    <wsdl:part name="parameters" element="tns:ResolvePrincipals" />
+  </wsdl:message>
+  <wsdl:message name="ResolvePrincipalsSoapOut">
+    <wsdl:part name="parameters" element="tns:ResolvePrincipalsResponse" />
+  </wsdl:message>
+  <wsdl:message name="SearchPrincipalsSoapIn">
+    <wsdl:part name="parameters" element="tns:SearchPrincipals" />
+  </wsdl:message>
+  <wsdl:message name="SearchPrincipalsSoapOut">
+    <wsdl:part name="parameters" element="tns:SearchPrincipalsResponse" />
+  </wsdl:message>
+  <wsdl:portType name="PeopleSoap">
+    <wsdl:operation name="IsClaimsMode">
+      <wsdl:input message="tns:IsClaimsModeSoapIn" />
+      <wsdl:output message="tns:IsClaimsModeSoapOut" />
+    </wsdl:operation>
+    <wsdl:operation name="ResolvePrincipals">
+      <wsdl:input message="tns:ResolvePrincipalsSoapIn" />
+      <wsdl:output message="tns:ResolvePrincipalsSoapOut" />
+    </wsdl:operation>
+    <wsdl:operation name="SearchPrincipals">
+      <wsdl:input message="tns:SearchPrincipalsSoapIn" />
+      <wsdl:output message="tns:SearchPrincipalsSoapOut" />
+    </wsdl:operation>
+  </wsdl:portType>
+  <wsdl:binding name="PeopleSoap" type="tns:PeopleSoap">
+    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
+    <wsdl:operation name="IsClaimsMode">
+      <soap:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/IsClaimsMode" style="document" />
+      <wsdl:input>
+        <soap:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+    <wsdl:operation name="ResolvePrincipals">
+      <soap:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/ResolvePrincipals" style="document" />
+      <wsdl:input>
+        <soap:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+    <wsdl:operation name="SearchPrincipals">
+      <soap:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/SearchPrincipals" style="document" />
+      <wsdl:input>
+        <soap:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+  </wsdl:binding>
+  <wsdl:binding name="PeopleSoap12" type="tns:PeopleSoap">
+    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
+    <wsdl:operation name="IsClaimsMode">
+      <soap12:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/IsClaimsMode" style="document" />
+      <wsdl:input>
+        <soap12:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap12:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+    <wsdl:operation name="ResolvePrincipals">
+      <soap12:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/ResolvePrincipals" style="document" />
+      <wsdl:input>
+        <soap12:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap12:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+    <wsdl:operation name="SearchPrincipals">
+      <soap12:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/SearchPrincipals" style="document" />
+      <wsdl:input>
+        <soap12:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap12:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+  </wsdl:binding>
+  <wsdl:service name="People">
+    <wsdl:port name="PeopleSoap" binding="tns:PeopleSoap">
+      <soap:address location="http://entpoint05/_vti_bin/People.asmx" />
+    </wsdl:port>
+    <wsdl:port name="PeopleSoap12" binding="tns:PeopleSoap12">
+      <soap12:address location="http://entpoint05/_vti_bin/People.asmx" />
+    </wsdl:port>
+  </wsdl:service>
+</wsdl:definitions>
\ No newline at end of file
diff --git a/People.wsdl b/People.wsdl
new file mode 100644
index 0000000..48bb5f3
--- /dev/null
+++ b/People.wsdl
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="utf-8"?>
+<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
+xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
+xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
+xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
+xmlns:tns="http://schemas.microsoft.com/sharepoint/soap/" 
+xmlns:s="http://www.w3.org/2001/XMLSchema" 
+xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
+xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
+targetNamespace="http://schemas.microsoft.com/sharepoint/soap/" 
+xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
+  <wsdl:types>
+    <s:schema elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/sharepoint/soap/">
+      <s:element name="IsClaimsMode">
+        <s:complexType />
+      </s:element>
+      <s:element name="IsClaimsModeResponse">
+        <s:complexType>
+          <s:sequence>
+            <s:element minOccurs="1" maxOccurs="1" name="IsClaimsModeResult" type="s:boolean" />
+          </s:sequence>
+        </s:complexType>
+      </s:element>
+      <s:element name="ResolvePrincipals">
+        <s:complexType>
+          <s:sequence>
+            <s:element minOccurs="0" maxOccurs="1" name="principalKeys" type="tns:ArrayOfString" />
+            <s:element minOccurs="1" maxOccurs="1" name="principalType" type="tns:SPPrincipalType" />
+            <s:element minOccurs="1" maxOccurs="1" name="addToUserInfoList" type="s:boolean" />
+          </s:sequence>
+        </s:complexType>
+      </s:element>
+      <s:complexType name="ArrayOfString">
+        <s:sequence>
+          <s:element minOccurs="0" maxOccurs="unbounded" name="string" nillable="true" type="s:string" />
+        </s:sequence>
+      </s:complexType>
+      <s:simpleType name="SPPrincipalType">
+        <!--s:list>
+          <s:simpleType-->
+            <s:restriction base="s:string">
+              <s:enumeration value="None" />
+              <s:enumeration value="User" />
+              <s:enumeration value="DistributionList" />
+              <s:enumeration value="SecurityGroup" />
+              <s:enumeration value="SharePointGroup" />
+              <s:enumeration value="All" />
+            </s:restriction>
+          <!--/s:simpleType>
+        </s:list-->
+      </s:simpleType>
+      <s:element name="ResolvePrincipalsResponse">
+        <s:complexType>
+          <s:sequence>
+            <s:element minOccurs="0" maxOccurs="1" name="ResolvePrincipalsResult" type="tns:ArrayOfPrincipalInfo" />
+          </s:sequence>
+        </s:complexType>
+      </s:element>
+      <s:complexType name="ArrayOfPrincipalInfo">
+        <s:sequence>
+          <s:element minOccurs="0" maxOccurs="unbounded" name="PrincipalInfo" type="tns:PrincipalInfo" />
+        </s:sequence>
+      </s:complexType>
+      <s:complexType name="PrincipalInfo">
+        <s:sequence>
+          <s:element minOccurs="0" maxOccurs="1" name="AccountName" type="s:string" />
+          <s:element minOccurs="1" maxOccurs="1" name="UserInfoID" type="s:int" />
+          <s:element minOccurs="0" maxOccurs="1" name="DisplayName" type="s:string" />
+          <s:element minOccurs="0" maxOccurs="1" name="Email" type="s:string" />
+          <s:element minOccurs="0" maxOccurs="1" name="Department" type="s:string" />
+          <s:element minOccurs="0" maxOccurs="1" name="Title" type="s:string" />
+          <s:element minOccurs="1" maxOccurs="1" name="IsResolved" type="s:boolean" />
+          <s:element minOccurs="0" maxOccurs="1" name="MoreMatches" type="tns:ArrayOfPrincipalInfo" />
+          <s:element minOccurs="1" maxOccurs="1" name="PrincipalType" type="tns:SPPrincipalType" />
+        </s:sequence>
+      </s:complexType>
+      <s:element name="SearchPrincipals">
+        <s:complexType>
+          <s:sequence>
+            <s:element minOccurs="0" maxOccurs="1" name="searchText" type="s:string" />
+            <s:element minOccurs="1" maxOccurs="1" name="maxResults" type="s:int" />
+            <s:element minOccurs="1" maxOccurs="1" name="principalType" type="tns:SPPrincipalType" />
+          </s:sequence>
+        </s:complexType>
+      </s:element>
+      <s:element name="SearchPrincipalsResponse">
+        <s:complexType>
+          <s:sequence>
+            <s:element minOccurs="0" maxOccurs="1" name="SearchPrincipalsResult" type="tns:ArrayOfPrincipalInfo" />
+          </s:sequence>
+        </s:complexType>
+      </s:element>
+    </s:schema>
+  </wsdl:types>
+  <wsdl:message name="IsClaimsModeSoapIn">
+    <wsdl:part name="parameters" element="tns:IsClaimsMode" />
+  </wsdl:message>
+  <wsdl:message name="IsClaimsModeSoapOut">
+    <wsdl:part name="parameters" element="tns:IsClaimsModeResponse" />
+  </wsdl:message>
+  <wsdl:message name="ResolvePrincipalsSoapIn">
+    <wsdl:part name="parameters" element="tns:ResolvePrincipals" />
+  </wsdl:message>
+  <wsdl:message name="ResolvePrincipalsSoapOut">
+    <wsdl:part name="parameters" element="tns:ResolvePrincipalsResponse" />
+  </wsdl:message>
+  <wsdl:message name="SearchPrincipalsSoapIn">
+    <wsdl:part name="parameters" element="tns:SearchPrincipals" />
+  </wsdl:message>
+  <wsdl:message name="SearchPrincipalsSoapOut">
+    <wsdl:part name="parameters" element="tns:SearchPrincipalsResponse" />
+  </wsdl:message>
+  <wsdl:portType name="PeopleSoap">
+    <wsdl:operation name="IsClaimsMode">
+      <wsdl:input message="tns:IsClaimsModeSoapIn" />
+      <wsdl:output message="tns:IsClaimsModeSoapOut" />
+    </wsdl:operation>
+    <wsdl:operation name="ResolvePrincipals">
+      <wsdl:input message="tns:ResolvePrincipalsSoapIn" />
+      <wsdl:output message="tns:ResolvePrincipalsSoapOut" />
+    </wsdl:operation>
+    <wsdl:operation name="SearchPrincipals">
+      <wsdl:input message="tns:SearchPrincipalsSoapIn" />
+      <wsdl:output message="tns:SearchPrincipalsSoapOut" />
+    </wsdl:operation>
+  </wsdl:portType>
+  <wsdl:binding name="PeopleSoap" type="tns:PeopleSoap">
+    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
+    <wsdl:operation name="IsClaimsMode">
+      <soap:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/IsClaimsMode" style="document" />
+      <wsdl:input>
+        <soap:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+    <wsdl:operation name="ResolvePrincipals">
+      <soap:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/ResolvePrincipals" style="document" />
+      <wsdl:input>
+        <soap:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+    <wsdl:operation name="SearchPrincipals">
+      <soap:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/SearchPrincipals" style="document" />
+      <wsdl:input>
+        <soap:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+  </wsdl:binding>
+  <wsdl:binding name="PeopleSoap12" type="tns:PeopleSoap">
+    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
+    <wsdl:operation name="IsClaimsMode">
+      <soap12:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/IsClaimsMode" style="document" />
+      <wsdl:input>
+        <soap12:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap12:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+    <wsdl:operation name="ResolvePrincipals">
+      <soap12:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/ResolvePrincipals" style="document" />
+      <wsdl:input>
+        <soap12:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap12:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+    <wsdl:operation name="SearchPrincipals">
+      <soap12:operation soapAction="http://schemas.microsoft.com/sharepoint/soap/SearchPrincipals" style="document" />
+      <wsdl:input>
+        <soap12:body use="literal" />
+      </wsdl:input>
+      <wsdl:output>
+        <soap12:body use="literal" />
+      </wsdl:output>
+    </wsdl:operation>
+  </wsdl:binding>
+  <wsdl:service name="People">
+    <wsdl:port name="PeopleSoap" binding="tns:PeopleSoap">
+      <soap:address location="http://entpoint05/_vti_bin/People.asmx" />
+    </wsdl:port>
+    <wsdl:port name="PeopleSoap12" binding="tns:PeopleSoap12">
+      <soap12:address location="http://entpoint05/_vti_bin/People.asmx" />
+    </wsdl:port>
+  </wsdl:service>
+</wsdl:definitions>
\ No newline at end of file
diff --git a/build.xml b/build.xml
index f8281cb..732c00a 100644
--- a/build.xml
+++ b/build.xml
@@ -165,12 +165,25 @@
       <arg value="Authentication.wsdl"/>
       <arg value="Authentication.wsdl"/>
     </exec>
+    <exec executable="wsimport">
+      <arg value="-s"/>
+      <arg value="${generate.dir}"/>
+      <arg value="-d"/>
+      <arg value="${build-generate.dir}"/>
+      <arg value="-p"/>
+      <arg value="com.microsoft.schemas.sharepoint.soap.people"/>
+      <arg value="-wsdllocation"/>
+      <arg value="People.wsdl"/>
+      <arg value="People.wsdl"/>
+    </exec>
     <copy file="SiteData.wsdl"
       todir="${build-generate.dir}/com/microsoft/schemas/sharepoint/soap/"/>
     <copy file="UserGroup.wsdl"
       todir="${build-generate.dir}/com/microsoft/schemas/sharepoint/soap/directory/"/>
     <copy file="Authentication.wsdl"
-      todir="${build-generate.dir}/com/microsoft/schemas/sharepoint/soap/authentication"/>
+      todir="${build-generate.dir}/com/microsoft/schemas/sharepoint/soap/authentication/"/>
+    <copy file="People.wsdl"
+      todir="${build-generate.dir}/com/microsoft/schemas/sharepoint/soap/people/"/>
     <copy file="UserProfileService.wsdl"
       todir="${build-generate.dir}/com/microsoft/webservices/sharepointportalserver/userprofileservice/"/>
     <copy file="UserProfileChangeService.wsdl"
diff --git a/src/com/google/enterprise/adaptor/sharepoint/FormsAuthenticationHandler.java b/src/com/google/enterprise/adaptor/sharepoint/FormsAuthenticationHandler.java
index 6ab0290..615e8a2 100644
--- a/src/com/google/enterprise/adaptor/sharepoint/FormsAuthenticationHandler.java
+++ b/src/com/google/enterprise/adaptor/sharepoint/FormsAuthenticationHandler.java
@@ -19,7 +19,6 @@
 import com.microsoft.schemas.sharepoint.soap.authentication.AuthenticationSoap;
 import com.microsoft.schemas.sharepoint.soap.authentication.LoginErrorCode;
 import com.microsoft.schemas.sharepoint.soap.authentication.LoginResult;
-
 import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
diff --git a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
index 7b90ac5..c2202e3 100644
--- a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
+++ b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
@@ -72,6 +72,11 @@
 import com.microsoft.schemas.sharepoint.soap.directory.GetUserCollectionFromSiteResponse.GetUserCollectionFromSiteResult;
 import com.microsoft.schemas.sharepoint.soap.directory.User;
 import com.microsoft.schemas.sharepoint.soap.directory.UserGroupSoap;
+import com.microsoft.schemas.sharepoint.soap.people.ArrayOfPrincipalInfo;
+import com.microsoft.schemas.sharepoint.soap.people.ArrayOfString;
+import com.microsoft.schemas.sharepoint.soap.people.PeopleSoap;
+import com.microsoft.schemas.sharepoint.soap.people.PrincipalInfo;
+import com.microsoft.schemas.sharepoint.soap.people.SPPrincipalType;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
@@ -511,14 +516,17 @@
       
       String endpointUserGroup = site + "/_vti_bin/UserGroup.asmx";
       UserGroupSoap userGroupSoap = soapFactory.newUserGroup(endpointUserGroup);
+      String endpointPeople= site + "/_vti_bin/People.asmx";
+      PeopleSoap peopleSoap = soapFactory.newPeople(endpointPeople);
       // JAX-WS RT 2.1.4 doesn't handle headers correctly and always assumes the
       // list contains precisely one entry, so we work around it here.
       if (authenticationHandler.isFormsAuthentication()) {
         addFormsAuthenticationCookies((BindingProvider) siteDataSoap);
-        addFormsAuthenticationCookies((BindingProvider) userGroupSoap);        
+        addFormsAuthenticationCookies((BindingProvider) userGroupSoap);
+        addFormsAuthenticationCookies((BindingProvider) peopleSoap);
       }
       siteAdaptor = new SiteAdaptor(site, web, siteDataSoap, userGroupSoap,
-          new MemberIdMappingCallable(site),
+          peopleSoap, new MemberIdMappingCallable(site),
           new SiteUserIdMappingCallable(site));
       siteAdaptors.putIfAbsent(web, siteAdaptor);
       siteAdaptor = siteAdaptors.get(web);
@@ -527,6 +535,11 @@
   }
   
   private void addFormsAuthenticationCookies(BindingProvider port) {
+    if (authenticationHandler.getAuthenticationCookies().isEmpty()) {
+      // JAX-WS RT 2.1.4 doesn't handle headers correctly and always assumes the
+      // list contains precisely one entry, so we work around it here.
+      return;
+    }
     port.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS,
         Collections.singletonMap("Cookie", 
             authenticationHandler.getAuthenticationCookies()));
@@ -565,6 +578,7 @@
   class SiteAdaptor {
     private final SiteDataClient siteDataClient;
     private final UserGroupSoap userGroup;
+    private final PeopleSoap people;
     private final String siteUrl;
     private final String webUrl;
     /**
@@ -576,7 +590,7 @@
     private final Callable<MemberIdMapping> siteUserIdMappingCallable;
 
     public SiteAdaptor(String site, String web, SiteDataSoap siteDataSoap,
-        UserGroupSoap userGroupSoap,
+        UserGroupSoap userGroupSoap, PeopleSoap people,
         Callable<MemberIdMapping> memberIdMappingCallable,
         Callable<MemberIdMapping> siteUserIdMappingCallable) {
       log.entering("SiteAdaptor", "SiteAdaptor",
@@ -593,6 +607,7 @@
       this.siteUrl = site;
       this.webUrl = web;
       this.userGroup = userGroupSoap;
+      this.people = people;
       this.siteDataClient = new SiteDataClient(siteDataSoap, xmlValidation);
       this.memberIdMappingCallable = memberIdMappingCallable;
       this.siteUserIdMappingCallable = siteUserIdMappingCallable;
@@ -725,26 +740,44 @@
       List<GroupPrincipal> permitGroups = new ArrayList<GroupPrincipal>();
       List<UserPrincipal> denyUsers = new ArrayList<UserPrincipal>();
       List<GroupPrincipal> denyGroups = new ArrayList<GroupPrincipal>();
+      List<String> policyUsers = new ArrayList<String>();
       for (PolicyUser policyUser : vs.getPolicies().getPolicyUser()) {
-        // TODO(ejona): special case NT AUTHORITY\LOCAL SERVICE.
-        String loginName = decodeClaim(policyUser.getLoginName(),
-            policyUser.getLoginName(), false);
-        if (loginName == null) {
+        policyUsers.add(policyUser.getLoginName());
+      }
+      Map<String, PrincipalInfo> resolvedPolicyUsers 
+          = resolvePrincipals(policyUsers);
+      for (PolicyUser policyUser : vs.getPolicies().getPolicyUser()) {
+        String loginName = policyUser.getLoginName();
+        PrincipalInfo p = resolvedPolicyUsers.get(loginName);
+        if (p == null || !p.isIsResolved()) {
           log.log(Level.WARNING, 
-              "Unable to decode claim. Skipping policy user {0}",
-              policyUser.getLoginName());
+              "Unable to resolve Policy User = {0}", loginName);
+          continue;
         }
-        log.log(Level.FINER, "Policy User Login Name = {0}", loginName);
+        // TODO(ejona): special case NT AUTHORITY\LOCAL SERVICE.
+        String accountName = decodeClaim(p.getAccountName(), p.getDisplayName(),
+            p.getPrincipalType() == SPPrincipalType.SECURITY_GROUP);
+        if (accountName == null) {
+          log.log(Level.WARNING, 
+              "Unable to decode claim. Skipping policy user {0}", loginName);
+        }
+        log.log(Level.FINER, "Policy User accountName = {0}", accountName);
         long grant = policyUser.getGrantMask().longValue();
         if ((necessaryPermissionMask & grant) == necessaryPermissionMask) {
-          permitUsers.add(new UserPrincipal(loginName));
-          permitGroups.add(new GroupPrincipal(loginName));
+          if (p.getPrincipalType() == SPPrincipalType.USER) {
+             permitUsers.add(new UserPrincipal(accountName));
+          } else {
+             permitGroups.add(new GroupPrincipal(accountName));
+          }
         }
         long deny = policyUser.getDenyMask().longValue();
         // If at least one necessary bit is masked, then deny user.
         if ((necessaryPermissionMask & deny) != 0) {
-          denyUsers.add(new UserPrincipal(loginName));
-          denyGroups.add(new GroupPrincipal(loginName));
+          if (p.getPrincipalType() == SPPrincipalType.USER) {
+             denyUsers.add(new UserPrincipal(accountName));
+          } else {
+             denyGroups.add(new GroupPrincipal(accountName));
+          }          
         }
       }
       response.setAcl(new Acl.Builder()
@@ -1681,14 +1714,14 @@
         , boolean isDomainGroup) {
       if (!loginName.startsWith(IDENTITY_CLAIMS_PREFIX)
           && !loginName.startsWith(OTHER_CLAIMS_PREFIX)) {
-        return isDomainGroup ? name : loginName;
+        return loginName;
       }
       // AD User
       if (loginName.startsWith("i:0#.w|")) {
         return loginName.substring(7);
       // AD Group
       } else if (loginName.startsWith("c:0+.w|")) {
-        return name.startsWith("c:0+.w|") ? name.substring(7) : name;
+        return name;
       } else if (loginName.equals("c:0(.s|true")) {
         return "Everyone";
       } else if (loginName.equals("c:0!.s|windows")) {
@@ -1704,6 +1737,29 @@
       return null;
     }
 
+    private Map<String,PrincipalInfo> resolvePrincipals(
+        List<String> principalsToResolve) {
+      Map<String, PrincipalInfo> resolved
+          = new HashMap<String, PrincipalInfo>();
+      if (principalsToResolve.isEmpty()) {
+        return resolved;
+      }
+      ArrayOfString aos = new ArrayOfString();
+      aos.getString().addAll(principalsToResolve);
+      ArrayOfPrincipalInfo resolvePrincipals = people.resolvePrincipals(
+          aos, SPPrincipalType.ALL, false);
+      List<PrincipalInfo> principals = resolvePrincipals.getPrincipalInfo();
+      // using loginname from input list principalsToResolve as a key
+      // instead of returned PrincipalInfo.getAccountName() as with claims
+      // authentication PrincipalInfo.getAccountName() is always encoded.
+      // e.g. if login name from Policy is NT Authority\Local Service
+      // returned account name is i:0#.w|NT Authority\Local Service
+      for (int i = 0; i < principalsToResolve.size(); i++) {
+         resolved.put(principalsToResolve.get(i), principals.get(i));
+      }
+      return resolved;
+    }
+
     private MemberIdMapping retrieveMemberIdMapping() throws IOException {
       log.entering("SiteAdaptor", "retrieveMemberIdMapping");
       Site site = siteDataClient.getContentSite();
@@ -1956,6 +2012,8 @@
     public UserGroupSoap newUserGroup(String endpoint);
     
     public AuthenticationSoap newAuthentication(String endpoint);
+    
+    public PeopleSoap newPeople(String endpoint);
   }
 
   @VisibleForTesting
@@ -1963,6 +2021,7 @@
     private final Service siteDataService;
     private final Service userGroupService;
     private final Service authenticationService;
+    private final Service peopleService;
 
     public SoapFactoryImpl() {
       this.siteDataService = SiteDataClient.createSiteDataService();
@@ -1972,6 +2031,9 @@
       this.authenticationService = Service.create(
           AuthenticationSoap.class.getResource("Authentication.wsdl"),
           new QName(XMLNS, "Authentication"));
+      this.peopleService = Service.create(
+          PeopleSoap.class.getResource("People.wsdl"),
+          new QName(XMLNS, "People"));
     }
 
     @Override
@@ -1995,6 +2057,13 @@
       return 
           authenticationService.getPort(endpointRef, AuthenticationSoap.class);
     }
+
+    @Override
+    public PeopleSoap newPeople(String endpoint) {
+      EndpointReference endpointRef = new W3CEndpointReferenceBuilder()
+          .address(endpoint).build();
+      return peopleService.getPort(endpointRef, PeopleSoap.class);      
+    }
   }
 
   private static class NtlmAuthenticator extends Authenticator {
diff --git a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
index 0bf8c65..663d74d 100644
--- a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
+++ b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
@@ -31,11 +31,10 @@
 import com.google.enterprise.adaptor.UserPrincipal;
 import com.google.enterprise.adaptor.sharepoint.SharePointAdaptor.SiteUserIdMappingCallable;
 import com.google.enterprise.adaptor.sharepoint.SharePointAdaptor.SoapFactory;
+
 import com.microsoft.schemas.sharepoint.soap.authentication.AuthenticationMode;
 import com.microsoft.schemas.sharepoint.soap.authentication.AuthenticationSoap;
-import com.microsoft.schemas.sharepoint.soap.authentication.LoginErrorCode;
 import com.microsoft.schemas.sharepoint.soap.authentication.LoginResult;
-
 import com.microsoft.schemas.sharepoint.soap.ObjectType;
 import com.microsoft.schemas.sharepoint.soap.SPContentDatabase;
 import com.microsoft.schemas.sharepoint.soap.SiteDataSoap;
@@ -75,6 +74,11 @@
 import com.microsoft.schemas.sharepoint.soap.directory.User;
 import com.microsoft.schemas.sharepoint.soap.directory.UserGroupSoap;
 import com.microsoft.schemas.sharepoint.soap.directory.Users;
+import com.microsoft.schemas.sharepoint.soap.people.ArrayOfPrincipalInfo;
+import com.microsoft.schemas.sharepoint.soap.people.ArrayOfString;
+import com.microsoft.schemas.sharepoint.soap.people.PeopleSoap;
+import com.microsoft.schemas.sharepoint.soap.people.PrincipalInfo;
+import com.microsoft.schemas.sharepoint.soap.people.SPPrincipalType;
 
 import org.junit.*;
 import org.junit.rules.ExpectedException;
@@ -363,11 +367,18 @@
 
   @Test
   public void testGetDocContentVirtualServer() throws Exception {
+    MockPeopleSoap mockPeople = new MockPeopleSoap();    
+    mockPeople.addToResult("NT AUTHORITY\\LOCAL SERVICE", 
+        "NT AUTHORITY\\LOCAL SERVICE", SPPrincipalType.USER);
+    mockPeople.addToResult("GDC-PSL\\spuser1", "spuser1", SPPrincipalType.USER);
+    mockPeople.addToResult("GDC-PSL\\Administrator", "dministrator", 
+        SPPrincipalType.USER);
     SoapFactory siteDataFactory = MockSoapFactory.blank()
         .endpoint(AUTH_ENDPOINT, new MockAuthenticationSoap())
         .endpoint(VS_ENDPOINT, MockSiteData.blank()
             .register(VS_CONTENT_EXCHANGE)
-            .register(CD_CONTENT_EXCHANGE));
+            .register(CD_CONTENT_EXCHANGE))
+        .endpoint("http://localhost:1/_vti_bin/People.asmx", mockPeople);
 
     adaptor = new SharePointAdaptor(siteDataFactory,
         new UnsupportedHttpClient(), executorFactory);
@@ -392,7 +403,58 @@
     assertEquals(new Acl.Builder()
         .setEverythingCaseInsensitive()
         .setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
-        .setPermitUsers(users(permit)).setPermitGroups(groups(permit)).build(),
+        .setPermitUsers(users(permit)).build(), response.getAcl());
+    assertNull(response.getDisplayUrl());
+  }
+  
+  @Test
+  public void testPolicyAclsWithClaims() throws Exception {
+    String claimsPolicyUsers = "<PolicyUser "
+        + "LoginName=\"i:0#.w|GSA-CONNECTORS\\Administrator\" "
+        + "BinaryIdentifier=\"i:0).w|s-1-5-21-3993744865-352142399"
+        + "7-1479072767-500\" Sid=\"\" BinaryIdentifierType=\"UserKey\" "
+        + "GrantMask=\"9223372036854775807\" DenyMask=\"0\" />"
+        + "<PolicyUser "
+        + "LoginName=\"c:0+.w|s-1-5-21-3993744865-3521423997-1479072767-513\" "
+        + "BinaryIdentifier=\"c:0+.w|s-1-5-21-3993744865-3521423997"
+        + "-1479072767-513\" Sid=\"\" BinaryIdentifierType=\"UserKey\" "
+        + "GrantMask=\"4611686224789442657\" "
+        + "DenyMask=\"0\" /></Policies>";
+    MockPeopleSoap mockPeople = new MockPeopleSoap();
+    mockPeople.addToResult("i:0#.w|GSA-CONNECTORS\\Administrator",
+        "Administrator", SPPrincipalType.USER);
+    mockPeople.addToResult(
+        "c:0+.w|s-1-5-21-3993744865-3521423997-1479072767-513",
+        "GSA-CONNECTORS\\domain users", SPPrincipalType.SECURITY_GROUP);
+    mockPeople.addToResult("NT AUTHORITY\\LOCAL SERVICE", 
+        "NT AUTHORITY\\LOCAL SERVICE", SPPrincipalType.USER);
+    mockPeople.addToResult("GDC-PSL\\spuser1", "spuser1", SPPrincipalType.USER);
+    mockPeople.addToResult("GDC-PSL\\Administrator", "dministrator", 
+        SPPrincipalType.USER);
+    
+    SoapFactory siteDataFactory = MockSoapFactory.blank()
+        .endpoint(AUTH_ENDPOINT, new MockAuthenticationSoap())
+        .endpoint(VS_ENDPOINT, MockSiteData.blank()
+            .register(VS_CONTENT_EXCHANGE
+              .replaceInContent("</Policies>", claimsPolicyUsers))
+            .register(CD_CONTENT_EXCHANGE))
+        .endpoint("http://localhost:1/_vti_bin/People.asmx", mockPeople);
+
+    adaptor = new SharePointAdaptor(siteDataFactory,
+        new UnsupportedHttpClient(), executorFactory);
+    adaptor.init(new MockAdaptorContext(config, pusher));
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    GetContentsResponse response = new GetContentsResponse(baos);
+    adaptor.getDocContent(new GetContentsRequest(new DocId("")), response);       
+    String[] permitUsers = new String[] {"GDC-PSL\\Administrator",
+        "GDC-PSL\\spuser1", "NT AUTHORITY\\LOCAL SERVICE",
+        "GSA-CONNECTORS\\Administrator"};
+    String[] permitGroups= new String[] {"GSA-CONNECTORS\\domain users"};
+    assertEquals(new Acl.Builder()
+        .setEverythingCaseInsensitive()
+        .setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
+        .setPermitUsers(users(permitUsers))
+        .setPermitGroups(groups(permitGroups)).build(),
         response.getAcl());
     assertNull(response.getDisplayUrl());
   }
@@ -461,11 +523,13 @@
         .endpoint(SITES_SITECOLLECTION_ENDPOINT, MockSiteData.blank()
             .register(SITES_SITECOLLECTION_URLSEG_EXCHANGE)
             .register(SITES_SITECOLLECTION_S_CONTENT_EXCHANGE
-              .replaceInContent("Name=\"spuser1\"", "Name=\"GDC-PSL\\group\"")
+              .replaceInContent("LoginName=\"GDC-PSL\\spuser1\"",
+                "LoginName=\"GDC-PSL\\group\"")
               .replaceInContent("IsDomainGroup=\"False\"",
                 "IsDomainGroup=\"True\""))
             .register(SITES_SITECOLLECTION_SC_CONTENT_EXCHANGE
-              .replaceInContent("Name=\"spuser1\"", "Name=\"GDC-PSL\\group\"")
+              .replaceInContent("LoginName=\"GDC-PSL\\spuser1\"",
+                "LoginName=\"GDC-PSL\\group\"")
               .replaceInContent("IsDomainGroup=\"False\"",
                 "IsDomainGroup=\"True\"")));
 
@@ -634,7 +698,8 @@
     GetContentsResponse response = new GetContentsResponse(baos);
     adaptor.new SiteAdaptor("http://localhost:1/sites/SiteCollection",
           "http://localhost:1/sites/SiteCollection", siteData,
-          new UnsupportedUserGroupSoap(), Callables.returning(memberIdMapping),
+          new UnsupportedUserGroupSoap(), new UnsupportedPeopleSoap(),
+          Callables.returning(memberIdMapping),
           new UnsupportedCallable<MemberIdMapping>())
         .getDocContent(request, response);
     String responseString = new String(baos.toByteArray(), charset);
@@ -677,7 +742,7 @@
     GetContentsResponse response = new GetContentsResponse(baos);
     adaptor.new SiteAdaptor("http://localhost:1/sites/SiteCollection",
           "http://localhost:1/sites/SiteCollection", siteData,
-          new UnsupportedUserGroupSoap(),
+          new UnsupportedUserGroupSoap(), new UnsupportedPeopleSoap(),
           new UnsupportedCallable<MemberIdMapping>(),
           new UnsupportedCallable<MemberIdMapping>())
         .getDocContent(request, response);
@@ -720,7 +785,7 @@
     GetContentsResponse response = new GetContentsResponse(baos);
     adaptor.new SiteAdaptor("http://localhost:1/sites/SiteCollection",
           "http://localhost:1/sites/SiteCollection", siteData,
-          new UnsupportedUserGroupSoap(),
+          new UnsupportedUserGroupSoap(), new UnsupportedPeopleSoap(),
           new UnsupportedCallable<MemberIdMapping>(),
           new UnsupportedCallable<MemberIdMapping>())
         .getDocContent(request, response);
@@ -770,7 +835,8 @@
     GetContentsResponse response = new GetContentsResponse(baos);
     adaptor.new SiteAdaptor("http://localhost:1/sites/SiteCollection",
           "http://localhost:1/sites/SiteCollection", siteData,
-          new UnsupportedUserGroupSoap(), Callables.returning(memberIdMapping),
+          new UnsupportedUserGroupSoap(), new UnsupportedPeopleSoap(),
+          Callables.returning(memberIdMapping),
           new UnsupportedCallable<MemberIdMapping>())
         .getDocContent(request, response);
     String responseString = new String(baos.toByteArray(), charset);
@@ -884,7 +950,8 @@
     GetContentsResponse response = new GetContentsResponse(baos);
     adaptor.new SiteAdaptor("http://localhost:1/sites/SiteCollection",
           "http://localhost:1/sites/SiteCollection", siteData,
-          new UnsupportedUserGroupSoap(), Callables.returning(memberIdMapping),
+          new UnsupportedUserGroupSoap(), new UnsupportedPeopleSoap(),
+          Callables.returning(memberIdMapping),
           new UnsupportedCallable<MemberIdMapping>())
         .getDocContent(request, response);
     assertNull(response.getAcl());
@@ -944,7 +1011,8 @@
     GetContentsResponse response = new GetContentsResponse(baos);
     adaptor.new SiteAdaptor("http://localhost:1/sites/SiteCollection",
           "http://localhost:1/sites/SiteCollection", siteData,
-          mockUserGroupSoap, Callables.returning(memberIdMapping),
+          mockUserGroupSoap, new UnsupportedPeopleSoap(),
+          Callables.returning(memberIdMapping),
           adaptor.new SiteUserIdMappingCallable(
               "http://localhost:1/sites/SiteCollection"))
         .getDocContent(request, response);
@@ -1008,7 +1076,7 @@
     GetContentsResponse response = new GetContentsResponse(baos);
     adaptor.new SiteAdaptor("http://localhost:1/sites/SiteCollection",
         "http://localhost:1/sites/SiteCollection",
-        siteData, new UnsupportedUserGroupSoap(),
+        siteData, new UnsupportedUserGroupSoap(), new UnsupportedPeopleSoap(),
         Callables.returning(memberIdMapping),
         new UnsupportedCallable<MemberIdMapping>())
         .getDocContent(request, response);
@@ -1051,7 +1119,7 @@
     GetContentsResponse response = new GetContentsResponse(baos);
     adaptor.new SiteAdaptor("http://localhost:1/sites/SiteCollection",
           "http://localhost:1/sites/SiteCollection",
-          siteData, new UnsupportedUserGroupSoap(),
+          siteData, new UnsupportedUserGroupSoap(), new UnsupportedPeopleSoap(),
         Callables.returning(memberIdMapping),
         new UnsupportedCallable<MemberIdMapping>())
         .getDocContent(request, response);
@@ -1328,7 +1396,7 @@
     adaptor.new SiteAdaptor(
         "http://localhost:1/sites/SiteCollection",
         "http://localhost:1/sites/SiteCollection", new UnsupportedSiteData(),
-        new UnsupportedUserGroupSoap(),
+        new UnsupportedUserGroupSoap(), new UnsupportedPeopleSoap(),
         new UnsupportedCallable<MemberIdMapping>(),
         new UnsupportedCallable<MemberIdMapping>())
         .getModifiedDocIds(result, pusher);
@@ -1444,6 +1512,71 @@
       throw new UnsupportedOperationException();
     }
   }
+  
+  private abstract static class DelegatingPeopleSoap implements PeopleSoap {
+    protected abstract PeopleSoap delegate();
+
+    @Override
+    public boolean isClaimsMode() {
+      return delegate().isClaimsMode();
+    } 
+
+    @Override
+    public ArrayOfPrincipalInfo resolvePrincipals(
+        ArrayOfString aos, SPPrincipalType sppt, boolean bln) {
+      return delegate().resolvePrincipals(aos, sppt, bln);
+    }
+
+    @Override
+    public ArrayOfPrincipalInfo searchPrincipals(
+        String string, int i, SPPrincipalType sppt) {
+      return delegate().searchPrincipals(string, i, sppt);
+    }
+  }
+  
+  private static class UnsupportedPeopleSoap extends DelegatingPeopleSoap {
+    private final String endpoint;
+
+    public UnsupportedPeopleSoap() {
+      this(null);
+    }
+
+    public UnsupportedPeopleSoap(String endpoint) {
+      this.endpoint = endpoint;
+    }
+
+    @Override
+    protected PeopleSoap delegate() {
+      if (endpoint == null) {
+        throw new UnsupportedOperationException();
+      } else {
+        throw new UnsupportedOperationException("Endpoint: " + endpoint);
+      }
+    }
+  }
+  
+  private static class MockPeopleSoap extends UnsupportedPeopleSoap {
+    private final ArrayOfPrincipalInfo result;
+    public MockPeopleSoap() {
+      this.result = new ArrayOfPrincipalInfo();
+    }
+    
+    @Override
+    public ArrayOfPrincipalInfo resolvePrincipals(
+        ArrayOfString aos, SPPrincipalType sppt, boolean bln) {      
+      return result;     
+    }
+    
+    public void addToResult(String accountName, String dispalyName, 
+        SPPrincipalType type) {
+      PrincipalInfo p = new PrincipalInfo();
+      p.setAccountName(accountName);
+      p.setDisplayName(dispalyName);
+      p.setIsResolved(true);
+      p.setPrincipalType(type);
+      result.getPrincipalInfo().add(p);      
+    }
+  }
 
   private static class MockUserGroupSoap extends UnsupportedUserGroupSoap {
     final Users users;    
@@ -1819,37 +1952,47 @@
     private final SiteDataSoap siteData;
     private final UserGroupSoap userGroup;
     private final AuthenticationSoap authentication;
+    private final PeopleSoap people;
     private final MockSoapFactory chain;
 
     private MockSoapFactory(String expectedEndpoint, SiteDataSoap siteData,
-        UserGroupSoap userGroup, AuthenticationSoap authentication,
-        MockSoapFactory chain) {
+        UserGroupSoap userGroup, PeopleSoap people,
+        AuthenticationSoap authentication,  MockSoapFactory chain) {
       this.expectedEndpoint = expectedEndpoint;
       this.siteData = siteData;
       this.userGroup = userGroup;
+      this.people = people;
       // Tests will always use windows authentication.
       this.authentication = authentication;
       this.chain = chain;
     }
 
     public static MockSoapFactory blank() {
-      return new MockSoapFactory(null, null, null, null, null);
+      return new MockSoapFactory(null, null, null, null, null, null);
     }
 
     public MockSoapFactory endpoint(String expectedEndpoint,
         SiteDataSoap siteData) {
-      return new MockSoapFactory(expectedEndpoint, siteData, null, null, this);
+      return new MockSoapFactory(
+          expectedEndpoint, siteData, null, null, null, this);
     }
 
     public MockSoapFactory endpoint(String expectedEndpoint,
         UserGroupSoap userGroup) {
-      return new MockSoapFactory(expectedEndpoint, null, userGroup, null, this);
+      return new MockSoapFactory(
+          expectedEndpoint, null, userGroup, null, null, this);
+    }
+    
+    public MockSoapFactory endpoint(String expectedEndpoint,
+        PeopleSoap people) {
+      return new MockSoapFactory(
+          expectedEndpoint, null, null, people, null, this);
     }
     
     public MockSoapFactory endpoint(String expectedEndpoint,
         AuthenticationSoap authentication) {
       return new MockSoapFactory(
-          expectedEndpoint, null, null, authentication, this);
+          expectedEndpoint, null, null, null, authentication, this);
     }
 
     @Override
@@ -1887,6 +2030,17 @@
       }
       return chain.newUserGroup(endpoint);
     }
+
+    @Override
+    public PeopleSoap newPeople(String endpoint) {
+      if (chain == null) {
+        return new UnsupportedPeopleSoap(endpoint);
+      }
+      if (expectedEndpoint.equals(endpoint) && people != null) {
+        return people;
+      }
+      return chain.newPeople(endpoint);
+    }
   }
 
   private static class ReferenceSiteData extends DelegatingSiteData {