Package com.google.enterprise.connector.sharepoint.client

Source Code of com.google.enterprise.connector.sharepoint.client.AclHelper$SPBasePermissions

// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.enterprise.connector.sharepoint.client;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.enterprise.connector.sharepoint.client.ListsHelper;
import com.google.enterprise.connector.sharepoint.client.SPConstants.FeedType;
import com.google.enterprise.connector.sharepoint.client.SPConstants.SPType;
import com.google.enterprise.connector.sharepoint.client.SPConstants;
import com.google.enterprise.connector.sharepoint.client.SharepointClientContext;
import com.google.enterprise.connector.sharepoint.client.Util;
import com.google.enterprise.connector.sharepoint.dao.UserGroupMembership;
import com.google.enterprise.connector.sharepoint.generated.gssacl.GssAce;
import com.google.enterprise.connector.sharepoint.generated.gssacl.GssAcl;
import com.google.enterprise.connector.sharepoint.generated.gssacl.GssAclChange;
import com.google.enterprise.connector.sharepoint.generated.gssacl.GssAclChangeCollection;
import com.google.enterprise.connector.sharepoint.generated.gssacl.GssGetAclChangesSinceTokenResult;
import com.google.enterprise.connector.sharepoint.generated.gssacl.GssGetAclForUrlsResult;
import com.google.enterprise.connector.sharepoint.generated.gssacl.GssGetListItemsWithInheritingRoleAssignments;
import com.google.enterprise.connector.sharepoint.generated.gssacl.GssPrincipal;
import com.google.enterprise.connector.sharepoint.generated.gssacl.GssResolveSPGroupResult;
import com.google.enterprise.connector.sharepoint.generated.gssacl.GssSharepointPermission;
import com.google.enterprise.connector.sharepoint.generated.gssacl.ObjectType;
import com.google.enterprise.connector.sharepoint.generated.gssacl.PrincipalType;
import com.google.enterprise.connector.sharepoint.generated.gssacl.SPChangeType;
import com.google.enterprise.connector.sharepoint.spiimpl.SPDocument;
import com.google.enterprise.connector.sharepoint.spiimpl.SPDocumentList;
import com.google.enterprise.connector.sharepoint.spiimpl.SharepointException;
import com.google.enterprise.connector.sharepoint.state.ListState;
import com.google.enterprise.connector.sharepoint.state.WebState;
import com.google.enterprise.connector.sharepoint.wsclient.client.AclWS;
import com.google.enterprise.connector.sharepoint.wsclient.client.BaseWS;
import com.google.enterprise.connector.spi.Principal;
import com.google.enterprise.connector.spi.SpiConstants;
import com.google.enterprise.connector.spi.SpiConstants.ActionType;
import com.google.enterprise.connector.spi.SpiConstants.CaseSensitivityType;
import com.google.enterprise.connector.spi.SpiConstants.DocumentType;
import com.google.enterprise.connector.spi.SpiConstants.RoleType;

import org.apache.axis.AxisFault;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.rpc.ServiceException;

/**
* Java Client for calling GssAcl.asmx web service. Provides a layer to talk to
* the ACL Web Service on the SharePoint server. Any call to this Web Service
* must go through this layer.
*/
@SuppressWarnings("deprecation") // TODO(jlacey): RoleType is deprecated.
public class AclHelper {
  private final Logger LOGGER = Logger.getLogger(AclHelper.class.getName());
  private final SharepointClientContext sharepointClientContext;
  private final boolean supportsInheritedAcls;
  private final boolean supportsDenyAcls;
  private final AclWS aclWS;

  /**
   * @param inSharepointClientContext The Context is passed so that necessary
   *          information can be used to create the instance of current class
   *          Web Service endpoint is set to the default SharePoint URL stored
   *          in SharePointClientContext.
   * @param siteurl siteurl to be used for constructing endpoints. If null, site
   *          url is taken from SharepointClientContext
   * @throws SharepointException
   */
  public AclHelper(final SharepointClientContext inSharepointClientContext,
      String siteurl) throws SharepointException {
    if (null == inSharepointClientContext) {
      throw new SharepointException("SharePointClient context cannot be null ");
    }
    sharepointClientContext = inSharepointClientContext;
    if (null == siteurl) {
      siteurl = sharepointClientContext.getSiteURL();
    }
    aclWS = sharepointClientContext.getClientFactory().getAclWS(
        sharepointClientContext, siteurl);

    if (null != sharepointClientContext.getTraversalContext()) {
      supportsInheritedAcls =
          sharepointClientContext.getTraversalContext().supportsInheritedAcls();
      supportsDenyAcls =
          sharepointClientContext.getTraversalContext().supportsDenyAcls();
    } else {
      supportsInheritedAcls = false;
      supportsDenyAcls = false;
    }
    if (!sharepointClientContext.isPushAcls()) {
      return;
    }
    LOGGER.log(Level.CONFIG, "Supports Inherited ACLs " + supportsInheritedAcls);

    final String strDomain = sharepointClientContext.getDomain();
    String strUser = sharepointClientContext.getUsername();
    final String strPassword = sharepointClientContext.getPassword();
    final int timeout = sharepointClientContext.getWebServiceTimeOut();
    LOGGER.fine("Setting time-out to " + timeout + " milliseconds.");

    strUser = Util.getUserNameWithDomain(strUser, strDomain);
    aclWS.setUsername(strUser);
    aclWS.setPassword(strPassword);
    aclWS.setTimeout(timeout);
  }

  /**
   * Executes GetAclForUrls() web method of GssAcl web service. Used to get the
   * ACL of a set of entities.
   *
   * @param urls Set of entity URLs whose ACLs are to be fetched
   * @param useInheritance flag indicating use of ACL inheritance
   * @param includePolicyAcls flag indicating if policy ACLs needs
   *  to be part of document ACLs. With Inheritance support this value should
   *  be false.
   * @return web service response {@link GssGetAclForUrlsResult} as it is
   */
  private GssGetAclForUrlsResult getAclForUrls(final String[] urls,
      final boolean useInheritance, final boolean includePolicyAcls)
      throws SharepointException {
    if (null == urls || urls.length == 0) {
      return null;
    }
   
    // errorFetchingAcls is defined as an array instead of
    // simple boolean object since needs to be used with inline class.
    final Throwable[] errorFetchingAcls = new Throwable[1];
    GssGetAclForUrlsResult aclResult =
        Util.makeWSRequest(sharepointClientContext, aclWS,
        new Util.RequestExecutor<GssGetAclForUrlsResult>() {
      public GssGetAclForUrlsResult onRequest(final BaseWS ws)
          throws Throwable {
        return ((AclWS) ws).getAclForUrlsUsingInheritance(urls,
            useInheritance, includePolicyAcls,
            sharepointClientContext.getLargeACLThreshold(),
            sharepointClientContext.getFeedType().equals(
                FeedType.METADATA_URL_FEED));
      }
     
      public void onError(final Throwable e) {
        LOGGER.log(Level.WARNING, "Call to getAclForUrls failed.", e);
        errorFetchingAcls[0] = e;
      }
    });
   
    if (errorFetchingAcls[0] != null) {
      throw new SharepointException("Error fetching ACLs for batch.",
          errorFetchingAcls[0]);
    }
    return aclResult;
  }

