Package com.google.enterprise.connector.filesystem

Source Code of com.google.enterprise.connector.filesystem.AbstractSmbAclBuilder

// Copyright 2013 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.filesystem;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.enterprise.connector.spi.Principal;
import com.google.enterprise.connector.spi.SpiConstants.CaseSensitivityType;

import jcifs.smb.ACE;
import jcifs.smb.SID;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Builds ACLs for SMB file system Directories and Files.
* </p>
* Note that this connector, if feeding directories at all, feeds them as
* DocumentType.ACL, not indexable documents.  For this reason, the ACLs
* built for directories are built strictly for the purpose of inheritance
* and may not reflect the true permissions for that directory.  This allows
* us to simplify the handling of INHERIT_ONLY and NO_PROPAGATE ACEs.
* </p>
* A note about the inheritance behaviours of ACEs, as described by Microsoft.
* From this Microsoft page describing the application of inheritance flags to
* ACEs:  http://support.microsoft.com/kb/220167
* </p>
* "This folder only": ACE flags = 0 : No inheritance applies to ACE.
* </p>
* "This folder, subfolders, and files":
* ACE flags = FLAG_OBJECT_INHERIT | FLAG_CONTAINER_INHERIT :
* All subordinate objects inherit this ACE, unless they are configured to
* block ACL inheritance altogether.
* </p>
* "This folder and subfolders": ACE flags = FLAG_CONTAINER_INHERIT :
* ACE propagates to subfolders of this container, but not to files within
* this container.
* </p>
* "This folder and files": ACE flags = FLAG_OBJECT_INHERIT :
* ACE propagates to files within this container, but not to subfolders.
* </p>
* "Subfolders and files only":
* ACE flags = FLAG_INHERIT_ONLY | FLAG_OBJECT_INHERIT | FLAG_CONTAINER_INHERIT:
* ACE does not apply to this container, but does propagate to both subfolders
* and files contained within.
* </p>
* "Subfolders only":
* ACE flags = FLAG_INHERIT_ONLY | FLAG_CONTAINER_INHERIT :
* ACE does not apply to this container, but propagates to subfolders.
* It does not propagate to contained files.
* </p>
* "Files only":
* ACE flags = FLAG_INHERIT_ONLY | FLAG_OBJECT_INHERIT :
* ACE does not apply to this container, but propagates to the files it
* contains. Subfolders do not receive this ACE.
* </p>
* "Apply permissions to objects and/or containers within this container only":
* ACE flags = * | FLAG_NO_PROPAGATE :
* This flag limits inheritance only to those sub-objects that are immediately
* subordinate to the current object.  It would be used in combination with
* other flags to indicate whether the ACE applies to this container,
* subordinate containers, and/or subordinate files.
* </p>
* More information regarding the explicit individual meanings of the ACE flags:
* FLAG_INHERIT_ONLY - This flag indicates that this ACE does not apply to the
* current object.
* FLAG_CONTAINER_INHERIT - This flag indicates that subordinate containers
* will inherit this ACE.
* FLAG_OBJECT_INHERIT - This flag indicates that subordinate files will
* inherit the ACE.
* FLAG_NO_PROPAGATE - This flag indicates that the subordinate object will
* not propagate the inherited ACE any further.
*/

abstract class AbstractSmbAclBuilder implements AclBuilder {
  private static final Logger LOGGER = Logger.getLogger(
      AbstractSmbAclBuilder.class.getName());

  protected final SmbFileDelegate file;

  /**
   * Represents the format in which the ACEs are to be returned for users.
   */
  protected final AclFormat userAclFormat;

  /**
   * Represents the format in which the ACEs are to be returned for groups.
   */
  protected final AclFormat groupAclFormat;

  /**
   * The global namespace for users and groups.
   */
  protected final String globalNamespace;

  /**
   * The local namespace for users and groups.
   */
  protected final String localNamespace;

  /**
   * Creates an {@link AbstractSmbAclBuilder}.
   *
   * @param file the {@link SmbFileDelegate} whose {@link Acl} we build.
   * @param propertyFetcher Object containing the required properties.
   */
  protected AbstractSmbAclBuilder(SmbFileDelegate file,
                                  AclProperties propertyFetcher) {
    Preconditions.checkNotNull(file, "file may not be null");
    Preconditions.checkNotNull(propertyFetcher,
                               "propertyFetcher may not be null");
    this.file = file;
    this.globalNamespace = propertyFetcher.getGlobalNamespace();
    this.localNamespace = propertyFetcher.getLocalNamespace();
    AclFormat tempFormat = AclFormat.getAclFormat(
        propertyFetcher.getUserAclFormat());
    if (tempFormat == null) {
      LOGGER.warning("Incorrect value specified for user AclFormat parameter; "
          + "Setting default value "
          + AclFormat.USER.getFormat());
      this.userAclFormat = AclFormat.USER;
    } else {
      this.userAclFormat = tempFormat;
    }
    tempFormat = AclFormat.getAclFormat(
        propertyFetcher.getGroupAclFormat());
    if (tempFormat == null) {
      LOGGER.warning("Incorrect value specified for group AclFormat parameter; "
            + "Setting default value "
            + AclFormat.GROUP.getFormat());
      this.groupAclFormat = AclFormat.GROUP;
    } else {
      this.groupAclFormat = tempFormat;
    }
  }

