namespaced acl support in command-line adaptor
diff --git a/src/com/google/enterprise/adaptor/CommandStreamParser.java b/src/com/google/enterprise/adaptor/CommandStreamParser.java
index 0da27bd..045d03d 100644
--- a/src/com/google/enterprise/adaptor/CommandStreamParser.java
+++ b/src/com/google/enterprise/adaptor/CommandStreamParser.java
@@ -26,6 +26,8 @@
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@@ -240,7 +242,6 @@
*/
public class CommandStreamParser {
-
private static enum Operation {
ID("id"),
RESULT_LINK("result-link"),
@@ -263,6 +264,17 @@
NO_FOLLOW("no-follow"),
NO_ARCHIVE("no-archive"),
DISPLAY_URL("display-url"),
+ ACL("acl"),
+ NAMESPACE("namespace"),
+ ACL_PERMIT_USER("acl-permit-user"),
+ ACL_DENY_USER("acl-deny-user"),
+ ACL_PERMIT_GROUP("acl-permit-group"),
+ ACL_DENY_GROUP("acl-deny-group"),
+ ACL_INHERIT_FROM("acl-inherit-from"),
+ ACL_INHERIT_FRAGMENT("acl-inherit-fragment"),
+ ACL_INHERITANCE_TYPE("acl-inheritance-type"),
+ ACL_CASE_SENSITIVE("acl-case-sensitive"),
+ ACL_CASE_INSENSITIVE("acl-case-insensitive"),
;
private final String commandName;
@@ -282,7 +294,6 @@
private static final Charset CHARSET = Charset.forName("UTF-8");
private static final Map<String, Operation> STRING_TO_OPERATION;
-
static {
Map<String, Operation> stringToOperation = new HashMap<String, Operation>();
for (Operation operation : Operation.values()) {
@@ -291,6 +302,16 @@
STRING_TO_OPERATION = Collections.unmodifiableMap(stringToOperation);
}
+ private static final Map<String, Acl.InheritanceType> STRING_TO_INHERITANCE_TYPE;
+ static {
+ Map<String, Acl.InheritanceType> stringToType
+ = new HashMap<String, Acl.InheritanceType>();
+ for (Acl.InheritanceType type : Acl.InheritanceType.values()) {
+ stringToType.put(type.getCommonForm(), type);
+ }
+ STRING_TO_INHERITANCE_TYPE = Collections.unmodifiableMap(stringToType);
+ }
+
private InputStream inputStream;
private int versionNumber = 0;
private String delimiter;
@@ -376,7 +397,6 @@
}
public void readFromRetriever(DocId docId, Response response) throws IOException {
-
Command command = readCommand();
if (command == null) {
@@ -391,6 +411,14 @@
throw new IOException("requested document " + docId + " does not match retrieved "
+ "document " + foundDocId + ".");
}
+
+ boolean sendAclWithDocument = false;
+ Acl.Builder aclBuilder = new Acl.Builder();
+ DocId inheritFrom = null; // saves inherit-from in case fragment comes
+ Set<Principal> permits = new TreeSet<Principal>(); // accumulates acl state
+ Set<Principal> denies = new TreeSet<Principal>(); // accumulates acl state
+ String namespace = Principal.DEFAULT_NAMESPACE; // last given namespace
+
command = readCommand();
while (command != null) {
switch (command.getOperation()) {
@@ -455,12 +483,59 @@
case LOCK:
response.setLock(Boolean.parseBoolean(command.getArgument()));
break;
+ case ACL:
+ sendAclWithDocument = true;
+ break;
+ case NAMESPACE:
+ namespace = command.getArgument();
+ break;
+ case ACL_PERMIT_USER:
+ permits.add(new UserPrincipal(command.getArgument(), namespace));
+ break;
+ case ACL_DENY_USER:
+ denies.add(new UserPrincipal(command.getArgument(), namespace));
+ break;
+ case ACL_PERMIT_GROUP:
+ permits.add(new GroupPrincipal(command.getArgument(), namespace));
+ break;
+ case ACL_DENY_GROUP:
+ denies.add(new GroupPrincipal(command.getArgument(), namespace));
+ break;
+ case ACL_INHERIT_FROM:
+ inheritFrom = new DocId(command.getArgument());
+ aclBuilder.setInheritFrom(inheritFrom);
+ break;
+ case ACL_INHERIT_FRAGMENT:
+ if (null == inheritFrom) {
+ throw new IOException("acl-inherit-fragment cannot preceed acl-inherit-from");
+ }
+ aclBuilder.setInheritFrom(inheritFrom, command.getArgument());
+ break;
+ case ACL_INHERITANCE_TYPE:
+ Acl.InheritanceType type = STRING_TO_INHERITANCE_TYPE.get(command.getArgument());
+ if (null == type) {
+ throw new IOException("invalid acl-inheritance-type: " + command.getArgument());
+ }
+ aclBuilder.setInheritanceType(type);
+ break;
+ case ACL_CASE_SENSITIVE:
+ aclBuilder.setEverythingCaseSensitive();
+ break;
+ case ACL_CASE_INSENSITIVE:
+ aclBuilder.setEverythingCaseInsensitive();
+ break;
default:
throw new IOException("Retriever Error: invalid operation: '" + command.getOperation() +
(command.hasArgument() ? "' with argument: '" + command.getArgument() + "'" : "'"));
}
command = readCommand();
}
+ // Finish by putting accumulated ACL into response.
+ if (sendAclWithDocument) {
+ aclBuilder.setPermits(permits);
+ aclBuilder.setDenies(denies);
+ response.setAcl(aclBuilder.build());
+ }
}
/**
@@ -559,7 +634,7 @@
Operation operation = STRING_TO_OPERATION.get(commandTokens[0]);
// Skip over unrecognized commands
if (operation == null) {
- // TODO(johnfelton) add a warning about an unrecognized command
+ log.warning("Unrecognized command: " + commandTokens[0]);
continue;
}
diff --git a/test/com/google/enterprise/adaptor/CommandStreamParserTest.java b/test/com/google/enterprise/adaptor/CommandStreamParserTest.java
index 8f02f7a..a60f514 100644
--- a/test/com/google/enterprise/adaptor/CommandStreamParserTest.java
+++ b/test/com/google/enterprise/adaptor/CommandStreamParserTest.java
@@ -200,6 +200,162 @@
}
@Test
+ public void testRetrieverWithoutAcl() throws IOException {
+ String source = "GSA Adaptor Data Version 1 [\n]\n" +
+ "id=isacltest\n" +
+ "content\npick-up-sticks";
+ InputStream inputStream
+ = new ByteArrayInputStream(source.getBytes("UTF-8"));
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ WrapperAdaptor.GetContentsResponse response
+ = new WrapperAdaptor.GetContentsResponse(outputStream);
+ CommandStreamParser parser = new CommandStreamParser(inputStream);
+ assertEquals(1, parser.getVersionNumber());
+ parser.readFromRetriever(new DocId("isacltest"), response);
+ assertArrayEquals("pick-up-sticks".getBytes(), outputStream.toByteArray());
+ assertEquals(null, response.getAcl());
+ assertEquals(false, response.isSecure());
+ }
+
+ @Test
+ public void testRetrieverNamespaceAloneDoesntMakeAcl() throws IOException {
+ String source = "GSA Adaptor Data Version 1 [\n]\n" +
+ "id=isacltest\n" +
+ "namespace=winds\n" +
+ "content\npick-up-sticks";
+ InputStream inputStream
+ = new ByteArrayInputStream(source.getBytes("UTF-8"));
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ WrapperAdaptor.GetContentsResponse response
+ = new WrapperAdaptor.GetContentsResponse(outputStream);
+ CommandStreamParser parser = new CommandStreamParser(inputStream);
+ assertEquals(1, parser.getVersionNumber());
+ parser.readFromRetriever(new DocId("isacltest"), response);
+ assertArrayEquals("pick-up-sticks".getBytes(), outputStream.toByteArray());
+ assertEquals(null, response.getAcl());
+ assertEquals(false, response.isSecure());
+ }
+
+ private static GroupPrincipal g(String n, String ns) {
+ return new GroupPrincipal(n, ns);
+ }
+
+ private static GroupPrincipal g(String n) {
+ return new GroupPrincipal(n);
+ }
+
+ private static UserPrincipal u(String n, String ns) {
+ return new UserPrincipal(n, ns);
+ }
+
+ private static UserPrincipal u(String n) {
+ return new UserPrincipal(n);
+ }
+
+ @Test
+ public void testRetrieverSimpleAcl() throws IOException {
+ String source = "GSA Adaptor Data Version 1 [\n]\n" +
+ "id=isacltest\n" +
+ "acl\n" +
+ "acl-permit-user=ted@abc\n" +
+ "acl-deny-group=todd@abc\n" +
+ "namespace=Winds\n" +
+ "acl-permit-user=Banjo@Zephyr\n" +
+ "acl-permit-group=ward@Zephyr\n" +
+ "namespace=RockaRollas\n" +
+ "acl-deny-user=bob.dylan@rocka\n" +
+ "acl-deny-user=paul@rocka\n" +
+ "acl-deny-group=beatles@rocka\n" +
+ "acl-inherit-from=oxfords\n" +
+ "acl-inheritance-type=and-both-permit\n" +
+ "content\npick-up-sticks";
+ InputStream inputStream
+ = new ByteArrayInputStream(source.getBytes("UTF-8"));
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ WrapperAdaptor.GetContentsResponse response
+ = new WrapperAdaptor.GetContentsResponse(outputStream);
+ CommandStreamParser parser = new CommandStreamParser(inputStream);
+ assertEquals(1, parser.getVersionNumber());
+ parser.readFromRetriever(new DocId("isacltest"), response);
+ assertArrayEquals("pick-up-sticks".getBytes(), outputStream.toByteArray());
+ Acl golden = new Acl.Builder().setPermits(Arrays.asList(
+ g("ward@Zephyr", "Winds"), u("ted@abc"), u("Banjo@Zephyr", "Winds")))
+ .setDenies(Arrays.asList(g("todd@abc"), g("beatles@rocka", "RockaRollas"),
+ u("bob.dylan@rocka", "RockaRollas"), u("paul@rocka", "RockaRollas")))
+ .setInheritFrom(new DocId("oxfords"))
+ .setInheritanceType(Acl.InheritanceType.AND_BOTH_PERMIT)
+ .build();
+ assertEquals(golden, response.getAcl());
+ }
+
+ @Test
+ public void testRetrieverInsensitiveAcl() throws IOException {
+ String source = "GSA Adaptor Data Version 1 [\n]\n" +
+ "id=isacltest\n" +
+ "acl\n" +
+ "acl-case-insensitive\n" +
+ "content\npick-up-sticks";
+ InputStream inputStream
+ = new ByteArrayInputStream(source.getBytes("UTF-8"));
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ WrapperAdaptor.GetContentsResponse response
+ = new WrapperAdaptor.GetContentsResponse(outputStream);
+ CommandStreamParser parser = new CommandStreamParser(inputStream);
+ assertEquals(1, parser.getVersionNumber());
+ parser.readFromRetriever(new DocId("isacltest"), response);
+ assertArrayEquals("pick-up-sticks".getBytes(), outputStream.toByteArray());
+ assertEquals(new Acl.Builder().setEverythingCaseInsensitive().build(),
+ response.getAcl());
+ }
+
+ @Test
+ public void testRetrieverLastSensitiveStick() throws IOException {
+ String source = "GSA Adaptor Data Version 1 [\n]\n" +
+ "id=isacltest\n" +
+ "acl\n" +
+ "acl-case-insensitive\n" +
+ "acl-case-sensitive\n" +
+ "acl-case-insensitive\n" +
+ "acl-case-sensitive\n" +
+ "content\npick-up-sticks";
+ InputStream inputStream
+ = new ByteArrayInputStream(source.getBytes("UTF-8"));
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ WrapperAdaptor.GetContentsResponse response
+ = new WrapperAdaptor.GetContentsResponse(outputStream);
+ CommandStreamParser parser = new CommandStreamParser(inputStream);
+ assertEquals(1, parser.getVersionNumber());
+ parser.readFromRetriever(new DocId("isacltest"), response);
+ assertArrayEquals("pick-up-sticks".getBytes(), outputStream.toByteArray());
+ assertEquals(true, response.getAcl().isEverythingCaseSensitive());
+ assertEquals(false, response.getAcl().isEverythingCaseInsensitive());
+ }
+
+ @Test
+ public void testRetrieverAclHasFragment() throws IOException {
+ String source = "GSA Adaptor Data Version 1 [\n]\n" +
+ "id=isacltest\n" +
+ "acl-inherit-from=london\n" +
+ "acl-inherit-from=oxfords\n" +
+ "acl\n" +
+ "acl-inherit-fragment=topleft\n" +
+ "acl-inherit-fragment=lowerright\n" +
+ "content\npick-up-sticks";
+ InputStream inputStream
+ = new ByteArrayInputStream(source.getBytes("UTF-8"));
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ WrapperAdaptor.GetContentsResponse response
+ = new WrapperAdaptor.GetContentsResponse(outputStream);
+ CommandStreamParser parser = new CommandStreamParser(inputStream);
+ assertEquals(1, parser.getVersionNumber());
+ parser.readFromRetriever(new DocId("isacltest"), response);
+ assertArrayEquals("pick-up-sticks".getBytes(), outputStream.toByteArray());
+ assertEquals(new Acl.Builder()
+ .setInheritFrom(new DocId("oxfords"), "lowerright").build(),
+ response.getAcl());
+ }
+
+ @Test
public void testRetrieverMultipleMetadataValuesSameKey() throws IOException {
String source = "GSA Adaptor Data Version 1 [\n]\n" +
"id=123\n" +