  /**
   * This method will attach inherited ACLs to the document since parent
   *  can not be crawled as per includedUrls
   * @param urls Urls to crawl
   * @param urlToDocMap Document map to update
   */
  private void fetchAclForDocumentsWithExcludedParents(String[] urls,
      Map<String, SPDocument> urlToDocMap, WebState webState)
      throws SharepointException {
    GssGetAclForUrlsResult wsResult = getAclForUrls(urls, false, false);
    if (wsResult != null) {
      processWsResponse(wsResult, urlToDocMap, webState);
    }
  }
 
  /**
   * To process documents with large ACLs
   */
  private void fetchAclForSPDocument(String documentUrl,
      SPDocument document, WebState webState) throws SharepointException {
    GssGetAclForUrlsResult wsResult =
        getAclForUrls(new String[] { documentUrl },
                 supportsInheritedAcls, !supportsInheritedAcls);
    if (wsResult != null) {
      Map<String, SPDocument> docMapToPass = Maps.newHashMap();
      docMapToPass.put(documentUrl, document);
      processWsResponse(wsResult, docMapToPass, webState);
    }
  }

  /**
   * Used to parse the response of {@link #getAclForUrls getAclForUrls} and
   * update the ACLs into the {@link SPDocument} The set of document objects
   * must be passed in form of a map with their URLs as keys. If a user has
   * more than one permission assigned on SHarePoint, connector will include
   * each of them in the ACE. Hence, the {@link RoleType} that is sent to CM may
   * include a list of role types.
   *
   * @param wsResult Web Service response to be parsed
   * @param urlToDocMap Documents whose ACLs are to be set. The keys in the map
   *          represents the document URL
   */
  private void processWsResponse(GssGetAclForUrlsResult wsResult,
      Map<String, SPDocument> urlToDocMap, WebState webState)
      throws SharepointException{
    if (wsResult == null || urlToDocMap == null) {
      return;
    }
    LOGGER.log(Level.CONFIG, "Building ACLs from the WS response. WSLog [ "
        + wsResult.getLogMessage() + " ]");
    GssAcl[] allAcls = wsResult.getAllAcls();
    if (allAcls == null || allAcls.length == 0) {
      return;
    }

    Set<UserGroupMembership> memberships = new TreeSet<UserGroupMembership>();
    Map<String, SPDocument> excludedParentUrlToDocMap = Maps.newHashMap();
    List<String> docUrlsToReprocess = Lists.newArrayList();
    Map<String, SPDocument> largeACLUrlToDocMap = Maps.newHashMap();
    List<String> largeAclUrlsToReprocess = Lists.newArrayList();
    Map<String,List<SPDocument>> reprocessDocs =
        Maps.newHashMap();

ACL: for (GssAcl acl : allAcls) {
      String entityUrl = acl.getEntityUrl();
      GssAce[] allAces = acl.getAllAce();
      if (null == entityUrl || null == allAces) {
        LOGGER.log(Level.WARNING, "Either entityUrl [ " + entityUrl
            + " ] is unavailable or No ACE found in the ACL. WSLog [ "
            + acl.getLogMessage() + " ] ");
        continue;
      }
      SPDocument document = urlToDocMap.get(entityUrl);
      if (document == null) {
        LOGGER.warning(
            "No document found in urlToDocMap map for the entityUrl [ "
            + entityUrl + " ], WSLog [ " + acl.getLogMessage() + " ] ");
        continue;
      }
      LOGGER.log(Level.CONFIG, "WsLog [ " + acl.getLogMessage() + " ] ");
      boolean allowAnonymousAccess = Boolean.parseBoolean(
          acl.getAnonymousAccess());
      if (allowAnonymousAccess) {
        LOGGER.log(Level.INFO, "Document [ " + document.getUrl()
            + " ] is identified as Public Document");
       document.setPublicDocument(allowAnonymousAccess);
       continue;
      }
      boolean largeAcl = Boolean.parseBoolean(acl.getLargeAcl());
      if (largeAcl) {
        boolean inheritPermissions =
            Boolean.parseBoolean(acl.getInheritPermissions());
        if (inheritPermissions) {
          String parentUrl = acl.getParentUrl();
          // if parentUrl is null or empty then document will be processed
          // as largeAcl document.
          if (!Strings.isNullOrEmpty(parentUrl)) {
            LOGGER.log(Level.INFO, "Document [ " + document.getUrl()
                + " ] is idenified as Large ACL but with inherit permissions, "
                + "with Parent URL as " + parentUrl);
            List<SPDocument> childList =
                reprocessDocs.get(parentUrl);
            if (childList == null) {
              childList = Lists.newArrayList();             
            }
            childList.add(document);
            reprocessDocs.put(parentUrl, childList);
            continue ACL;
          }         
        }
        LOGGER.log(Level.INFO, "Document [ " + document.getUrl()
            + " ] needs to be reprocessed as Large ACL Document");
        largeAclUrlsToReprocess.add(document.getUrl());
        largeACLUrlToDocMap.put(document.getUrl(), document);
        continue ACL;
      }
      Set<Principal> aclUsers = Sets.newHashSet();
      Set<Principal> aclGroups = Sets.newHashSet();
      Set<Principal> aclDenyUsers = Sets.newHashSet();
      Set<Principal> aclDenyGroups = Sets.newHashSet();
      document.setUniquePermissions(
          !Boolean.parseBoolean(acl.getInheritPermissions()));
      if (!Strings.isNullOrEmpty(acl.getParentUrl())) {
        if (sharepointClientContext.isIncludedUrl(acl.getParentUrl())) {
          document.setParentUrl(acl.getParentUrl());
          document.setParentId(acl.getParentId());
        } else {
          if (document.isUniquePermissions()) {
            document.setParentUrl(sharepointClientContext.getSiteURL());
            document.setParentId(acl.getParentId());
          } else {
            LOGGER.log(Level.INFO, "Document [ " + document.getUrl()
                + " ] needs to be reprocessed as Parent Url ["
                + acl.getParentUrl() + "] is not included for Traversal");
            docUrlsToReprocess.add(document.getUrl());
            excludedParentUrlToDocMap.put(document.getUrl(), document);
            continue ACL;
          }
        }
      }
      for (GssAce ace : allAces) {
        // Handle Principal
        GssPrincipal principal = ace.getPrincipal();
        if (null == principal) {
          LOGGER.log(Level.WARNING, "No Principal found in ace.");
          continue;
        }
        if (null == principal.getType() || null == principal.getName()) {
          LOGGER.log(Level.WARNING, "Either Principal Name [ "
              + principal.getName() + " ] or Principal Type [ "
              + principal.getType() + " ]  is unavailable");
          continue;
        }

        // Handle Permissions
        GssSharepointPermission permissions = ace.getPermission();
        if (null == permissions) {
          LOGGER.log(Level.WARNING, "No permissions found for Principal [ "
              + principal.getName() + " ] ");
          continue;
        }
        // Check to determine whether the object-type of the document is list
        // list-item or site.

        ObjectType objectType = ObjectType.ITEM;

        if (document.getObjType().equals(SPConstants.SITE)) {
          objectType = ObjectType.SITE_LANDING_PAGE;
        } else if (null != document.getParentList()) {
          if (document.getParentList().getPrimaryKey().equals(
              Util.getOriginalDocId(document.getDocId(),
                  document.getFeedType()))) {
            objectType = ObjectType.LIST;
          }
        }
        final String principalName = getPrincipalName(principal);
        String siteCollUrl = wsResult.getSiteCollectionUrl();
        String[] deniedPermissions = permissions.getDeniedPermission();
        if (null != deniedPermissions) {
          Set<RoleType> deniedRoleTypes =
              getRoleTypesFor(deniedPermissions, objectType);
          if (deniedRoleTypes.size() > 0) {
            LOGGER.fine("Denied Permission list "
                + Arrays.asList(permissions.getDeniedPermission())
                + " for the User " + principalName);
            LOGGER.fine("Principal [" + principalName
                + "] Denied Role Types [ " + deniedRoleTypes + " ]");
            // Pass denied permissions only if Peeker or Reader role is denied.
            if (deniedRoleTypes.contains(RoleType.PEEKER)
                || deniedRoleTypes.contains(RoleType.READER)) {
              if (supportsDenyAcls) {
                LOGGER.fine("Processing Deny permissions"
                    + " for Principal ["+ principalName + "]");
                processPrincipal(principal, aclDenyUsers, aclDenyGroups,
                    principalName, siteCollUrl, memberships, webState);
              } else {
                // Skipping ACL as denied ACLs are not supported as per
                // Traversal Context.
                LOGGER.warning("Skipping ACL as Deny permissions are detected"
                    + "for Document [" + entityUrl + "] for Principal ["
                    + principalName + " ] when Supports Deny ACL ["
                    + supportsDenyAcls + "].");
                continue ACL;
              }
            }
          }
        }
        LOGGER.fine("Permission list "
            + Arrays.asList(permissions.getAllowedPermissions())
            + " for the User " + principalName);
        Set<RoleType> allowedRoleTypes =
            getRoleTypesFor(permissions.getAllowedPermissions(), objectType);
        if (!allowedRoleTypes.isEmpty()) {
          LOGGER.fine("Principal [ "+ principalName
              + " ] Allowed Role Types [ "+ allowedRoleTypes + " ]");
          // Pass allowed permissions only if role other than Peeker is allowed.
          if (allowedRoleTypes.contains(RoleType.READER)
              || allowedRoleTypes.contains(RoleType.WRITER)
              || allowedRoleTypes.contains(RoleType.OWNER)) {
            processPrincipal(principal, aclUsers, aclGroups,
                principalName, siteCollUrl, memberships, webState);
          }
        }
      }
      document.setAclUsers(aclUsers);
      document.setAclGroups(aclGroups);
      document.setAclDenyUsers(aclDenyUsers);
      document.setAclDenyGroups(aclDenyGroups);
    }

    if (!reprocessDocs.isEmpty()) {
      for (String parentUrl : reprocessDocs.keySet()) {
        LOGGER.fine("Processing Parent URL [ "+ parentUrl + " ] ");
        SPDocument parent = new SPDocument(parentUrl, parentUrl,
            Calendar.getInstance(), ActionType.ADD);
        fetchAclForSPDocument(parentUrl, parent, webState);
        List<SPDocument> childList = reprocessDocs.get(parentUrl);
        if (childList != null) {
          for(SPDocument child : childList) {
            copyAcls(parent, child);
          }
        }
      }
    }

    if (!largeAclUrlsToReprocess.isEmpty()) {
      for (String largeACLUrl : largeAclUrlsToReprocess) {
        SPDocument documentToPass = largeACLUrlToDocMap.get(largeACLUrl);
        if (documentToPass != null) {
          fetchAclForSPDocument(largeACLUrl, documentToPass, webState);
        }
      }
    }

    if (!docUrlsToReprocess.isEmpty()) {
      String[] arrToPass = new String[docUrlsToReprocess.size()];
      docUrlsToReprocess.toArray(arrToPass);
      fetchAclForDocumentsWithExcludedParents(arrToPass,
          excludedParentUrlToDocMap, webState);
    }

    if (null != sharepointClientContext.getUserDataStoreDAO()) {
      try {
        sharepointClientContext.getUserDataStoreDAO().addMemberships(
            memberships);
      } catch (Exception e) {
        LOGGER.log(Level.WARNING, "Failed to add #" + memberships.size()
            + " memberships in user data store. ", e);
      }
    }
  }
 
