| // Copyright 2013 Google Inc. All Rights Reserved. |
| // |
| // 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.adaptor.fs; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.base.Predicate; |
| import com.google.enterprise.adaptor.Acl; |
| import com.google.enterprise.adaptor.GroupPrincipal; |
| import com.google.enterprise.adaptor.Principal; |
| import com.google.enterprise.adaptor.UserPrincipal; |
| |
| import java.io.IOException; |
| import java.nio.file.Path; |
| import java.nio.file.attribute.AclEntry; |
| import java.nio.file.attribute.AclEntryFlag; |
| import java.nio.file.attribute.AclEntryPermission; |
| import java.nio.file.attribute.AclEntryType; |
| import java.nio.file.attribute.AclFileAttributeView; |
| import java.util.HashSet; |
| import java.util.Set; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** |
| * Build Acl for the file system adaptor. |
| */ |
| public class AclBuilder { |
| private static final Logger log |
| = Logger.getLogger(AclBuilder.class.getName()); |
| |
| private Path doc; |
| private AclFileAttributeView aclView; |
| private Set<String> supportedWindowsAccounts; |
| private String builtinPrefix; |
| private String namespace; |
| |
| public AclBuilder(Path doc, AclFileAttributeView aclView, |
| Set<String> supportedWindowsAccounts, String builtinPrefix, |
| String namespace) { |
| Preconditions.checkNotNull(doc, "doc may not be null"); |
| Preconditions.checkNotNull(aclView, "aclView may not be null"); |
| Preconditions.checkNotNull(supportedWindowsAccounts, |
| "supportedWindowsAccounts may not be null"); |
| Preconditions.checkNotNull(builtinPrefix, "builtinPrefix may not be null"); |
| Preconditions.checkNotNull(namespace, "namespace may not be null"); |
| this.doc = doc; |
| this.aclView = aclView; |
| this.supportedWindowsAccounts = supportedWindowsAccounts; |
| this.builtinPrefix = builtinPrefix.toUpperCase(); |
| this.namespace = namespace; |
| } |
| |
| public Acl.Builder getAcl() throws IOException { |
| return getAcl(isDirectEntry); |
| } |
| |
| public Acl.Builder getInheritableByAllDescendentFoldersAcl() |
| throws IOException { |
| return getAcl(isInheritableByAllDescendentFoldersEntry); |
| } |
| |
| public Acl.Builder getInheritableByAllDescendentFilesAcl() |
| throws IOException { |
| return getAcl(isInheritableByAllDescendentFilesEntry); |
| } |
| |
| public Acl.Builder getInheritableByChildFoldersOnlyAcl() throws IOException { |
| return getAcl(isInheritableByChildFoldersOnlyEntry); |
| } |
| |
| public Acl.Builder getInheritableByChildFilesOnlyAcl() throws IOException { |
| return getAcl(isInheritableByChildFilesOnlyEntry); |
| } |
| |
| private Acl.Builder getAcl(Predicate<Set<AclEntryFlag>> predicate) |
| throws IOException { |
| Set<Principal> permits = new HashSet<Principal>(); |
| Set<Principal> denies = new HashSet<Principal>(); |
| for (AclEntry entry : aclView.getAcl()) { |
| if (!predicate.apply(entry.flags())) { |
| continue; |
| } |
| if (filterOutAclEntry(entry)) { |
| continue; |
| } |
| |
| Principal principal; |
| if (entry.principal() instanceof |
| java.nio.file.attribute.GroupPrincipal) { |
| principal = new GroupPrincipal(entry.principal().getName(), namespace); |
| } else if (entry.principal() instanceof |
| java.nio.file.attribute.UserPrincipal) { |
| principal = new UserPrincipal(entry.principal().getName(), namespace); |
| } else { |
| log.log(Level.WARNING, "Unsupported Acl entry found: {0}", entry); |
| continue; |
| } |
| |
| if (entry.type() == AclEntryType.ALLOW) { |
| permits.add(principal); |
| } else if (entry.type() == AclEntryType.DENY) { |
| denies.add(principal); |
| } |
| } |
| |
| return new Acl.Builder().setPermits(permits).setDenies(denies) |
| .setEverythingCaseInsensitive(); |
| } |
| |
| /** |
| * Returns true if provided {@link AclEntry} should be excluded from Acl. |
| * |
| * @param entry The AclEntry to check. |
| */ |
| private boolean filterOutAclEntry(AclEntry entry) { |
| String principalName = entry.principal().getName(); |
| |
| if (!isSupportedWindowsAccount(principalName)) { |
| if (isBuiltin(principalName)) { |
| log.log(Level.FINEST, "Filtering BUILTIN ACE {0} for file {1}.", |
| new Object[] { entry, doc }); |
| return true; |
| } |
| } |
| |
| if (isSid(principalName)) { |
| log.log(Level.FINEST, "Filtering unresolved ACE {0} for file {1}.", |
| new Object[] { entry, doc }); |
| return true; |
| } |
| |
| if (!hasReadPermission(entry.permissions())) { |
| log.log(Level.FINEST, "Filtering non-read ACE {0} for file {1}.", |
| new Object[] { entry, doc }); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Returns true if the provided set of {@link AclEntryPermission} enables |
| * read permission. |
| */ |
| private boolean hasReadPermission(Set<AclEntryPermission> p) { |
| return p.contains(AclEntryPermission.READ_DATA) |
| && p.contains(AclEntryPermission.READ_ACL) |
| && p.contains(AclEntryPermission.READ_NAMED_ATTRS); |
| } |
| |
| /** |
| * Returns true if the passed in user name is a Windows builtin user. |
| */ |
| private boolean isBuiltin(String name) { |
| return name.toUpperCase().startsWith(builtinPrefix); |
| } |
| |
| /** |
| * Returns true if the supplied account qualifies for inclusion in an ACL, |
| * regardless of the value returned by {@link #isBuiltin(String name)}. |
| */ |
| private final boolean isSupportedWindowsAccount(String user) { |
| return supportedWindowsAccounts.contains(user); |
| } |
| |
| /** |
| * Returns true if the supplied account is an unresolved SID. |
| */ |
| private final boolean isSid(String user) { |
| // TODO(mifern): Implementation needed. Can use JNA ConvertStringSidToSid. |
| // If ConvertStringSidToSid return true then it's a SID. |
| return false; |
| } |
| |
| /** |
| * Returns true if the associated set of {@link AclEntryFlag} is explicit |
| * for this node, not inherited from another node. |
| */ |
| private static final Predicate<Set<AclEntryFlag>> isDirectEntry = |
| new Predicate<Set<AclEntryFlag>>() { |
| public boolean apply(Set<AclEntryFlag> flags) { |
| return !flags.contains(AclEntryFlag.INHERIT_ONLY); |
| } |
| }; |
| |
| /** |
| * Returns true if the associated set of {@link AclEntryFlag} is inherited |
| * by direct children folders only. |
| */ |
| private static final Predicate<Set<AclEntryFlag>> |
| isInheritableByChildFoldersOnlyEntry = |
| new Predicate<Set<AclEntryFlag>>() { |
| public boolean apply(Set<AclEntryFlag> flags) { |
| return flags.contains(AclEntryFlag.DIRECTORY_INHERIT); |
| } |
| }; |
| |
| /** |
| * Returns true if the associated set of {@link AclEntryFlag} is inherited |
| * by direct children files only. |
| */ |
| private static final Predicate<Set<AclEntryFlag>> |
| isInheritableByChildFilesOnlyEntry = |
| new Predicate<Set<AclEntryFlag>>() { |
| public boolean apply(Set<AclEntryFlag> flags) { |
| return flags.contains(AclEntryFlag.FILE_INHERIT); |
| } |
| }; |
| |
| /** |
| * Returns true if the associated set of {@link AclEntryFlag} is inherited |
| * by all descendent folders. |
| */ |
| private static final Predicate<Set<AclEntryFlag>> |
| isInheritableByAllDescendentFoldersEntry = |
| new Predicate<Set<AclEntryFlag>>() { |
| public boolean apply(Set<AclEntryFlag> flags) { |
| return flags.contains(AclEntryFlag.DIRECTORY_INHERIT) && |
| !flags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT); |
| } |
| }; |
| |
| /** |
| * Returns true if the associated set of {@link AclEntryFlag} is inherited |
| * by all descendent files. |
| */ |
| private static final Predicate<Set<AclEntryFlag>> |
| isInheritableByAllDescendentFilesEntry = |
| new Predicate<Set<AclEntryFlag>>() { |
| public boolean apply(Set<AclEntryFlag> flags) { |
| return flags.contains(AclEntryFlag.FILE_INHERIT) && |
| !flags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT); |
| } |
| }; |
| } |