Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend StorageExample to show how to add ACLs to blobs and buckets #1033

Merged
merged 2 commits into from
Jun 8, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -75,7 +79,11 @@
* cp <from_bucket> <from_path> <to_bucket> <to_path> |
* compose <bucket> <from_path>+ <to_path> |
* update_metadata <bucket> <file> [key=value]* |
* sign_url <service_account_private_key_file> <service_account_email> <bucket> <path>"}</pre>
* sign_url <service_account_private_key_file> <service_account_email> <bucket> <path> |
* add-acl domain <bucket> <path>? <domain> OWNER|READER|WRITER |
* add-acl project <bucket> <path>? <projectId>:(OWNERS|EDITORS|VIEWERS) OWNER|READER|WRITER |
* add-acl user <bucket> <path>? <userEmail>|allUsers|allAuthenticatedUsers OWNER|READER|WRITER |
* add-acl group <bucket> <path>? <group> OWNER|READER|WRITER"}</pre>
* </li>
* </ol>
*
Expand All @@ -87,6 +95,7 @@
public class StorageExample {

private static final Map<String, StorageAction> ACTIONS = new HashMap<>();
private static final Map<String, StorageAction> ACL_ACTIONS = new HashMap<>();

private abstract static class StorageAction<T> {

Expand Down Expand Up @@ -119,6 +128,48 @@ public String params() {
}
}

private static class ParentAction extends StorageAction<StorageRpc.Tuple<StorageAction, Object>> {

private final Map<String, StorageAction> subActions;

ParentAction(Map<String, StorageAction> subActions) {
this.subActions = ImmutableMap.copyOf(subActions);
}

@Override
@SuppressWarnings("unchecked")
void run(Storage storage, StorageRpc.Tuple<StorageAction, Object> subaction) throws Exception {
subaction.x().run(storage, subaction.y());
}

@Override
StorageRpc.Tuple<StorageAction, Object> 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<String, StorageAction> 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
Expand All @@ -127,6 +178,12 @@ public String params() {
* @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/get">Objects: get</a>
*/
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) {
Expand Down Expand Up @@ -512,6 +569,194 @@ public String params() {
}
}

private abstract static class AclAction extends StorageAction<Tuple<BlobId, Acl>> {

/**
* 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<BlobId, Acl> params) {

This comment was marked as spam.

This comment was marked as spam.

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<Acl> addAcl(List<Acl> acls, Acl newAcl) {
List<Acl> 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).

This comment was marked as spam.

This comment was marked as spam.

*
* @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
* Control Lists (ACLs)</a>
*/
private static class AddGroupAclAction extends AclAction {

@Override
Tuple<BlobId, Acl> parse(String... args) {

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

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));

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

}
throw new IllegalArgumentException("Missing required bucket, groupEmail or role arguments.");
}

@Override
public String params() {
return "<bucket> <path>? <group> OWNER|READER|WRITER";
}
}

/**
* This class demonstrates how to add an ACL to a blob or a bucket for a domain.

This comment was marked as spam.

This comment was marked as spam.

*
* @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
* Control Lists (ACLs)</a>
*/
private static class AddDomainAclAction extends AclAction {

@Override
Tuple<BlobId, Acl> 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));

This comment was marked as spam.

}
throw new IllegalArgumentException("Missing required bucket, domain or role arguments.");
}

@Override
public String params() {
return "<bucket> <path>? <domain> 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 <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
* Control Lists (ACLs)</a>
*/
private static class AddUserAclAction extends AclAction {

@Override
Tuple<BlobId, Acl> 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 "<bucket> <path>? <userEmail>|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 <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
* Control Lists (ACLs)</a>
*/
private static class AddProjectAclAction extends AclAction {

@Override
Tuple<BlobId, Acl> 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 <projectId>:(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 "<bucket> <path>? <projectId>:(OWNERS|EDITORS|VIEWERS) OWNER|READER|WRITER";
}
}

static {
ACTIONS.put("info", new InfoAction());
ACTIONS.put("delete", new DeleteAction());
Expand All @@ -522,6 +767,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() {
Expand All @@ -531,10 +781,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 [<project_id>] operation <args>*%s%n",
System.out.printf("Usage: %s [<project_id>] operation [entity] <args>*%s%n",
StorageExample.class.getSimpleName(), actionAndParams);
}

Expand Down