  /**
   * Utility method to copy ACLs.
   * @param source source SPDocument for copy
   * @param target target SPDocument for copy
   */
  private void copyAcls(SPDocument source, SPDocument target) {
    target.setAclUsers(source.getAclUsers());
    target.setAclGroups(source.getAclGroups());
    target.setAclDenyUsers(source.getAclDenyUsers());
    target.setAclDenyGroups(source.getAclDenyGroups());
  }

  private static class SPBasePermissions {
    public static final String EMPTYMASK = "EmptyMask";
    public static final String VIEWLISTITEMS = "ViewListItems";
    public static final String ADDLISTITEMS = "AddListItems";
    public static final String EDITLISTITEMS = "EditListItems";
    public static final String DELETELISTITEMS = "DeleteListItems";
    public static final String APPROVEITEMS = "ApproveItems";
    public static final String OPENITEMS = "OpenItems";
    public static final String VIEWVERSIONS = "ViewVersions";
    public static final String DELETEVERSIONS = "DeleteVersions";
    public static final String CANCELCHECKOUT = "CancelCheckout";
    public static final String MANAGEPERSONALVIEWS = "ManagePersonalViews";
    public static final String MANAGELISTS = "ManageLists";
    public static final String VIEWFORMPAGES = "ViewFormPages";
    public static final String OPEN = "Open";
    public static final String VIEWPAGES = "ViewPages";
    public static final String ADDANDCUSTOMIZEPAGES = "AddAndCustomizePages";
    public static final String APPLYTHEMEANDBORDER = "ApplyThemeAndBorder";
    public static final String APPLYSTYLESHEETS = "ApplyStyleSheets";
    public static final String VIEWUSAGEDATA = "ViewUsageData";
    public static final String CREATESSCSITE = "CreateSSCSite";
    public static final String MANAGESUBWEBS = "ManageSubwebs";
    public static final String CREATEGROUPS = "CreateGroups";
    public static final String MANAGEPERMISSIONS = "ManagePermissions";
    public static final String BROWSEDIRECTORIES = "BrowseDirectories";
    public static final String BROWSEUSERINFO = "BrowseUserInfo";
    public static final String ADDDELPRIVATEWEBPARTS = "AddDelPrivateWebParts";
    public static final String UPDATEPERSONALWEBPARTS = "UpdatePersonalWebParts";
    public static final String MANAGEWEB = "ManageWeb";
    public static final String USECLIENTINTEGRATION = "UseClientIntegration";
    public static final String USEREMOTEAPIS = "UseRemoteAPIs";
    public static final String MANAGEALERTS = "ManageAlerts";
    public static final String CREATEALERTS = "CreateAlerts";
    public static final String EDITMYUSERINFO = "EditMyUserInfo";
    public static final String ENUMERATEPERMISSIONS = "EnumeratePermissions";
    public static final String FULLMASK = "FullMask";
  }

