Add AclBuilderTest

Code Review: https://codereview.appspot.com/35900047/
diff --git a/test/com/google/enterprise/adaptor/fs/AclBuilderTest.java b/test/com/google/enterprise/adaptor/fs/AclBuilderTest.java
new file mode 100644
index 0000000..1fb3414
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/AclBuilderTest.java
@@ -0,0 +1,435 @@
+// 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 static com.google.enterprise.adaptor.fs.AclView.user;
+import static com.google.enterprise.adaptor.fs.AclView.group;
+import static com.google.enterprise.adaptor.fs.AclView.GenericPermission.*;
+
+import static org.junit.Assert.*;
+
+import static java.nio.file.attribute.AclEntryFlag.*;
+import static java.nio.file.attribute.AclEntryPermission.*;
+import static java.nio.file.attribute.AclEntryType.*;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.enterprise.adaptor.Acl;
+import com.google.enterprise.adaptor.Acl.InheritanceType;
+import com.google.enterprise.adaptor.DocId;
+import com.google.enterprise.adaptor.GroupPrincipal;
+import com.google.enterprise.adaptor.Principal;
+import com.google.enterprise.adaptor.UserPrincipal;
+
+import org.junit.*;
+import org.junit.rules.ExpectedException;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.AclEntry;
+import java.nio.file.attribute.AclFileAttributeView;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Test cases for {@link AclBuilder}.
+ */
+public class AclBuilderTest {
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+  
+  private final Path doc = Paths.get("foo", "bar");
+  private final DocId inheritId = new DocId("foo");
+  private final Set<String> windowsAccounts = ImmutableSet.of(
+      "BUILTIN\\Administrators", "Everyone", "BUILTIN\\Users",
+      "BUILTIN\\Guest", "NT AUTHORITY\\INTERACTIVE",
+      "NT AUTHORITY\\Authenticated Users");
+  private final String builtinPrefix = "BUILTIN\\";
+  private final String namespace = "namespace";
+  private final Set<GroupPrincipal> emptyGroups = ImmutableSet.of();
+  private final AclFileAttributeView aclView = new AclView(
+      user("joe").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      user("mary").type(ALLOW).perms(GENERIC_READ, GENERIC_WRITE)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      user("mike").type(DENY).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      group("EVERYONE").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      group("sales").type(DENY).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+
+  @Test
+  public void testConstructorNullPath() throws Exception {
+    thrown.expect(NullPointerException.class);
+    new AclBuilder(null, aclView, windowsAccounts, builtinPrefix, namespace);
+  }
+
+  @Test
+  public void testConstructorNullAclView() throws Exception {
+    thrown.expect(NullPointerException.class);
+    new AclBuilder(doc, null, windowsAccounts, builtinPrefix, namespace);
+  }
+
+  @Test
+  public void testConstructorNullAccounts() throws Exception {
+    thrown.expect(NullPointerException.class);
+    new AclBuilder(doc, aclView, null, builtinPrefix, namespace);
+  }
+
+  @Test
+  public void testConstructorNullPrefix() throws Exception {
+    thrown.expect(NullPointerException.class);
+    new AclBuilder(doc, aclView, windowsAccounts, null, namespace);
+  }
+
+  @Test
+  public void testConstructorNullNamespace() throws Exception {
+    thrown.expect(NullPointerException.class);
+    new AclBuilder(doc, aclView, windowsAccounts, builtinPrefix, null);
+  }
+
+  @Test
+  public void testGetAclForDirectory() throws Exception {
+    Acl acl = newBuilder(aclView).getAcl(inheritId, true, null);
+    Acl expected = expectedBuilder().build();
+    assertEquals(expected, acl);
+  }
+
+  @Test
+  public void testGetAclForFile() throws Exception {
+    Acl acl = newBuilder(aclView).getAcl(inheritId, false, null);
+    Acl expected = expectedBuilder()
+        .setInheritanceType(InheritanceType.LEAF_NODE)
+        .build();
+    assertEquals(expected, acl);
+  }
+
+  @Test
+  public void testGetShareAcl() throws Exception {
+    Acl acl = newBuilder(aclView).getShareAcl(inheritId);
+    Acl expected = expectedBuilder()
+        .setInheritanceType(InheritanceType.AND_BOTH_PERMIT)
+        .build();
+    assertEquals(expected, acl);
+  }
+
+  @Test
+  public void testGetInheritableByAllDescendentFoldersAcl() throws Exception {
+    String fragment = "allFoldersAcl";
+    Acl acl = newBuilder(aclView).getInheritableByAllDescendentFoldersAcl(
+        inheritId, fragment);
+    Acl expected = expectedBuilder(fragment).build();
+    assertEquals(expected, acl);
+  }
+  
+  @Test
+  public void testGetInheritableByAllDescendentFilesAcl() throws Exception {
+    String fragment = "allFilesAcl";
+    Acl acl = newBuilder(aclView).getInheritableByAllDescendentFilesAcl(
+        inheritId, fragment);
+    Acl expected = expectedBuilder(fragment).build();
+    assertEquals(expected, acl);
+  }
+  
+  @Test
+  public void testGetInheritableByChildFoldersOnlyAcl() throws Exception {
+    String fragment = "childFoldersAcl";
+    Acl acl = newBuilder(aclView).getInheritableByChildFoldersOnlyAcl(
+        inheritId, fragment);
+    Acl expected = expectedBuilder(fragment).build();
+    assertEquals(expected, acl);
+  }
+  
+  @Test
+  public void testGetInheritableByChildFilesOnlyAcl() throws Exception {
+    String fragment = "childFilesAcl";
+    Acl acl = newBuilder(aclView).getInheritableByChildFilesOnlyAcl(
+        inheritId, fragment);
+    Acl expected = expectedBuilder(fragment).build();
+    assertEquals(expected, acl);
+  }
+
+  @Test
+  public void testFileInheritAcl() throws Exception {
+    // "mary" and "sales" are only inheritable by files, not directories.
+    AclFileAttributeView aclView = new AclView(
+      user("joe").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      user("mary").type(ALLOW).perms(GENERIC_READ, GENERIC_WRITE)
+          .flags(FILE_INHERIT),
+      user("mike").type(DENY).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      group("EVERYONE").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      group("sales").type(DENY).perms(GENERIC_READ)
+          .flags(FILE_INHERIT));
+    AclBuilder aclBuilder = newBuilder(aclView);
+
+    String fragment = "allFilesAcl";
+    Acl acl = aclBuilder.getInheritableByAllDescendentFilesAcl(inheritId,
+        fragment);
+    Acl expected = expectedBuilder(fragment).build();
+    assertEquals(expected, acl);
+
+    fragment = "childFilesAcl";
+    acl = aclBuilder.getInheritableByChildFilesOnlyAcl(inheritId, fragment);
+    expected = expectedBuilder(fragment).build();
+    assertEquals(expected, acl);
+
+    // The folder inherit ACLs should not include "mary" or "sales".
+    fragment = "allFoldersAcl";
+    acl = aclBuilder.getInheritableByAllDescendentFoldersAcl(inheritId,
+        fragment);
+    expected = expectedBuilder(fragment)
+        .setPermitUsers(users("joe")).setDenyGroups(emptyGroups).build();
+    assertEquals(expected, acl);
+
+    fragment = "childFoldersAcl";
+    acl = aclBuilder.getInheritableByAllDescendentFoldersAcl(inheritId,
+        fragment);
+    expected = expectedBuilder(fragment)
+        .setPermitUsers(users("joe")).setDenyGroups(emptyGroups).build();
+    assertEquals(expected, acl);
+  }
+
+  @Test
+  public void testFolderInheritAcl() throws Exception {
+    // "mary" and "sales" are only inheritable by directories, not files.
+    AclFileAttributeView aclView = new AclView(
+      user("joe").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      user("mary").type(ALLOW).perms(GENERIC_READ, GENERIC_WRITE)
+          .flags(DIRECTORY_INHERIT),
+      user("mike").type(DENY).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      group("EVERYONE").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      group("sales").type(DENY).perms(GENERIC_READ)
+          .flags(DIRECTORY_INHERIT));
+    AclBuilder aclBuilder = newBuilder(aclView);
+
+    String fragment = "allFoldersAcl";
+    Acl acl = aclBuilder.getInheritableByAllDescendentFoldersAcl(inheritId,
+        fragment);
+    Acl expected = expectedBuilder(fragment).build();
+    assertEquals(expected, acl);
+
+    fragment = "childFoldersAcl";
+    acl = aclBuilder.getInheritableByChildFoldersOnlyAcl(inheritId, fragment);
+    expected = expectedBuilder(fragment).build();
+    assertEquals(expected, acl);
+
+    // The file inherit ACLs should not include "mary" or "sales".
+    fragment = "allFilesAcl";
+    acl = aclBuilder.getInheritableByAllDescendentFilesAcl(inheritId,
+        fragment);
+    expected = expectedBuilder(fragment)
+        .setPermitUsers(users("joe")).setDenyGroups(emptyGroups).build();
+    assertEquals(expected, acl);
+
+    fragment = "childFilesAcl";
+    acl = aclBuilder.getInheritableByAllDescendentFilesAcl(inheritId,
+        fragment);
+    expected = expectedBuilder(fragment)
+        .setPermitUsers(users("joe")).setDenyGroups(emptyGroups).build();
+    assertEquals(expected, acl);
+  }
+
+  @Test
+  public void testNoPropagateFolderInheritAcl() throws Exception {
+    AclFileAttributeView aclView = new AclView(
+      user("joe").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      user("mike").type(ALLOW).perms(GENERIC_READ)
+          .flags(DIRECTORY_INHERIT, NO_PROPAGATE_INHERIT),
+      user("mary").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+    AclBuilder aclBuilder = newBuilder(aclView);
+
+    Acl acl =
+        aclBuilder.getInheritableByAllDescendentFoldersAcl(inheritId, null);
+    Acl expected = emptyExpectedBuilder()
+        .setPermitUsers(users("joe", "mary")).build();
+    assertEquals(expected, acl);
+
+    acl = aclBuilder.getInheritableByChildFoldersOnlyAcl(inheritId, null);
+    expected = emptyExpectedBuilder()
+        .setPermitUsers(users("joe", "mike", "mary")).build();
+    assertEquals(expected, acl);
+  }
+
+  @Test
+  public void testNoPropagateFileInheritAcl() throws Exception {
+    AclFileAttributeView aclView = new AclView(
+      user("joe").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      user("mike").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, NO_PROPAGATE_INHERIT),
+      user("mary").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+    AclBuilder aclBuilder = newBuilder(aclView);
+
+    Acl acl =
+        aclBuilder.getInheritableByAllDescendentFilesAcl(inheritId, null);
+    Acl expected = emptyExpectedBuilder()
+        .setPermitUsers(users("joe", "mary")).build();
+    assertEquals(expected, acl);
+
+    acl = aclBuilder.getInheritableByChildFilesOnlyAcl(inheritId, null);
+    expected = emptyExpectedBuilder()
+        .setPermitUsers(users("joe", "mike", "mary")).build();
+    assertEquals(expected, acl);
+  }
+
+  @Test
+  public void testInheritOnlyAcl() throws Exception {
+    AclFileAttributeView aclView = new AclView(
+      user("joe").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+      user("mike").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT, INHERIT_ONLY),
+      user("mary").type(ALLOW).perms(GENERIC_READ)
+          .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+    AclBuilder aclBuilder = newBuilder(aclView);
+
+    // This node's ACL should not include mike.
+    Acl acl = aclBuilder.getAcl(inheritId, true, null);
+    Acl expected = emptyExpectedBuilder()
+        .setPermitUsers(users("joe", "mary")).build();
+    assertEquals(expected, acl);
+                         
+    // However, all of its children should include mike.
+    expected = emptyExpectedBuilder()
+        .setPermitUsers(users("joe", "mike", "mary")).build();
+    acl = aclBuilder.getInheritableByAllDescendentFoldersAcl(inheritId, null);
+    assertEquals(expected, acl);
+    acl = aclBuilder.getInheritableByAllDescendentFilesAcl(inheritId, null);
+    assertEquals(expected, acl);
+  }
+
+  @Test
+  public void testInsufficientReadPerms() throws Exception {
+    AclFileAttributeView aclView = new AclView(
+        user("joe").type(ALLOW).perms(GENERIC_READ)
+            .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+        user("mike").type(ALLOW).perms(READ_DATA)
+            .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+    AclBuilder aclBuilder = newBuilder(aclView);
+
+    // This node's ACLs should not include mike.
+    Acl expected = emptyExpectedBuilder()
+        .setPermitUsers(users("joe")).build();
+    Acl acl = aclBuilder.getAcl(inheritId, true, null);
+    assertEquals(expected, acl);
+    acl = aclBuilder.getInheritableByAllDescendentFoldersAcl(inheritId, null);
+    assertEquals(expected, acl);
+    acl = aclBuilder.getInheritableByAllDescendentFilesAcl(inheritId, null);
+    assertEquals(expected, acl);
+  }
+
+  @Test
+  public void testWindowsBuiltinUsers() throws Exception {
+    ArrayList<AclEntry> entries = Lists.newArrayList();
+    // Add all the permitted builtin users.
+    for (String builtin : windowsAccounts) {
+      entries.add(user(builtin).type(ALLOW).perms(GENERIC_READ)
+                  .flags(FILE_INHERIT, DIRECTORY_INHERIT).build());
+    }
+    String badBuiltin = builtinPrefix + "BACKUP";
+    // Now add a builtin user that should be excluded.
+    entries.add(user(badBuiltin).type(ALLOW).perms(GENERIC_READ)
+                .flags(FILE_INHERIT, DIRECTORY_INHERIT).build());
+
+    AclFileAttributeView aclView =
+        new AclView(entries.toArray(new AclEntry[0]));
+    AclBuilder aclBuilder = newBuilder(aclView);
+
+    // The permitted users should contain all of the acceptable builtins.
+    // But should not contain the bad builtin.
+    Acl expected = emptyExpectedBuilder()
+        .setPermitUsers(users(Iterables.toArray(windowsAccounts, String.class)))
+        .build();
+    Acl acl = aclBuilder.getAcl(inheritId, true, null);
+    assertEquals(expected, acl);    
+  }
+
+  /** Returns an AclBuilder for the AclFileAttributeView. */
+  private AclBuilder newBuilder(AclFileAttributeView aclView) {
+    return new AclBuilder(doc, aclView, windowsAccounts, builtinPrefix,
+                          namespace);
+  }
+
+  /**
+   * Returns an Acl.Builder representing the aclView field.
+   * The caller is expected to overwrite any of thes presets,
+   * then call build().
+   */
+  private Acl.Builder expectedBuilder() {
+    return expectedBuilder(null);
+  }
+
+  /**
+   * Returns an Acl.Builder representing the aclView field.
+   * The caller is expected to overwrite any of thes presets,
+   * then call build().
+   */
+  private Acl.Builder expectedBuilder(String fragment) {
+    return emptyExpectedBuilder(fragment)
+        .setPermitUsers(users("joe", "mary")).setDenyUsers(users("mike"))
+        .setPermitGroups(groups("EVERYONE")).setDenyGroups(groups("sales"));
+  }
+
+  /**
+   * Returns an Acl.Builder with no users or groups.
+   */
+  private Acl.Builder emptyExpectedBuilder() {
+    return emptyExpectedBuilder(null);
+  }
+
+  /**
+   * Returns an Acl.Builder with no users or groups.
+   */
+  private Acl.Builder emptyExpectedBuilder(String fragment) {
+    return new Acl.Builder().setInheritFrom(inheritId, fragment)
+        .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+        .setEverythingCaseInsensitive();
+  }
+
+  /**
+   * Returns a Set of UserPrincipals of the named users.
+   */
+  private Set<UserPrincipal> users(String... users) {
+    Set<UserPrincipal> principals = Sets.newHashSet();
+    for (String user : users) {
+      principals.add(new UserPrincipal(user, namespace));
+    }
+    return principals;
+  }
+
+  /**
+   * Returns a Set of GroupPrincipals of the named groups.
+   */
+  private Set<GroupPrincipal> groups(String... groups) {
+    Set<GroupPrincipal> principals = Sets.newHashSet();
+    for (String group : groups) {
+      principals.add(new GroupPrincipal(group, namespace));
+    }
+    return principals;
+  }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/AclView.java b/test/com/google/enterprise/adaptor/fs/AclView.java
index 510a49e..233061e 100644
--- a/test/com/google/enterprise/adaptor/fs/AclView.java
+++ b/test/com/google/enterprise/adaptor/fs/AclView.java
@@ -14,6 +14,8 @@
 
 package com.google.enterprise.adaptor.fs;
 
+import static java.nio.file.attribute.AclEntryPermission.*;
+
 import java.nio.file.attribute.AclEntry;
 import java.nio.file.attribute.AclEntryFlag;
 import java.nio.file.attribute.AclEntryPermission;
@@ -24,7 +26,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * This convenience class allows creation of an {@link AclFileAttributeView}
@@ -57,6 +61,27 @@
  */
 class AclView extends SimpleAclFileAttributeView {
 
+  /** Compound permissions for easier specification. */
+  static enum GenericPermission {
+    GENERIC_READ(SYNCHRONIZE, READ_ACL, READ_DATA, READ_ATTRIBUTES,
+                 READ_NAMED_ATTRS),
+    GENERIC_WRITE(SYNCHRONIZE, READ_ACL, WRITE_DATA, APPEND_DATA,
+                  WRITE_ATTRIBUTES, WRITE_NAMED_ATTRS),
+    GENERIC_EXECUTE(SYNCHRONIZE, READ_ACL, READ_ATTRIBUTES, EXECUTE),
+    GENERIC_ALL(AclEntryPermission.values());
+
+    private final Set<AclEntryPermission> permissions;
+
+    GenericPermission(AclEntryPermission... permissions) {
+      this.permissions = Collections.unmodifiableSet(
+          EnumSet.copyOf(Arrays.asList(permissions)));
+    }
+
+    Set<AclEntryPermission> getPermissions() {
+      return permissions;
+    }
+  }
+
   AclView() {
     super(Collections.<AclEntry>emptyList());
   }
@@ -112,6 +137,15 @@
       return this;
     }
 
+    AclEntryBuilder perms(GenericPermission... permissions) {
+      Set<AclEntryPermission> perms = EnumSet.noneOf(AclEntryPermission.class);
+      for (GenericPermission genericPerm : permissions) {
+        perms.addAll(genericPerm.getPermissions());
+      }
+      builder.setPermissions(perms);
+      return this;
+    }
+
     AclEntryBuilder flags(AclEntryFlag... flags) {
       builder.setFlags(flags);
       return this;