From 6e859c945bb4122131aa1b69d50faaf13206a980 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Tue, 31 May 2016 16:39:52 +0200 Subject: [PATCH 1/2] Extend StorageExample to show how to add ACLs to blobs and buckets --- .../examples/storage/StorageExample.java | 245 +++++++++++++++++- 1 file changed, 242 insertions(+), 3 deletions(-) diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java index 1a6aafa60e9d..0c08f7dd1c2f 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java @@ -20,6 +20,7 @@ import com.google.cloud.AuthCredentials.ServiceAccountAuthCredentials; import com.google.cloud.ReadChannel; import com.google.cloud.WriteChannel; +import com.google.cloud.storage.Acl; import com.google.cloud.storage.Blob; import com.google.cloud.storage.BlobId; import com.google.cloud.storage.BlobInfo; @@ -30,7 +31,9 @@ import com.google.cloud.storage.Storage.CopyRequest; import com.google.cloud.storage.Storage.SignUrlOption; import com.google.cloud.storage.StorageOptions; +import com.google.cloud.storage.spi.StorageRpc; import com.google.cloud.storage.spi.StorageRpc.Tuple; +import com.google.common.collect.ImmutableMap; import java.io.FileOutputStream; import java.io.IOException; @@ -51,6 +54,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -75,7 +79,11 @@ * cp | * compose + | * update_metadata [key=value]* | - * sign_url "} + * sign_url | + * add-acl domain ? OWNER|READER|WRITER | + * add-acl project ? :(OWNERS|EDITORS|VIEWERS) OWNER|READER|WRITER | + * add-acl user ? |allUsers|allAuthenticatedUsers OWNER|READER|WRITER | + * add-acl group ? OWNER|READER|WRITER"} * * * @@ -87,6 +95,7 @@ public class StorageExample { private static final Map ACTIONS = new HashMap<>(); + private static final Map ACL_ACTIONS = new HashMap<>(); private abstract static class StorageAction { @@ -119,6 +128,48 @@ public String params() { } } + private static class ParentAction extends StorageAction> { + + private final Map subActions; + + ParentAction(Map subActions) { + this.subActions = ImmutableMap.copyOf(subActions); + } + + @Override + @SuppressWarnings("unchecked") + void run(Storage storage, StorageRpc.Tuple subaction) throws Exception { + subaction.x().run(storage, subaction.y()); + } + + @Override + StorageRpc.Tuple parse(String... args) throws Exception { + if (args.length >= 1) { + StorageAction action = subActions.get(args[0]); + if (action != null) { + Object actionArguments = action.parse(Arrays.copyOfRange(args, 1, args.length)); + return StorageRpc.Tuple.of(action, actionArguments); + } else { + throw new IllegalArgumentException("Unrecognized entity '" + args[0] + "'."); + } + } + throw new IllegalArgumentException("Missing required entity."); + } + + @Override + public String params() { + StringBuilder builder = new StringBuilder(); + for (Map.Entry entry : subActions.entrySet()) { + builder.append('\n').append(entry.getKey()); + String param = entry.getValue().params(); + if (param != null && !param.isEmpty()) { + builder.append(' ').append(param); + } + } + return builder.toString(); + } + } + /** * This class demonstrates how to retrieve Bucket or Blob metadata. * If more than one blob is supplied a Batch operation would be used to get all blobs metadata @@ -512,6 +563,188 @@ public String params() { } } + private abstract static class AclAction extends StorageAction> { + + @Override + public void run(Storage storage, Tuple params) { + BlobId blobId = params.x(); + Acl acl = params.y(); + if (blobId.name().isEmpty()) { + Bucket bucket = storage.get(blobId.bucket()); + if (bucket == null) { + System.out.printf("Bucket %s does not exist%n", blobId.bucket()); + return; + } + bucket.toBuilder().acl(addAcl(bucket.acl(), acl)).build().update(); + System.out.printf("Added ACL %s to bucket %s%n", acl, blobId.bucket()); + } else { + Blob blob = storage.get(blobId); + if (blob == null) { + System.out.printf("Blob %s does not exist%n", blobId); + return; + } + blob.toBuilder().acl(addAcl(blob.acl(), acl)).build().update(); + System.out.printf("Added ACL %s to blob %s%n", acl, blobId); + } + } + + private static List addAcl(List acls, Acl newAcl) { + List newAcls = new LinkedList<>(acls); + newAcls.add(newAcl); + return newAcls; + } + } + + /** + * This class demonstrates how to add an ACL to a blob or a bucket for a group of users + * (identified by the group's email). + * + * @see Access + * Control Lists (ACLs) + */ + private static class AddGroupAclAction extends AclAction { + + @Override + Tuple parse(String... args) { + if (args.length >= 3) { + BlobId blob; + int nextArg; + if (args.length == 3) { + blob = BlobId.of(args[0], ""); + nextArg = 1; + } else if (args.length == 4) { + blob = BlobId.of(args[0], args[1]); + nextArg = 2; + } else { + throw new IllegalArgumentException("Too many arguments."); + } + String group = args[nextArg++]; + Acl.Role role = Acl.Role.valueOf(args[nextArg]); + return Tuple.of(blob, Acl.of(new Acl.Group(group), role)); + } + throw new IllegalArgumentException("Missing required bucket, groupEmail or role arguments."); + } + + @Override + public String params() { + return " ? OWNER|READER|WRITER"; + } + } + + /** + * This class demonstrates how to add an ACL to a blob or a bucket for a domain. + * + * @see Access + * Control Lists (ACLs) + */ + private static class AddDomainAclAction extends AclAction { + + @Override + Tuple parse(String... args) { + if (args.length >= 3) { + BlobId blob; + int nextArg; + if (args.length == 3) { + blob = BlobId.of(args[0], ""); + nextArg = 1; + } else if (args.length == 4) { + blob = BlobId.of(args[0], args[1]); + nextArg = 2; + } else { + throw new IllegalArgumentException("Too many arguments."); + } + String domain = args[nextArg++]; + Acl.Role role = Acl.Role.valueOf(args[nextArg]); + return Tuple.of(blob, Acl.of(new Acl.Domain(domain), role)); + } + throw new IllegalArgumentException("Missing required bucket, domain or role arguments."); + } + + @Override + public String params() { + return " ? OWNER|READER|WRITER"; + } + } + + /** + * This class demonstrates how to add an ACL to a blob or a bucket for either a user (if an email + * is provided), all users (if {@code allUsers} is provided), or all authenticated users (if + * {@code allAuthenticatedUsers} is provided). + * + * @see Access + * Control Lists (ACLs) + */ + private static class AddUserAclAction extends AclAction { + + @Override + Tuple parse(String... args) { + if (args.length >= 3) { + BlobId blob; + int nextArg; + if (args.length == 3) { + blob = BlobId.of(args[0], ""); + nextArg = 1; + } else if (args.length == 4) { + blob = BlobId.of(args[0], args[1]); + nextArg = 2; + } else { + throw new IllegalArgumentException("Too many arguments."); + } + String user = args[nextArg++]; + Acl.Role role = Acl.Role.valueOf(args[nextArg]); + return Tuple.of(blob, Acl.of(new Acl.User(user), role)); + } + throw new IllegalArgumentException("Missing required bucket, userEmail or role arguments."); + } + + @Override + public String params() { + return " ? |allUsers|allAuthenticatedUsers OWNER|READER|WRITER"; + } + } + + /** + * This class demonstrates how to add an ACL to a blob or a bucket for all users that have a + * specific role in a provided project. + * + * @see Access + * Control Lists (ACLs) + */ + private static class AddProjectAclAction extends AclAction { + + @Override + Tuple parse(String... args) { + if (args.length >= 3) { + BlobId blob; + int nextArg; + if (args.length == 3) { + blob = BlobId.of(args[0], ""); + nextArg = 1; + } else if (args.length == 4) { + blob = BlobId.of(args[0], args[1]); + nextArg = 2; + } else { + throw new IllegalArgumentException("Too many arguments."); + } + String[] projectAndRole = args[nextArg++].split(":"); + if (projectAndRole.length != 2) { + throw new IllegalArgumentException( + "Project entity must be specified as :(OWNERS|READERS|WRITERS)"); + } else { + Acl.Project.ProjectRole projectRole = Acl.Project.ProjectRole.valueOf(projectAndRole[1]); + Acl.Role role = Acl.Role.valueOf(args[nextArg]); + return Tuple.of(blob, Acl.of(new Acl.Project(projectRole, projectAndRole[0]), role)); + } + } + throw new IllegalArgumentException("Missing required bucket, project or role arguments."); + } + + @Override + public String params() { + return " ? :(OWNERS|EDITORS|VIEWERS) OWNER|READER|WRITER"; + } + } + static { ACTIONS.put("info", new InfoAction()); ACTIONS.put("delete", new DeleteAction()); @@ -522,6 +755,11 @@ public String params() { ACTIONS.put("compose", new ComposeAction()); ACTIONS.put("update_metadata", new UpdateMetadataAction()); ACTIONS.put("sign_url", new SignUrlAction()); + ACL_ACTIONS.put("group", new AddGroupAclAction()); + ACL_ACTIONS.put("domain", new AddDomainAclAction()); + ACL_ACTIONS.put("user", new AddUserAclAction()); + ACL_ACTIONS.put("project", new AddProjectAclAction()); + ACTIONS.put("add-acl", new ParentAction(ACL_ACTIONS)); } private static void printUsage() { @@ -531,10 +769,11 @@ private static void printUsage() { String param = entry.getValue().params(); if (param != null && !param.isEmpty()) { - actionAndParams.append(' ').append(param); + // Add extra padding for multi-line action + actionAndParams.append(' ').append(param.replace("\n", "\n\t\t")); } } - System.out.printf("Usage: %s [] operation *%s%n", + System.out.printf("Usage: %s [] operation [entity] *%s%n", StorageExample.class.getSimpleName(), actionAndParams); } From 44cb081e08f5297ce91777210ad85e92e6516d2f Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 8 Jun 2016 17:59:42 +0200 Subject: [PATCH 2/2] Document AclAction and InfoAction run() methods --- .../cloud/examples/storage/StorageExample.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java index 0c08f7dd1c2f..d8273e65797d 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java @@ -178,6 +178,12 @@ public String params() { * @see Objects: get */ private static class InfoAction extends BlobsAction { + + /** + * Gets information for the provided blobs, using the {@code storage} service. If + * {@code blobIds} contains only one blob identity and {@code blobIds[0].name()} is empty, this + * method gets information for the bucket identified by {@code blobIds[0].bucket()}. + */ @Override public void run(Storage storage, BlobId... blobIds) { if (blobIds.length == 1) { @@ -565,6 +571,12 @@ public String params() { private abstract static class AclAction extends StorageAction> { + /** + * Sets the ACL according to the provided {@code params}, using the {@code storage} service. If + * {@code params.x()} returns a complete blob identity, the {@code params.y()} ACL is added to + * the blob. If {@code params.x().name()} is empty, the {@code params.y()} ACL is added to the + * bucket identified by {@code params.x().bucket()}. + */ @Override public void run(Storage storage, Tuple params) { BlobId blobId = params.x();