  /**
   * Maps a set of SharePoint defined permissions to CM defined permissions.
   * TODO: The logic used for mapping could be improved depending on the current
   * discussions going on at this front with John Felton.
   *
   * @param permissions SharePoint Permissions
   * @param objectType Kind of entity (List/List-Item/Web) for which the mapping
   *          is to be done. This is required because SharePoint defines
   *          multiple granular permissions for various entity types and all
   *          these permissions may not be applicable to all the entities. For
   *          Example, "ManageWeb" has nothing do with ListItems.
   * @return a list of {@link RoleType}
   */
  private Set<RoleType> getRoleTypesFor(String[] permissions,
      ObjectType objectType) {
    Set<RoleType> roleTypes = Sets.newHashSet();
    if (null == permissions || permissions.length == 0 || null == objectType) {
      return roleTypes;
    }
    if (permissions.length == 0
        || (permissions.length == 1 && permissions[0].equals(SPBasePermissions.EMPTYMASK))) {
      return roleTypes;
    }

    // The following two flags are to check if all the required permissions
    // for WRITER access on a list are fulfilled or not. We may need to add
    // more flags in future corresponding to any extra permissions that we
    // agree to check to give a user WRITER access on a list
    boolean managelist = false;
    boolean additems = false;

    // For checking Limited Access permission which will be mapped to the
    // PEEKER in CM
    boolean viewFormPages = false;
    boolean open = false;
    // flags to check all the required permissions for READER access on
    // SharePoint List or Document Library.
    boolean viewPages = false;
    boolean viewListItems = false;

    for (String permission : permissions) {
      if (SPBasePermissions.FULLMASK.equals(permission)) {
        roleTypes.add(RoleType.OWNER);
      }

      if (SPBasePermissions.VIEWFORMPAGES.equals(permission)) {
        viewFormPages = true;
      }
      if (SPBasePermissions.OPEN.equals(permission)) {
        open = true;
      }

      if (ObjectType.ITEM.equals(objectType)) {
        if (SPBasePermissions.EDITLISTITEMS.equals(permission)) {
          roleTypes.add(RoleType.WRITER);
        }
        if (SPBasePermissions.VIEWLISTITEMS.equals(permission)) {
          roleTypes.add(RoleType.READER);
        }
      } else if (ObjectType.LIST.equals(objectType)) {
        if (!managelist && SPBasePermissions.MANAGELISTS.equals(permission)) {
          managelist = true;
        }
        if (!additems && SPBasePermissions.ADDLISTITEMS.equals(permission)) {
          additems = true;
        }
        if (SPBasePermissions.VIEWPAGES.equals(permission)) {
          viewPages = true;
        }
        if (SPBasePermissions.VIEWLISTITEMS.equals(permission)) {
          viewListItems = true;
        }
      } else if (ObjectType.SITE_LANDING_PAGE.equals(objectType)) {
        if (SPBasePermissions.EDITLISTITEMS.equals(permission)) {
          roleTypes.add(RoleType.WRITER);
        }
        if (SPBasePermissions.VIEWLISTITEMS.equals(permission)) {
          roleTypes.add(RoleType.READER);
        }
      }
      // Currently, only list and list-items are fed as documents. In
      // future, if sites and pages are also sent, more checks will have
      // to be added here
    }
    if (ObjectType.LIST.equals(objectType) && viewPages && viewListItems) {
      roleTypes.add(RoleType.READER);
    }    
    if (ObjectType.LIST.equals(objectType) && managelist && additems) {
      roleTypes.add(RoleType.WRITER);
    }
    if (viewFormPages && open) {
      roleTypes.add(RoleType.PEEKER);
    }
    return roleTypes;
  }

  /**
   * Method to process GssAcl principals.
   *
   * @param principal GssPrincipal Object to process
   * @param users set to add user principals to
   * @param groups set to add group principals to
   * @param principalName Principal Name
   * @param webStateUrl Site Collection Url from WebState
   * @param memberships UserGroup Membership object
   */
  @VisibleForTesting
  void processPrincipal(GssPrincipal principal, Set<Principal> users,
      Set<Principal> groups, String principalName, String webStateUrl,
      Set<UserGroupMembership> memberships, WebState webState) {
    String globalNamespace = sharepointClientContext.getGoogleGlobalNamespace();
    String localNamespace = sharepointClientContext.getGoogleLocalNamespace();
    if (PrincipalType.USER.equals(principal.getType())) {
      users.add(new Principal(SpiConstants.PrincipalType.UNKNOWN,
              globalNamespace, principalName,
              CaseSensitivityType.EVERYTHING_CASE_INSENSITIVE));
    } else if (PrincipalType.DOMAINGROUP.equals(principal.getType())) {
      groups.add(new Principal(SpiConstants.PrincipalType.UNKNOWN,
              globalNamespace, principalName,
              CaseSensitivityType.EVERYTHING_CASE_INSENSITIVE));
    } else if (PrincipalType.SPGROUP.equals(principal.getType())) {
      groups.add(Util.getSharePointGroupPrincipal(localNamespace,
          webStateUrl, principalName));

      // If it's a SharePoint group, add the membership info
      // into the User Data Store
      if (PrincipalType.SPGROUP.equals(principal.getType())
          && null != sharepointClientContext.getUserDataStoreDAO()) {
        GssPrincipal[] members = principal.getMembers();
        if (members == null || members.length == 0) {
          // If SharePoint group does not have any members
          // then such groups will be resolved later.
          webState.addSPGroupToResolve (Integer.toString(principal.getID()));        
        } else {
          for (GssPrincipal member : members) {
            memberships.add(new UserGroupMembership(member.getID(),
                getPrincipalName(member), principal.getID(), principalName,
                webStateUrl));
          }
        }
      }
    } else {
      LOGGER.log(Level.WARNING, "Skipping ACE for principal [ "
          + principal.getName() + " ] because its type [ "
          + principal.getType() + " ] is unknown");
    }
  }

  /**
   * Returns user/group name in the format as specified in as specified in
   * the connector configuration page.
   *
   * @param principal the principal used to get the user/group name from
   * @return a string that represents the user/group name in the appropriate format
   */
  /*
   * Marked public of JUnit test case GssAclTest.testGetPrincipalName.
   */
  @VisibleForTesting
  public String getPrincipalName(GssPrincipal principal) {
    String principalname = Util.getUserFromUsername(principal.getName());
    final String domain = Util.getDomainFromUsername(principal.getName());
    String domainStringConst = SPConstants.DOMAIN_CONSTANT_IN_ACL;

    if (null != domain) {
      if (PrincipalType.USER.equals(principal.getType())) {
        String usernameFormatInAcl = sharepointClientContext.getUsernameFormatInAce();
        if (null != usernameFormatInAcl
            && usernameFormatInAcl.trim().length() > 0) {
          if (principalname.contains(domainStringConst)) {
            usernameFormatInAcl = usernameFormatInAcl.replace(domainStringConst, domainStringConst = "_"
                + domainStringConst);
          }
          principalname = usernameFormatInAcl.replace(SPConstants.USERNAME_CONSTANT_IN_ACL, principalname);
          principalname = principalname.replace(domainStringConst, domain);
        }
      } else if (PrincipalType.DOMAINGROUP.equals(principal.getType())
          || PrincipalType.SPGROUP.equals(principal.getType())) {
        String groupnameFormatInAcl = sharepointClientContext.getGroupnameFormatInAce();
        if (null != groupnameFormatInAcl
            && groupnameFormatInAcl.trim().length() > 0) {
          if (principalname.contains(domainStringConst)) {
            groupnameFormatInAcl = groupnameFormatInAcl.replace(domainStringConst, domainStringConst = "_"
                + domainStringConst);
          }
          principalname = groupnameFormatInAcl.replace(SPConstants.GROUPNAME_CONSTANT_IN_ACL, principalname);
          principalname = principalname.replace(domainStringConst, domain);
        }
      }
    }
    return principalname;
  }

