Skip to content

Commit f176d81

Browse files
committed
Extend StorageExample to show how to add ACLs to blobs and buckets (#1033)
1 parent eb6f182 commit f176d81

File tree

1 file changed

+254
-3
lines changed

1 file changed

+254
-3
lines changed

gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/StorageExample.java

Lines changed: 254 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.cloud.AuthCredentials.ServiceAccountAuthCredentials;
2121
import com.google.cloud.ReadChannel;
2222
import com.google.cloud.WriteChannel;
23+
import com.google.cloud.storage.Acl;
2324
import com.google.cloud.storage.Blob;
2425
import com.google.cloud.storage.BlobId;
2526
import com.google.cloud.storage.BlobInfo;
@@ -30,7 +31,9 @@
3031
import com.google.cloud.storage.Storage.CopyRequest;
3132
import com.google.cloud.storage.Storage.SignUrlOption;
3233
import com.google.cloud.storage.StorageOptions;
34+
import com.google.cloud.storage.spi.StorageRpc;
3335
import com.google.cloud.storage.spi.StorageRpc.Tuple;
36+
import com.google.common.collect.ImmutableMap;
3437

3538
import java.io.FileOutputStream;
3639
import java.io.IOException;
@@ -51,6 +54,7 @@
5154
import java.util.Arrays;
5255
import java.util.HashMap;
5356
import java.util.Iterator;
57+
import java.util.LinkedList;
5458
import java.util.List;
5559
import java.util.Map;
5660
import java.util.concurrent.TimeUnit;
@@ -75,7 +79,11 @@
7579
* cp <from_bucket> <from_path> <to_bucket> <to_path> |
7680
* compose <bucket> <from_path>+ <to_path> |
7781
* update_metadata <bucket> <file> [key=value]* |
78-
* sign_url <service_account_private_key_file> <service_account_email> <bucket> <path>"}</pre>
82+
* sign_url <service_account_private_key_file> <service_account_email> <bucket> <path> |
83+
* add-acl domain <bucket> <path>? <domain> OWNER|READER|WRITER |
84+
* add-acl project <bucket> <path>? <projectId>:(OWNERS|EDITORS|VIEWERS) OWNER|READER|WRITER |
85+
* add-acl user <bucket> <path>? <userEmail>|allUsers|allAuthenticatedUsers OWNER|READER|WRITER |
86+
* add-acl group <bucket> <path>? <group> OWNER|READER|WRITER"}</pre>
7987
* </li>
8088
* </ol>
8189
*
@@ -87,6 +95,7 @@
8795
public class StorageExample {
8896

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

91100
private abstract static class StorageAction<T> {
92101

@@ -119,6 +128,48 @@ public String params() {
119128
}
120129
}
121130

131+
private static class ParentAction extends StorageAction<StorageRpc.Tuple<StorageAction, Object>> {
132+
133+
private final Map<String, StorageAction> subActions;
134+
135+
ParentAction(Map<String, StorageAction> subActions) {
136+
this.subActions = ImmutableMap.copyOf(subActions);
137+
}
138+
139+
@Override
140+
@SuppressWarnings("unchecked")
141+
void run(Storage storage, StorageRpc.Tuple<StorageAction, Object> subaction) throws Exception {
142+
subaction.x().run(storage, subaction.y());
143+
}
144+
145+
@Override
146+
StorageRpc.Tuple<StorageAction, Object> parse(String... args) throws Exception {
147+
if (args.length >= 1) {
148+
StorageAction action = subActions.get(args[0]);
149+
if (action != null) {
150+
Object actionArguments = action.parse(Arrays.copyOfRange(args, 1, args.length));
151+
return StorageRpc.Tuple.of(action, actionArguments);
152+
} else {
153+
throw new IllegalArgumentException("Unrecognized entity '" + args[0] + "'.");
154+
}
155+
}
156+
throw new IllegalArgumentException("Missing required entity.");
157+
}
158+
159+
@Override
160+
public String params() {
161+
StringBuilder builder = new StringBuilder();
162+
for (Map.Entry<String, StorageAction> entry : subActions.entrySet()) {
163+
builder.append('\n').append(entry.getKey());
164+
String param = entry.getValue().params();
165+
if (param != null && !param.isEmpty()) {
166+
builder.append(' ').append(param);
167+
}
168+
}
169+
return builder.toString();
170+
}
171+
}
172+
122173
/**
123174
* This class demonstrates how to retrieve Bucket or Blob metadata.
124175
* If more than one blob is supplied a Batch operation would be used to get all blobs metadata
@@ -127,6 +178,12 @@ public String params() {
127178
* @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/get">Objects: get</a>
128179
*/
129180
private static class InfoAction extends BlobsAction {
181+
182+
/**
183+
* Gets information for the provided blobs, using the {@code storage} service. If
184+
* {@code blobIds} contains only one blob identity and {@code blobIds[0].name()} is empty, this
185+
* method gets information for the bucket identified by {@code blobIds[0].bucket()}.
186+
*/
130187
@Override
131188
public void run(Storage storage, BlobId... blobIds) {
132189
if (blobIds.length == 1) {
@@ -512,6 +569,194 @@ public String params() {
512569
}
513570
}
514571

572+
private abstract static class AclAction extends StorageAction<Tuple<BlobId, Acl>> {
573+
574+
/**
575+
* Sets the ACL according to the provided {@code params}, using the {@code storage} service. If
576+
* {@code params.x()} returns a complete blob identity, the {@code params.y()} ACL is added to
577+
* the blob. If {@code params.x().name()} is empty, the {@code params.y()} ACL is added to the
578+
* bucket identified by {@code params.x().bucket()}.
579+
*/
580+
@Override
581+
public void run(Storage storage, Tuple<BlobId, Acl> params) {
582+
BlobId blobId = params.x();
583+
Acl acl = params.y();
584+
if (blobId.name().isEmpty()) {
585+
Bucket bucket = storage.get(blobId.bucket());
586+
if (bucket == null) {
587+
System.out.printf("Bucket %s does not exist%n", blobId.bucket());
588+
return;
589+
}
590+
bucket.toBuilder().acl(addAcl(bucket.acl(), acl)).build().update();
591+
System.out.printf("Added ACL %s to bucket %s%n", acl, blobId.bucket());
592+
} else {
593+
Blob blob = storage.get(blobId);
594+
if (blob == null) {
595+
System.out.printf("Blob %s does not exist%n", blobId);
596+
return;
597+
}
598+
blob.toBuilder().acl(addAcl(blob.acl(), acl)).build().update();
599+
System.out.printf("Added ACL %s to blob %s%n", acl, blobId);
600+
}
601+
}
602+
603+
private static List<Acl> addAcl(List<Acl> acls, Acl newAcl) {
604+
List<Acl> newAcls = new LinkedList<>(acls);
605+
newAcls.add(newAcl);
606+
return newAcls;
607+
}
608+
}
609+
610+
/**
611+
* This class demonstrates how to add an ACL to a blob or a bucket for a group of users
612+
* (identified by the group's email).
613+
*
614+
* @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
615+
* Control Lists (ACLs)</a>
616+
*/
617+
private static class AddGroupAclAction extends AclAction {
618+
619+
@Override
620+
Tuple<BlobId, Acl> parse(String... args) {
621+
if (args.length >= 3) {
622+
BlobId blob;
623+
int nextArg;
624+
if (args.length == 3) {
625+
blob = BlobId.of(args[0], "");
626+
nextArg = 1;
627+
} else if (args.length == 4) {
628+
blob = BlobId.of(args[0], args[1]);
629+
nextArg = 2;
630+
} else {
631+
throw new IllegalArgumentException("Too many arguments.");
632+
}
633+
String group = args[nextArg++];
634+
Acl.Role role = Acl.Role.valueOf(args[nextArg]);
635+
return Tuple.of(blob, Acl.of(new Acl.Group(group), role));
636+
}
637+
throw new IllegalArgumentException("Missing required bucket, groupEmail or role arguments.");
638+
}
639+
640+
@Override
641+
public String params() {
642+
return "<bucket> <path>? <group> OWNER|READER|WRITER";
643+
}
644+
}
645+
646+
/**
647+
* This class demonstrates how to add an ACL to a blob or a bucket for a domain.
648+
*
649+
* @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
650+
* Control Lists (ACLs)</a>
651+
*/
652+
private static class AddDomainAclAction extends AclAction {
653+
654+
@Override
655+
Tuple<BlobId, Acl> parse(String... args) {
656+
if (args.length >= 3) {
657+
BlobId blob;
658+
int nextArg;
659+
if (args.length == 3) {
660+
blob = BlobId.of(args[0], "");
661+
nextArg = 1;
662+
} else if (args.length == 4) {
663+
blob = BlobId.of(args[0], args[1]);
664+
nextArg = 2;
665+
} else {
666+
throw new IllegalArgumentException("Too many arguments.");
667+
}
668+
String domain = args[nextArg++];
669+
Acl.Role role = Acl.Role.valueOf(args[nextArg]);
670+
return Tuple.of(blob, Acl.of(new Acl.Domain(domain), role));
671+
}
672+
throw new IllegalArgumentException("Missing required bucket, domain or role arguments.");
673+
}
674+
675+
@Override
676+
public String params() {
677+
return "<bucket> <path>? <domain> OWNER|READER|WRITER";
678+
}
679+
}
680+
681+
/**
682+
* This class demonstrates how to add an ACL to a blob or a bucket for either a user (if an email
683+
* is provided), all users (if {@code allUsers} is provided), or all authenticated users (if
684+
* {@code allAuthenticatedUsers} is provided).
685+
*
686+
* @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
687+
* Control Lists (ACLs)</a>
688+
*/
689+
private static class AddUserAclAction extends AclAction {
690+
691+
@Override
692+
Tuple<BlobId, Acl> parse(String... args) {
693+
if (args.length >= 3) {
694+
BlobId blob;
695+
int nextArg;
696+
if (args.length == 3) {
697+
blob = BlobId.of(args[0], "");
698+
nextArg = 1;
699+
} else if (args.length == 4) {
700+
blob = BlobId.of(args[0], args[1]);
701+
nextArg = 2;
702+
} else {
703+
throw new IllegalArgumentException("Too many arguments.");
704+
}
705+
String user = args[nextArg++];
706+
Acl.Role role = Acl.Role.valueOf(args[nextArg]);
707+
return Tuple.of(blob, Acl.of(new Acl.User(user), role));
708+
}
709+
throw new IllegalArgumentException("Missing required bucket, userEmail or role arguments.");
710+
}
711+
712+
@Override
713+
public String params() {
714+
return "<bucket> <path>? <userEmail>|allUsers|allAuthenticatedUsers OWNER|READER|WRITER";
715+
}
716+
}
717+
718+
/**
719+
* This class demonstrates how to add an ACL to a blob or a bucket for all users that have a
720+
* specific role in a provided project.
721+
*
722+
* @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
723+
* Control Lists (ACLs)</a>
724+
*/
725+
private static class AddProjectAclAction extends AclAction {
726+
727+
@Override
728+
Tuple<BlobId, Acl> parse(String... args) {
729+
if (args.length >= 3) {
730+
BlobId blob;
731+
int nextArg;
732+
if (args.length == 3) {
733+
blob = BlobId.of(args[0], "");
734+
nextArg = 1;
735+
} else if (args.length == 4) {
736+
blob = BlobId.of(args[0], args[1]);
737+
nextArg = 2;
738+
} else {
739+
throw new IllegalArgumentException("Too many arguments.");
740+
}
741+
String[] projectAndRole = args[nextArg++].split(":");
742+
if (projectAndRole.length != 2) {
743+
throw new IllegalArgumentException(
744+
"Project entity must be specified as <projectId>:(OWNERS|READERS|WRITERS)");
745+
} else {
746+
Acl.Project.ProjectRole projectRole = Acl.Project.ProjectRole.valueOf(projectAndRole[1]);
747+
Acl.Role role = Acl.Role.valueOf(args[nextArg]);
748+
return Tuple.of(blob, Acl.of(new Acl.Project(projectRole, projectAndRole[0]), role));
749+
}
750+
}
751+
throw new IllegalArgumentException("Missing required bucket, project or role arguments.");
752+
}
753+
754+
@Override
755+
public String params() {
756+
return "<bucket> <path>? <projectId>:(OWNERS|EDITORS|VIEWERS) OWNER|READER|WRITER";
757+
}
758+
}
759+
515760
static {
516761
ACTIONS.put("info", new InfoAction());
517762
ACTIONS.put("delete", new DeleteAction());
@@ -522,6 +767,11 @@ public String params() {
522767
ACTIONS.put("compose", new ComposeAction());
523768
ACTIONS.put("update_metadata", new UpdateMetadataAction());
524769
ACTIONS.put("sign_url", new SignUrlAction());
770+
ACL_ACTIONS.put("group", new AddGroupAclAction());
771+
ACL_ACTIONS.put("domain", new AddDomainAclAction());
772+
ACL_ACTIONS.put("user", new AddUserAclAction());
773+
ACL_ACTIONS.put("project", new AddProjectAclAction());
774+
ACTIONS.put("add-acl", new ParentAction(ACL_ACTIONS));
525775
}
526776

527777
private static void printUsage() {
@@ -531,10 +781,11 @@ private static void printUsage() {
531781

532782
String param = entry.getValue().params();
533783
if (param != null && !param.isEmpty()) {
534-
actionAndParams.append(' ').append(param);
784+
// Add extra padding for multi-line action
785+
actionAndParams.append(' ').append(param.replace("\n", "\n\t\t"));
535786
}
536787
}
537-
System.out.printf("Usage: %s [<project_id>] operation <args>*%s%n",
788+
System.out.printf("Usage: %s [<project_id>] operation [entity] <args>*%s%n",
538789
StorageExample.class.getSimpleName(), actionAndParams);
539790
}
540791

0 commit comments

Comments
 (0)