  /*
   * Returns ACL from the list of ACEs
   */
  @VisibleForTesting
  Acl getAclFromAceList(List<ACE> allowAceList, List<ACE> denyAceList) {
    Set<Principal> users = new TreeSet<Principal>();
    Set<Principal> groups = new TreeSet<Principal>();
    for (ACE ace : allowAceList) {
      addAceToSet(users, groups, ace);
    }
    Set<Principal> denyusers;
    Set<Principal> denygroups;
    if (denyAceList != null) {
      denyusers = new TreeSet<Principal>();
      denygroups = new TreeSet<Principal>();
      for (ACE ace : denyAceList) {
        addAceToSet(denyusers, denygroups, ace);
      }
    } else {
      denyusers = null;
      denygroups = null;
    }
    return Acl.newAcl(users, groups, denyusers, denygroups);
  }

  /**
   * Adds the ACE to appropriate set by checking whether
   * it is a user ACE or group ACE.
   * @param users Container for User ACEs
   * @param groups Container for Group ACEs
   * @param finalAce ACE that needs to be added to the ACL
   */
  protected void addAceToSet(Set<Principal> users, Set<Principal> groups,
      ACE finalAce) {
    SID sid = finalAce.getSID();
    int sidType = sid.getType();
    String aclEntry = sid.toDisplayString();
    int ix = aclEntry.indexOf('\\');
    if (ix > 0) {
      String domain = aclEntry.substring(0, ix);
      String userOrGroup = aclEntry.substring(ix + 1);
      if (sidType == SID.SID_TYPE_USER) {
        aclEntry = AclFormat.formatString(userAclFormat, userOrGroup, domain);
      } else {
        aclEntry = AclFormat.formatString(groupAclFormat, userOrGroup, domain);
      }
    }
    switch (sidType) {
      case SID.SID_TYPE_USER:
        // TODO: I don't think SID supports local users, so assume global.
        users.add(new Principal(userAclFormat.getPrincipalType(),
                globalNamespace, aclEntry,
                CaseSensitivityType.EVERYTHING_CASE_INSENSITIVE));
        break;
      case SID.SID_TYPE_DOM_GRP:
      case SID.SID_TYPE_DOMAIN:
        groups.add(new Principal(groupAclFormat.getPrincipalType(),
                globalNamespace, aclEntry,
                CaseSensitivityType.EVERYTHING_CASE_INSENSITIVE));
        break;
      case SID.SID_TYPE_ALIAS:
      case SID.SID_TYPE_WKN_GRP:
        if (ix < 0 && !Strings.isNullOrEmpty(sid.getDomainName())) {
          aclEntry = AclFormat.formatString(groupAclFormat, aclEntry,
                                            sid.getDomainName());
        }
        groups.add(new Principal(groupAclFormat.getPrincipalType(),
                globalNamespace, aclEntry,
                CaseSensitivityType.EVERYTHING_CASE_INSENSITIVE));
        break;
    }
  }

  /**
   * Gets the Allow and Deny ACEs from the {@code securityAces} and
   * adds them to the repective list if they pass the {@code predicate}.
   *
   * @param securityAces
   * @param aceList List where the allow ACEs are to be added.
   * @param aceDenyList List where the deny ACEs are to be added.
   * @param predicate decides whether to add the ACE or not.
   * @throws IOException
   */
  @VisibleForTesting
  void checkAndAddAces(ACE[] securityAces, List<ACE> aceList,
      List<ACE> aceDenyList, Predicate<ACE> predicate) throws IOException {
    if (securityAces == null) {
      LOGGER.warning("Cannot process ACL because of null ACL on " + file);
      return;
    }
    for (ACE ace : securityAces) {
      if (predicate.apply(ace)) {
        checkAndAddAce(ace, aceList, aceDenyList);
      }
    }
  }