  /**
   * Gets a set of documents in the form of {@link SPDocumentList} crawled from
   * a single SharePoint site {@link WebState} and fetches ACL for all the
   * documents and set it the document's properties.
   *
   * @param resultSet {@link SPDocumentList} to be processed
   * @param webState parent {@link WebState} from which documents have been
   *          crawled
   */
  public void fetchAclForDocuments(SPDocumentList resultSet, WebState webState)
      throws SharepointException {
    if (!sharepointClientContext.isPushAcls() || null == resultSet) {
      return;
    }
    List<SPDocument> documents = resultSet.getDocuments();
    if (null != documents) {
      Map<String, SPDocument> urlToDocMap = Maps.newHashMap();
      String[] allUrlsForAcl = new String[resultSet.size()];
      int i = 0;
      for (SPDocument document : documents) {
        if (document.isWebAppPolicyDoc()) {
          LOGGER.log(Level.FINEST,
              "Skipping Web application policy DOC for ACL Fetch "
              + document.getUrl());
          continue;
        }
        urlToDocMap.put(document.getUrl(), document);
        allUrlsForAcl[i++] = document.getUrl();
      }
      LOGGER.log(Level.CONFIG, "Getting ACL for #" + urlToDocMap.size()
          + " entities crawled from site [ " + webState.getWebUrl()
          + " ]. Document list : " + resultSet.toString());
      GssGetAclForUrlsResult wsResult = getAclForUrls(allUrlsForAcl,
          supportsInheritedAcls, !supportsInheritedAcls);
      processWsResponse(wsResult, urlToDocMap, webState);
    }
  }

   /**
   * Works similar to
   * {@link ListsHelper#getListItems(ListState, java.util.Calendar, String, Set)}
   * but is designed to be used only to get those list items whose ACLs have
   * changed because of any security change at parent level.
   *
   * @param listState The list from which the items are to be retrieved
   * @param listsHelper The lists helper for parsing the web service response
   * @return a list of {@link SPDocument}
   */
  public List<SPDocument> getListItemsForAclChangeAndUpdateState(
      ListState listState, ListsHelper listsHelper) {
    List<SPDocument> aclChangedDocs = null;
    List<SPDocument> attachmentsForChangedDocs = new ArrayList<SPDocument>();
    if (sharepointClientContext.isPushAcls() && listState.isAclChanged()) {
      GssGetListItemsWithInheritingRoleAssignments wsResult =
          GetListItemsWithInheritingRoleAssignments(listState.getPrimaryKey(),
          String.valueOf(listState.getLastDocIdCrawledForAcl()));
      if (null != wsResult) {
        aclChangedDocs = listsHelper.parseCustomWSResponseForListItemNodes(
            wsResult.getDocXml(), listState);
        if (null != aclChangedDocs) {
          LOGGER.log(Level.INFO, "Found " + aclChangedDocs.size()
              + " documents from list [ " + listState
              + " ] under ACL based crawling. Crawling status: FromID [ "
              + listState.getLastDocIdCrawledForAcl() + " ], ToID [ "
              + wsResult.getLastIdVisited() + " ], moreDocs [ "
              + wsResult.isMoreDocs() + " ] ");
          boolean lookForAttachment = listState.canContainAttachments();
          for (SPDocument document : aclChangedDocs) {
            document.setForAclChange(true);
            if (lookForAttachment) {
              // Get attachments for ACL changed documents
              attachmentsForChangedDocs.addAll(
                  listsHelper.getAttachments(listState, document));
            }
          }
        }

        if (attachmentsForChangedDocs.size() > 0) {
          for(SPDocument attachment : attachmentsForChangedDocs) {
            attachment.setForAclChange(true);
          }
          aclChangedDocs.addAll(attachmentsForChangedDocs);
        }

        if (wsResult.isMoreDocs()) {
          listState.updateAclCrawlStatus(true, wsResult.getLastIdVisited());
        } else {
          SPDocument listDoc = listState.getDocumentInstance(sharepointClientContext.getFeedType());
          listDoc.setForAclChange(true);
          aclChangedDocs.add(listDoc);
          if (null != aclChangedDocs && aclChangedDocs.size() > 0) {
            // We have crawled the last set of documents and there
            // are
            // no more documents to be crawled. However, we can not
            // say
            // listState.endAclCrawl() at this point because the
            // crawled
            // documents are not yet fed to GSA. Once these
            // documents
            // get fed, we'll call listState.commitAclCrawlStatus()
            // and
            // the state will be updated with the same effect as if
            // we
            // have
            // called listState.endAclCrawl().
            listState.updateAclCrawlStatus(false, 0);
          } else {
            // Since, the current crawled not return any document
            // and
            // also, there are no more documents to be crawled, we
            // can
            // safely end the ACL crawl for this list.
            listState.endAclCrawl();
          }
        }
      }
    }
    return aclChangedDocs;
  }

  /**
   * Executes GetAclChangesSinceToken() web method of GssAcl web service Used
   * for ACL change detection; change token is used for synchronization purpose.
   *
   * @param webstate The {@link WebState} for which change detection is to be
   *          done
   * @return web service response {@link GssGetAclChangesSinceTokenResult} as it
   *         is
   */
  private GssGetAclChangesSinceTokenResult getAclChangesSinceToken(
      final WebState webstate) {
    return Util.makeWSRequest(sharepointClientContext, aclWS,
        new Util.RequestExecutor<GssGetAclChangesSinceTokenResult>() {
      public GssGetAclChangesSinceTokenResult onRequest(final BaseWS ws)
          throws Throwable {
        return ((AclWS) ws).getAclChangesSinceToken(
            webstate.getAclChangeTokenForWsCall(),
            webstate.getNextAclChangeToken());
      }
     
      public void onError(final Throwable e) {
        LOGGER.log(Level.WARNING, "ACL change detection has failed.", e);
      }
    });
  }

  public void fetchAclChangesSinceTokenAndUpdateState(WebState webState) {
    if (!sharepointClientContext.isPushAcls()) {
      LOGGER.log(Level.INFO,
          "Not performing ACL change detection because PushAcls is false.");
      return;
    }

    // Do not initiate ACL change detection if all the list states have not
    // yet been processed for the previously detected ACl changes
    for (ListState listState : webState.getAllListStateSet()) {
      if (listState.isAclChanged() && !listState.isNoCrawl()) {
        LOGGER.log(Level.INFO,
            "Not performing ACL change detection because List [" + listState
            + "] is still pending for ACL change from previous run.");
        return;
      }
    }

    // Commit the cached change token to be used for subsequent change
    // detections before initiating the change detection
    if (null != webState.getNextAclChangeToken()
        && webState.getNextAclChangeToken().trim().length() != 0) {
      webState.commitAclChangeToken();
    }

    LOGGER.log(Level.CONFIG, "Initiating ACL Change detection for web [ "
        + webState.getWebUrl() + " ] from change token [ "
        + webState.getAclChangeTokenForWsCall());
    GssGetAclChangesSinceTokenResult wsResult = getAclChangesSinceToken(webState);
    processWsResponse(wsResult, webState);
  }

  /**
   * Analyze the set of changes returned by the Custom ACL web service and
   * update the status of child ListStates reflecting the way crawl should
   * proceed for them. Typically, any permission change at Web level will
   * trigger a re-crawl of all the list and items which are inheriting role
   * assignments.
   *
   * @param wsResult @link{GssGetAclChangesSinceTokenResult}
   * @param webstate The {@link WebState} for which the change detection is
   *          being done
   */
  private void processWsResponse(GssGetAclChangesSinceTokenResult wsResult,
      WebState webstate) {
    if (null == wsResult || null == webstate) {
      return;
    }
    LOGGER.log(Level.CONFIG, "Processing the received ACL changes. WsLog [ "
        + wsResult.getLogMessage() + " ]");
    GssAclChangeCollection allChanges = wsResult.getAllChanges();
    GssAclChange[] changes = (null == allChanges) ? null
        : allChanges.getChanges();

    if (null == changes) {
      return;
    }
    LOGGER.log(Level.CONFIG, "Total changes to be oprocessed # "
        + changes.length + " . WsLog [ " + allChanges.getLogMessage() + " ]");
    // If permissions of the Web has changed due to role assignment changes
    // at its first unique ancestor
    boolean isWebChanged = false;

    // If the Web has been reset to initiate a re-crawl due to a high level
    // permission change like security policy change
    boolean isWebReset = false;

    // To keep track of all the lists which have been processed. This is to
    // avoid re-processing of the same list due to multiple changes
    Set<ListState> processedLists = Sets.newHashSet();

    // All groups where there are some membership changes
    // TODO: why not this is integer?
    Set<String> changedGroups = new TreeSet<String>();
    Set<Integer> deletedGroups = new TreeSet<Integer>();
    Set<Integer> deletedUsers = new TreeSet<Integer>();
    for (GssAclChange change : changes) {
      if (null == change) {
        continue;
      }
      ObjectType objType = change.getChangedObject();
      SPChangeType changeType = change.getChangeType();
      String changeObjectHint = change.getHint();
      if (!change.isIsEffectiveInCurrentWeb()) {
        LOGGER.log(Level.CONFIG, "Change changeType [ "
            + changeType
            + " ], objectType [ "
            + objType
            + " ] is not applicable to the current web. skipping tio the next change...");
        continue;
      }
      LOGGER.log(Level.CONFIG, "Change detected changeType [ " + changeType
          + " ], objectType [ " + objType + " ]. ");

      if (objType == ObjectType.SECURITY_POLICY) {
        LOGGER.log(Level.INFO, "Policy Change under web [ "
            + webstate.getWebUrl() + " ]");
         // With inherited ACL support no need to re-crawl entire Web.
         // Web Application policy is represented by a separate document
         // which will be processed by
         // SharePointClient.java --> processSiteData.
        if (supportsInheritedAcls) {
          webstate.setWebApplicationPolicyChange(true);
        } else {
          LOGGER.log(Level.INFO, "Resetting all list states under web [ "
              + webstate.getWebUrl()
              + " ] because web application policy change detected.");
          webstate.resetState();
          isWebReset = true;
        }
       } else if (objType == ObjectType.WEB) {
         if (changeType == SPChangeType.AssignmentDelete) {
          // Typically, deletion of a role affects the ACL of only
          // those entities down the hierarchy which are inheriting
          // the permission. But, limited access is a special case
          // where the ACL of all entities gets affected. Since, we do
          // not know what permission has been deleted, we have to
          // consider the worst case scenario and assume that the
          // deleted role was of limited access.
          LOGGER.log(Level.INFO, "Resetting all list states under web [ "
              + webstate.getWebUrl()
              + " ] because some role has been deleted and the deleted role could be Limited Access.");
          webstate.resetState();
          webstate.setWebApplicationPolicyChange(true);  
          isWebReset = true;
        } else if (!isWebChanged){
          // With inherited ACL support no need to re-crawl
          // all inheriting Lists.
          // Web Permissions are associated with Web home Page.
          // just marking web home page for re-crawl.
          // TODO : Need to change setWebApplicationPolicyChange
          // to something like setRevisitWebHome.
        webstate.setWebApplicationPolicyChange(true);
          if (supportsInheritedAcls) {
            LOGGER.log(Level.INFO, "Change in Web Permissions - Reseting Site home Page");
            webstate.setWebApplicationPolicyChange(true);
            for (ListState list : webstate.getAllListStateSet()) {
              if (list.isApplyReadSecurity() && list.isInheritedSecurity()) {
                LOGGER.log(Level.INFO, "Change in Web Permissions -"
                    + " Resetting list state URL [ "
                    + list.getListURL()
                    + " ] because effective permisssions modified"
                    + " for List with Read Security");
                list.resetState();
              }
            }
          } else {
            isWebChanged = true;
            // Since, role assignment at web have changed, we need to
            // re-crawl all the list/items which are inheriting the
            // changed role assignments.
            for (ListState listState : webstate.getAllListStateSet()) {
              if (!listState.isInheritedSecurity()) {
                LOGGER.log(Level.INFO, "Skipping List [ "
                    + listState
                    + " ] as it does not inherit its permission");
                continue;
              }
             
              if (listState.isNoCrawl()) {
                LOGGER.log(Level.INFO, "Skipping List [ "
                    + listState
                    + " ] as it is marked for no crawl");
                continue;
              }
             
              if (!processedLists.contains(listState)) {
                LOGGER.log(Level.INFO, "Marking List [ "
                    + listState
                    + " ] as a candidate for ACL based crawl because the effective ACL at this list have been updated. All the items with inheriting permissions wil be crawled from this list.");
                listState.startAclCrawl();
                processedLists.add(listState);
              }
            }
          }
        }
      } else if (objType == ObjectType.LIST && null != changeObjectHint) {
        ListState listState = webstate.getListStateForGuid(changeObjectHint);
        if (null == listState) {
          LOGGER.log(Level.WARNING, "Changed List ID [ "
              + changeObjectHint
              + " ] is not found in the WebState. Skipping to the next change.");
          continue;
        }

        if (changeType == SPChangeType.AssignmentDelete) {
          // Assuming the worst case scenario of Limited Access
          // deletion
          LOGGER.log(Level.INFO, "Resetting list state URL [ "
              + listState
              + " ] because some role has been deleted and the deleted role"
              + " could be Limited Access.");
          listState.resetState();
        } else {
          if (supportsInheritedAcls) {
            // Revisit List home for ACL changes.
            listState.markListToRevisitListHome(sharepointClientContext.getFeedType());
            if (listState.isApplyReadSecurity()) {
              LOGGER.log(Level.INFO, "Resetting list state URL [ "
                  + listState.getListURL()
                  + " ] because effective permisssions modified"
                  + " for List with Read Security");
              listState.resetState();
            }
          } else {
            if (!processedLists.contains(listState)) {
              LOGGER.log(Level.INFO, "Marking List [ "
                  + listState
                  + " ] as a candidate for ACL based crawl because the effective"
                  + " ACL at this list have been updated. All the items with"
                  + " inheriting permissions wil be crawled from this list.");
              listState.startAclCrawl();
              processedLists.add(listState);
            }
          }
        }
      } else if (objType == ObjectType.USER
      // For user-related changes, we only consider deletion changes.
      // Rest all are covered as part of web/list/item/group
      // specific changes. Refer to the WS impl. for more details
          && changeType == SPChangeType.Delete) {
        try {
          deletedUsers.add(new Integer(changeObjectHint));
        } catch (Exception e) {
          LOGGER.log(Level.WARNING, "UserId [ " + changeObjectHint
              + " ] is invalid. skipping... ", e);
          continue;
        }

        // TODO: Even if the user is not known to the user data store,
        // we would proceed here. This is because, the user data store,
        // currently, stores only those users who are member of some
        // groups. A re-crawl due to user deletion can be avoided
        // by storing all the SharePoint users (sent into ACLs in past)
        // in the local data store.

        LOGGER.log(Level.INFO, "Resetting all list states under web [ "
            + webstate.getWebUrl()
            + " ] because a user has been deleted from the SharePoint.");
        webstate.resetState();
        isWebReset = true;
        // TODO: A re-crawl due to User deletion can be avoided
        // by storing more ACL information in the local data
        // store.
      }
      // Administrators are treated as another SPGroup
      else if (objType == ObjectType.GROUP
          || objType == ObjectType.ADMINISTRATORS) {
        if (changeType == SPChangeType.Delete) {
          try {
            deletedGroups.add(Integer.parseInt(changeObjectHint));
          } catch (Exception e) {
            LOGGER.log(Level.WARNING, "GroupId [ " + changeObjectHint
                + " ] is invalid. skipping... ", e);
            continue;
          }
          // TODO: A re-crawl due to Group deletion can be avoided
          // by storing more ACL information in the local data
          // store.
          webstate.resetState();
          isWebReset = true;
        } else {
          changedGroups.add(changeObjectHint);
          // Mark Web Application Policy Change to track Site Admin
          // Change also.
          if (objType == ObjectType.ADMINISTRATORS) {
            webstate.setWebApplicationPolicyChange(true);
          }
        }
      }

      if (isWebReset) {
        webstate.setWebApplicationPolicyChange(true);
        break;
      }
    }

    // Sync the membership of all changed groups
    syncGroupMembership(deletedUsers, deletedGroups, changedGroups, wsResult.getSiteCollectionUrl());

    if (null == webstate.getNextAclChangeToken()
        || webstate.getNextAclChangeToken().trim().length() == 0) {
      webstate.setNextAclChangeToken(allChanges.getChangeToken());
    }
  }