  /**
   * Checks for various conditions on ACEs before adding them as valid READ ACEs.
   *
   * @param ace ACE to be checked and added.
   * @param aceList List where the ACE needs to be added if it is a valid ACE
   * @param aceDenyList List where the deny ACE needs to be added if it is a
   * valid ACE. May be null.
   */
  protected void checkAndAddAce(ACE ace, List<ACE> aceList,
      List<ACE> aceDenyList) {
    SID sid = ace.getSID();
    if (!isSupportedWindowsSid(sid)) {
      if (!isSupportedSidType(sid.getType())) {
        LOGGER.log(Level.FINEST, "Filtering unsupported ACE {0} for file {1}",
                   new Object[] { ace, file });
        return;
      }
      if (isBuiltin(sid)) {
        LOGGER.log(Level.FINEST, "Filtering BUILTIN ACE {0} for file {1}",
                   new Object[] { ace, file });
        return;
      }
    }
    String aclEntry = sid.toDisplayString();
    if (sid.toString().equals(aclEntry)) {
      LOGGER.log(Level.FINEST, "Filtering unresolved ACE {0} for file {1}",
                 new Object[] { ace, file });
      return;
    }
    if (!isReadAce(ace.getAccessMask())) {
      LOGGER.log(Level.FINEST, "Filtering non-read ACE {0} for file {1}",
                 new Object[] { ace, file });
      return;
    }
    if (ace.isAllow()) {
      LOGGER.log(Level.FINEST, "Adding read ACE {0} for file {1}",
                 new Object[] { ace, file });
      aceList.add(ace);
    } else if (aceDenyList != null) {
      LOGGER.log(Level.FINEST, "Adding deny read ACE {0} for file {1}",
                 new Object[] { ace, file });
      aceDenyList.add(ace);
    }
  }

  /**
   * {@link ACE#getAccessMask()} bits that must all be set to enable read
   * permission.
   */
  /* @VisibleForTesting */
  static final int READ_ACCESS_MASK =
      ACE.READ_CONTROL | ACE.FILE_READ_ATTRIBUTES
          | ACE.FILE_READ_EA | ACE.FILE_READ_DATA;

  /**
   * Returns true if the provided {@link ACE#getAccessMask()} enables read
   * permission.
   */
  protected boolean isReadAce(int accessMask) {
    return (((accessMask & READ_ACCESS_MASK) == READ_ACCESS_MASK) ||
            ((accessMask & (ACE.GENERIC_ALL | ACE.GENERIC_READ)) != 0));
  }

  /**
   * An {@link ACE} is considered for inclusion in a returned ACL if its
   * {@link SID} is one of these supported types.
   */
  private static final int[] SUPPORTED_TYPES =
      new int[] {SID.SID_TYPE_USER, SID.SID_TYPE_DOMAIN, SID.SID_TYPE_DOM_GRP,
          SID.SID_TYPE_ALIAS};

  /**
   * Returns true if the provided {@link SID#getType} indicates the associated
   * {@link ACE} may be included in an ACL.
   */
  private boolean isSupportedSidType(int type) {
    for (int st : SUPPORTED_TYPES) {
      if (st == type) {
        return true;
      }
    }
    return false;
  }

  protected static final String EVERYONE_SID = "S-1-1-0";

  /**
   * {@link String} representations of {@link SID} objects that qualify for
   * inclusion in an ACL regardless of the value returned by
   * {@link #isSupportedSidType(int type)} or by {@link #isBuiltin(SID)}.
   */
  private static final Set<String> SUPPORTED_WINDOWS_SIDS =
      new HashSet<String>(Arrays.asList(
          "S-1-5-32-544"// Administrators
          EVERYONE_SID,    // Everyone
          "S-1-5-32-545"// Users
          "S-1-5-32-546"// Guests
          "S-1-5-4",       // NT AUTHORITY\INTERACTIVE  
          "S-1-5-11"       // NT AUTHORITY\Authenticated Users
      ));

  /**
   * Returns true if the supplied {@link SID} qualifies for inclusion in an ACL,
   * regardless of the value returned by {@link #isSupportedSidType(int type)}
   * or by {@link #isBuiltin(SID)}.
   */
  private final boolean isSupportedWindowsSid(SID sid) {
    return SUPPORTED_WINDOWS_SIDS.contains(sid.toString());
  }

  /**
   * A special name returned by {@link SID#getDomainName()} for built in
   * objects. Generally an {@link ACE} for such an object would not qualify for
   * inclusion in an ACL. For Exceptions refer to
   * {@link #isSupportedWindowsSid(SID)}.
   */
  private static final String BUILTIN_DOMAIN_NAME = "BUILTIN";

  /**
   * Returns true if the passed in {@link SID} has type
   * {@link SID#SID_TYPE_ALIAS} and domain {@link #BUILTIN_DOMAIN_NAME}.
   */
  private boolean isBuiltin(SID sid) {
    return (sid.getType() == SID.SID_TYPE_ALIAS &&
            BUILTIN_DOMAIN_NAME.equals(sid.getDomainName()));
  }
}
TOP

Related Classes of com.google.enterprise.connector.filesystem.AbstractSmbAclBuilder

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.