  /**
   * Updates all the deleted/changed user group membership information into the
   * user data store.
   *
   * @param deletedUsers
   * @param deletedGroups
   * @param changedGroups
   * @param siteCollectionUrl
   */
  private void syncGroupMembership(Set<Integer> deletedUsers,
      Set<Integer> deletedGroups, Set<String> changedGroups,
      String siteCollectionUrl) {
    if (null == sharepointClientContext.getUserDataStoreDAO()) {
      return;
    }

    if (null != deletedUsers && deletedUsers.size() > 0) {
      try {
        sharepointClientContext.getUserDataStoreDAO().removeUserMembershipsFromNamespace(deletedUsers, siteCollectionUrl);
      } catch (Exception e) {
        LOGGER.log(Level.WARNING, "Failed to remove user memberships from namespace [ "
            + siteCollectionUrl + " ] ");
      }
    }

    if (null != deletedGroups && deletedGroups.size() > 0) {
      try {
        sharepointClientContext.getUserDataStoreDAO().removeGroupMembershipsFromNamespace(deletedGroups, siteCollectionUrl);
      } catch (Exception e) {
        LOGGER.log(Level.WARNING, "Failed to remove group memberships from namespace [ "
            + siteCollectionUrl + " ] ");
      }
    }

    if (null != changedGroups && changedGroups.size() > 0) {
      try {
        Map<Integer, Set<UserGroupMembership>> groupToMemberships = processChangedGroupsToSync(changedGroups);
        if (null != groupToMemberships && groupToMemberships.size() > 0) {
          try {
            sharepointClientContext.getUserDataStoreDAO().syncGroupMemberships(groupToMemberships, siteCollectionUrl);
          } catch (Exception e) {
            LOGGER.log(Level.WARNING, "Failure while syncing memberships from namespace [ "
                + siteCollectionUrl + " ]");
          }
        }
      } catch (Exception e) {
        LOGGER.log(Level.WARNING, "Failed to update/sync group memberships from namespace [ "
            + siteCollectionUrl + " ] ");
      }
    }
  }

  /**
   * Resolves a set of groups identified by their IDs and returns a map
   * <groupID, latest_memberships>. This is useful when a group has been changed
   * and its membership is to be re-synced with the user data store.
   *
   * @param changedGroups IDs of the groups that is to be resolved
   * @return a map of group ID to latest memberships, the map could be
   *         empty but not null.
   */
  private Map<Integer, Set<UserGroupMembership>> processChangedGroupsToSync(
      Set<String> changedGroups) {
    Map<Integer, Set<UserGroupMembership>> groupsToMemberships =
        Maps.newHashMap();
    if (null != changedGroups && changedGroups.size() > 0) {
      String[] groupIds = new String[changedGroups.size()];
      changedGroups.toArray(groupIds);
      GssResolveSPGroupResult wsResult = resolveSPGroup(groupIds);
      if (null != wsResult) {
        GssPrincipal[] groups = wsResult.getPrinicpals();
        if (null != groups && groups.length > 0) {
          for (GssPrincipal group : groups) {
            Set<UserGroupMembership> memberships = new TreeSet<UserGroupMembership>();
            for (GssPrincipal member : group.getMembers()) {
              memberships.add(new UserGroupMembership(member.getID(),
                  getPrincipalName(member), group.getID(), group.getName(),
                  wsResult.getSiteCollectionUrl()));
            }
            groupsToMemberships.put(group.getID(), memberships);
          }
        }
      }
    }
    return groupsToMemberships;
  }

  /**
   * Executes GetAffectedItemIDsForChangeList() web method of GssAcl web
   * service. Used for getting all the Item IDs which are inheriting their role
   * assignments from the parent List.
   *
   * @param listGuid GUID of the List to be processed
   * @return Item IDs which are inheriting their role assignments from their
   *         parent list whose GUID was passed in the argument
   */
  private GssGetListItemsWithInheritingRoleAssignments GetListItemsWithInheritingRoleAssignments(
      final String listGuid, String lastItemId) {
    final int intLastItemId = Util.parseNumeric(lastItemId, 0);
    if (0 == intLastItemId) {
      LOGGER.finest("The incoming lastItemId [ " + lastItemId
          + " ] is not of a list item.");
    }

    return Util.makeWSRequest(sharepointClientContext, aclWS,
        new Util.RequestExecutor<
            GssGetListItemsWithInheritingRoleAssignments>() {
      public GssGetListItemsWithInheritingRoleAssignments onRequest(
          final BaseWS ws) throws Throwable {
        return ((AclWS) ws).getListItemsWithInheritingRoleAssignments(
            listGuid, sharepointClientContext.getBatchHint(), intLastItemId);
      }
     
      public void onError(final Throwable e) {
        LOGGER.log(Level.WARNING,
            "Failed to get ListItems With Inheriting RoleAssignments.", e);
      }
    });
  }

  /**
   * Executes GetAffectedListIDsForChangeWeb() web method of GssAcl web service.
   * Used for getting all the List IDs which are inheriting their role
   * assignments from the parent web site.
   *
   * @return List IDs which are inheriting their role assignments from their
   *         parent web site whose ID was passed in the argument
   */
  private String[] getListsWithInheritingRoleAssignments() {
    return Util.makeWSRequest(sharepointClientContext, aclWS,
        new Util.RequestExecutor<String[]>() {
      public String[] onRequest(final BaseWS ws) throws Throwable {
        return ((AclWS) ws).getListsWithInheritingRoleAssignments();
      }
     
      public void onError(final Throwable e) {
        LOGGER.log(Level.WARNING,
            "Failed to get List With Inheriting RoleAssignments.", e);
      }
    });
  }

  /**
   * Executes ResolveSPGroup() web method of GssAcl web service. Used for
   * expanding SharePoint groups to get the members.
   *
   * @param groupIds IDs of the SP Groups to be resolved
   * @return web service response {@link GssResolveSPGroupResult} as it is
   */
  public GssResolveSPGroupResult resolveSPGroup(final String[] groupIds) {
    return Util.makeWSRequest(sharepointClientContext, aclWS,
        new Util.RequestExecutor<GssResolveSPGroupResult>() {
      public GssResolveSPGroupResult onRequest(final BaseWS ws)
          throws Throwable {
        return ((AclWS) ws).resolveSPGroupInBatch(groupIds,
            sharepointClientContext.getGroupResolutionBatchSize());
      }
     
      public void onError(final Throwable e) {
        LOGGER.log(Level.WARNING,
            "Call to resolveSPGroupInBatch call failed.", e);
      }
    });
  }
 
  /**
   * Resolves SharePoint Groups associated with WebState and stores
   * User Group mapping in local database. Returns true if all groups
   * are resolved without error. Returns false in case of error.
   *
   * @param webState WebState for which groups needs to be resolved
   * @return True if group resolution is successful,
   *         False if group resolution failed
   */
  public boolean resolveSharePointGroups(WebState webState) {
    // Return true if there are no groups to resolve
    if (webState.getSPGroupsToResolve() == null ||
        webState.getSPGroupsToResolve().isEmpty()) {
      return true;
    }   
    try {
      while (!webState.getSPGroupsToResolve().isEmpty()) {
        int size = webState.getSPGroupsToResolve().size();
        String[] groupIds = new String[webState.getSPGroupsToResolve().size()];
        webState.getSPGroupsToResolve().toArray(groupIds);
        GssResolveSPGroupResult result = resolveSPGroup(groupIds);
        // Null check for result. Return false if result is null.
        if (result == null) {
          LOGGER.warning("Group Resolution null for WebState[ "
              + webState.getWebUrl() + " ]. Returning false");
          return false;
        }
        GssPrincipal[] groups = result.getPrinicpals();
        if (groups !=null && groups.length > 0) {
          Set<UserGroupMembership> memberships =
              new TreeSet<UserGroupMembership>();
          for (GssPrincipal group : groups) {        
            for (GssPrincipal member : group.getMembers()) {
              memberships.add(new UserGroupMembership(member.getID(),
                  getPrincipalName(member), group.getID(), group.getName(),
                  result.getSiteCollectionUrl()));
            }
            webState.removeSPGroupToResolve(Integer.toString(group.getID()));
          }
          if (!memberships.isEmpty() &&
              sharepointClientContext.getUserDataStoreDAO() != null) {
            try {
              sharepointClientContext.getUserDataStoreDAO().addMemberships(
                  memberships);
            } catch (Exception e) {
              LOGGER.log(Level.WARNING, "Failed to add #" + memberships.size()
                  + " memberships in user data store.", e);
              return false;
            }
          }
        }
        if (webState.getSPGroupsToResolve().isEmpty()) {
          LOGGER.info("Group resolution complete for WebState [ " +
              webState.getWebUrl()
              +" ]");
        } else {
          if (webState.getSPGroupsToResolve().size() < size) {
            LOGGER.info("Need to revisit group resolution for WebState [ "
                + webState.getWebUrl()
                + " ] as all the groups are not resolved in current batch."
                + " Remaining: " + webState.getSPGroupsToResolve().toString());
          } else {
            LOGGER.warning("Unable to resolve following IDs: "
                + webState.getSPGroupsToResolve().toString());
            // returning true because we don't want to revisit this
            return true;
          }
        }
      }
      return true;
    } catch (Exception ex) {
      LOGGER.log(Level.WARNING,
          "Group Resolution failed for WebState[ "
              + webState.getWebUrl() + " ]. Returning false",ex);
      return false;
    }
  }

  /**
   * Construct SPDocument object for representing Web application policy
   * ACL information
   */
  public SPDocument getWebApplicationPolicy(WebState webState,
      String strFeedType) {
    GssGetAclForUrlsResult result = Util.makeWSRequest(
        sharepointClientContext, aclWS,
        new Util.RequestExecutor<GssGetAclForUrlsResult>() {
      public GssGetAclForUrlsResult onRequest(final BaseWS ws)
          throws Throwable {
        return ((AclWS) ws).getAclForWebApplicationPolicy();
      }

      public void onError(final Throwable e) {
        LOGGER.log(Level.WARNING,
            "Call to getAclForWebApplicationPolicy failed.", e);
      }
    });

    FeedType feedType = FeedType.getFeedType(strFeedType);
    SPDocument webAppPolicy = null;
    if (result == null) {
      return webAppPolicy;
    }
    String siteCollectionUrlToUse;
    if (sharepointClientContext.isIncludedUrl(result.getSiteCollectionUrl())) {
      siteCollectionUrlToUse = result.getSiteCollectionUrl();
    } else {
      LOGGER.log(Level.INFO,
          "Changing web app policy URL to connector URL ["
          + sharepointClientContext.getSiteURL() + "] as policy URL [ "
          + result.getSiteCollectionUrl() + " ] is not included.");
      siteCollectionUrlToUse = sharepointClientContext.getSiteURL();
    }
    String docID = siteCollectionUrlToUse;
    if (feedType == FeedType.CONTENT_FEED) {
      docID = docID + "|{" + result.getSiteCollectionGuid().toUpperCase() +"}";
    }
    // TODO Set SPType and Last Modified correctly.
    webAppPolicy = new SPDocument(docID, siteCollectionUrlToUse,
        Calendar.getInstance(), SPConstants.NO_AUTHOR, SPConstants.NO_OBJTYPE,
        siteCollectionUrlToUse, feedType, SPType.SP2007);
    webAppPolicy.setDocumentType(DocumentType.ACL);
    Map<String, SPDocument> urlToDocMap = Maps.newHashMap();
    urlToDocMap.put(result.getSiteCollectionUrl(), webAppPolicy);
    try {
      processWsResponse(result, urlToDocMap, webState);
    } catch (SharepointException ex) {
      LOGGER.log(Level.WARNING,
          "Error processing ACL response for web application policy", ex);
    }
    webAppPolicy.setWebAppPolicyDoc(true);
    return webAppPolicy;
  }

  /**
   * Executes CheckConnectivity() web method of GssAcl web service. Used for
   * checking the Web Service connectivity
   */
  public void checkConnectivity() throws SharepointException {
    Util.makeWSRequestVoid(sharepointClientContext, aclWS,
        new Util.RequestExecutorVoid() {
      public void onRequest(final BaseWS ws) throws Throwable {
        ((AclWS) ws).checkConnectivity();
      }
     
      public void onError(final Throwable e) {
        LOGGER.log(Level.WARNING, "Call to checkConnectivity failed.", e);
      }
    });
  }
}
TOP

Related Classes of com.google.enterprise.connector.sharepoint.client.AclHelper$SPBasePermissions

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.