2020import com .google .cloud .AuthCredentials .ServiceAccountAuthCredentials ;
2121import com .google .cloud .ReadChannel ;
2222import com .google .cloud .WriteChannel ;
23+ import com .google .cloud .examples .compute .ComputeExample ;
24+ import com .google .cloud .storage .Acl ;
2325import com .google .cloud .storage .Blob ;
2426import com .google .cloud .storage .BlobId ;
2527import com .google .cloud .storage .BlobInfo ;
3032import com .google .cloud .storage .Storage .CopyRequest ;
3133import com .google .cloud .storage .Storage .SignUrlOption ;
3234import com .google .cloud .storage .StorageOptions ;
35+ import com .google .cloud .storage .spi .StorageRpc ;
3336import com .google .cloud .storage .spi .StorageRpc .Tuple ;
37+ import com .google .common .collect .ImmutableMap ;
3438
3539import java .io .FileOutputStream ;
3640import java .io .IOException ;
5155import java .util .Arrays ;
5256import java .util .HashMap ;
5357import java .util .Iterator ;
58+ import java .util .LinkedList ;
5459import java .util .List ;
5560import java .util .Map ;
5661import java .util .concurrent .TimeUnit ;
7580 * cp <from_bucket> <from_path> <to_bucket> <to_path> |
7681 * compose <bucket> <from_path>+ <to_path> |
7782 * update_metadata <bucket> <file> [key=value]* |
78- * sign_url <service_account_private_key_file> <service_account_email> <bucket> <path>"}</pre>
83+ * sign_url <service_account_private_key_file> <service_account_email> <bucket> <path> |
84+ * add-acl domain <bucket> <path>? <domain> OWNER|READER|WRITER |
85+ * add-acl project <bucket> <path>? <projectId>:(OWNERS|EDITORS|VIEWERS) OWNER|READER|WRITER |
86+ * add-acl user <bucket> <path>? <userEmail>|allUsers|allAuthenticatedUsers OWNER|READER|WRITER |
87+ * add-acl group <bucket> <path>? <group> OWNER|READER|WRITER"}</pre>
7988 * </li>
8089 * </ol>
8190 *
8796public class StorageExample {
8897
8998 private static final Map <String , StorageAction > ACTIONS = new HashMap <>();
99+ private static final Map <String , StorageAction > ACL_ACTIONS = new HashMap <>();
90100
91101 private abstract static class StorageAction <T > {
92102
@@ -119,6 +129,48 @@ public String params() {
119129 }
120130 }
121131
132+ private static class ParentAction extends StorageAction <StorageRpc .Tuple <StorageAction , Object >> {
133+
134+ private final Map <String , StorageAction > subActions ;
135+
136+ ParentAction (Map <String , StorageAction > subActions ) {
137+ this .subActions = ImmutableMap .copyOf (subActions );
138+ }
139+
140+ @ Override
141+ @ SuppressWarnings ("unchecked" )
142+ void run (Storage storage , StorageRpc .Tuple <StorageAction , Object > subaction ) throws Exception {
143+ subaction .x ().run (storage , subaction .y ());
144+ }
145+
146+ @ Override
147+ StorageRpc .Tuple <StorageAction , Object > parse (String ... args ) throws Exception {
148+ if (args .length >= 1 ) {
149+ StorageAction action = subActions .get (args [0 ]);
150+ if (action != null ) {
151+ Object actionArguments = action .parse (Arrays .copyOfRange (args , 1 , args .length ));
152+ return StorageRpc .Tuple .of (action , actionArguments );
153+ } else {
154+ throw new IllegalArgumentException ("Unrecognized entity '" + args [0 ] + "'." );
155+ }
156+ }
157+ throw new IllegalArgumentException ("Missing required entity." );
158+ }
159+
160+ @ Override
161+ public String params () {
162+ StringBuilder builder = new StringBuilder ();
163+ for (Map .Entry <String , StorageAction > entry : subActions .entrySet ()) {
164+ builder .append ('\n' ).append (entry .getKey ());
165+ String param = entry .getValue ().params ();
166+ if (param != null && !param .isEmpty ()) {
167+ builder .append (' ' ).append (param );
168+ }
169+ }
170+ return builder .toString ();
171+ }
172+ }
173+
122174 /**
123175 * This class demonstrates how to retrieve Bucket or Blob metadata.
124176 * If more than one blob is supplied a Batch operation would be used to get all blobs metadata
@@ -512,6 +564,188 @@ public String params() {
512564 }
513565 }
514566
567+ private abstract static class AclAction extends StorageAction <Tuple <BlobId , Acl >> {
568+
569+ @ Override
570+ public void run (Storage storage , Tuple <BlobId , Acl > params ) {
571+ BlobId blobId = params .x ();
572+ Acl acl = params .y ();
573+ if (blobId .name ().isEmpty ()) {
574+ Bucket bucket = storage .get (blobId .bucket ());
575+ if (bucket == null ) {
576+ System .out .printf ("Bucket %s does not exist%n" , blobId .bucket ());
577+ return ;
578+ }
579+ bucket .toBuilder ().acl (addAcl (bucket .acl (), acl )).build ().update ();
580+ System .out .printf ("Added ACL %s to bucket %s%n" , acl , blobId .bucket ());
581+ } else {
582+ Blob blob = storage .get (blobId );
583+ if (blob == null ) {
584+ System .out .printf ("Blob %s does not exist%n" , blobId );
585+ return ;
586+ }
587+ blob .toBuilder ().acl (addAcl (blob .acl (), acl )).build ().update ();
588+ System .out .printf ("Added ACL %s to blob %s%n" , acl , blobId );
589+ }
590+ }
591+
592+ private static List <Acl > addAcl (List <Acl > acls , Acl newAcl ) {
593+ List <Acl > newAcls = new LinkedList <>(acls );
594+ newAcls .add (newAcl );
595+ return newAcls ;
596+ }
597+ }
598+
599+ /**
600+ * This class demonstrates how to add an ACL to a blob or a bucket for a group of users
601+ * (identified by the group's email).
602+ *
603+ * @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
604+ * Control Lists (ACLs)</a>
605+ */
606+ private static class AddGroupAclAction extends AclAction {
607+
608+ @ Override
609+ Tuple <BlobId , Acl > parse (String ... args ) {
610+ if (args .length >= 3 ) {
611+ BlobId blob ;
612+ int nextArg ;
613+ if (args .length == 3 ) {
614+ blob = BlobId .of (args [0 ], "" );
615+ nextArg = 1 ;
616+ } else if (args .length == 4 ) {
617+ blob = BlobId .of (args [0 ], args [1 ]);
618+ nextArg = 2 ;
619+ } else {
620+ throw new IllegalArgumentException ("Too many arguments." );
621+ }
622+ String group = args [nextArg ++];
623+ Acl .Role role = Acl .Role .valueOf (args [nextArg ]);
624+ return Tuple .of (blob , Acl .of (new Acl .Group (group ), role ));
625+ }
626+ throw new IllegalArgumentException ("Missing required bucket, groupEmail or role arguments." );
627+ }
628+
629+ @ Override
630+ public String params () {
631+ return "<bucket> <path>? <group> OWNER|READER|WRITER" ;
632+ }
633+ }
634+
635+ /**
636+ * This class demonstrates how to add an ACL to a blob or a bucket for a domain.
637+ *
638+ * @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
639+ * Control Lists (ACLs)</a>
640+ */
641+ private static class AddDomainAclAction extends AclAction {
642+
643+ @ Override
644+ Tuple <BlobId , Acl > parse (String ... args ) {
645+ if (args .length >= 3 ) {
646+ BlobId blob ;
647+ int nextArg ;
648+ if (args .length == 3 ) {
649+ blob = BlobId .of (args [0 ], "" );
650+ nextArg = 1 ;
651+ } else if (args .length == 4 ) {
652+ blob = BlobId .of (args [0 ], args [1 ]);
653+ nextArg = 2 ;
654+ } else {
655+ throw new IllegalArgumentException ("Too many arguments." );
656+ }
657+ String domain = args [nextArg ++];
658+ Acl .Role role = Acl .Role .valueOf (args [nextArg ]);
659+ return Tuple .of (blob , Acl .of (new Acl .Domain (domain ), role ));
660+ }
661+ throw new IllegalArgumentException ("Missing required bucket, domain or role arguments." );
662+ }
663+
664+ @ Override
665+ public String params () {
666+ return "<bucket> <path>? <domain> OWNER|READER|WRITER" ;
667+ }
668+ }
669+
670+ /**
671+ * This class demonstrates how to add an ACL to a blob or a bucket for either a user (if an email
672+ * is provided), all users (if {@code allUsers} is provided), or all authenticated users (if
673+ * {@code allAuthenticatedUsers} is provided).
674+ *
675+ * @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
676+ * Control Lists (ACLs)</a>
677+ */
678+ private static class AddUserAclAction extends AclAction {
679+
680+ @ Override
681+ Tuple <BlobId , Acl > parse (String ... args ) {
682+ if (args .length >= 3 ) {
683+ BlobId blob ;
684+ int nextArg ;
685+ if (args .length == 3 ) {
686+ blob = BlobId .of (args [0 ], "" );
687+ nextArg = 1 ;
688+ } else if (args .length == 4 ) {
689+ blob = BlobId .of (args [0 ], args [1 ]);
690+ nextArg = 2 ;
691+ } else {
692+ throw new IllegalArgumentException ("Too many arguments." );
693+ }
694+ String user = args [nextArg ++];
695+ Acl .Role role = Acl .Role .valueOf (args [nextArg ]);
696+ return Tuple .of (blob , Acl .of (new Acl .User (user ), role ));
697+ }
698+ throw new IllegalArgumentException ("Missing required bucket, userEmail or role arguments." );
699+ }
700+
701+ @ Override
702+ public String params () {
703+ return "<bucket> <path>? <userEmail>|allUsers|allAuthenticatedUsers OWNER|READER|WRITER" ;
704+ }
705+ }
706+
707+ /**
708+ * This class demonstrates how to add an ACL to a blob or a bucket for all users that have a
709+ * specific role in a provided project.
710+ *
711+ * @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
712+ * Control Lists (ACLs)</a>
713+ */
714+ private static class AddProjectAclAction extends AclAction {
715+
716+ @ Override
717+ Tuple <BlobId , Acl > parse (String ... args ) {
718+ if (args .length >= 3 ) {
719+ BlobId blob ;
720+ int nextArg ;
721+ if (args .length == 3 ) {
722+ blob = BlobId .of (args [0 ], "" );
723+ nextArg = 1 ;
724+ } else if (args .length == 4 ) {
725+ blob = BlobId .of (args [0 ], args [1 ]);
726+ nextArg = 2 ;
727+ } else {
728+ throw new IllegalArgumentException ("Too many arguments." );
729+ }
730+ String [] projectAndRole = args [nextArg ++].split (":" );
731+ if (projectAndRole .length != 2 ) {
732+ throw new IllegalArgumentException (
733+ "Project entity must be specified as <projectId>:(OWNERS|READERS|WRITERS)" );
734+ } else {
735+ Acl .Project .ProjectRole projectRole = Acl .Project .ProjectRole .valueOf (projectAndRole [1 ]);
736+ Acl .Role role = Acl .Role .valueOf (args [nextArg ]);
737+ return Tuple .of (blob , Acl .of (new Acl .Project (projectRole , projectAndRole [0 ]), role ));
738+ }
739+ }
740+ throw new IllegalArgumentException ("Missing required bucket, project or role arguments." );
741+ }
742+
743+ @ Override
744+ public String params () {
745+ return "<bucket> <path>? <projectId>:(OWNERS|EDITORS|VIEWERS) OWNER|READER|WRITER" ;
746+ }
747+ }
748+
515749 static {
516750 ACTIONS .put ("info" , new InfoAction ());
517751 ACTIONS .put ("delete" , new DeleteAction ());
@@ -522,6 +756,11 @@ public String params() {
522756 ACTIONS .put ("compose" , new ComposeAction ());
523757 ACTIONS .put ("update_metadata" , new UpdateMetadataAction ());
524758 ACTIONS .put ("sign_url" , new SignUrlAction ());
759+ ACL_ACTIONS .put ("group" , new AddGroupAclAction ());
760+ ACL_ACTIONS .put ("domain" , new AddDomainAclAction ());
761+ ACL_ACTIONS .put ("user" , new AddUserAclAction ());
762+ ACL_ACTIONS .put ("project" , new AddProjectAclAction ());
763+ ACTIONS .put ("add-acl" , new ParentAction (ACL_ACTIONS ));
525764 }
526765
527766 private static void printUsage () {
@@ -531,11 +770,12 @@ private static void printUsage() {
531770
532771 String param = entry .getValue ().params ();
533772 if (param != null && !param .isEmpty ()) {
534- actionAndParams .append (' ' ).append (param );
773+ // Add extra padding for multi-line action
774+ actionAndParams .append (' ' ).append (param .replace ("\n " , "\n \t \t " ));
535775 }
536776 }
537- System .out .printf ("Usage: %s [<project_id>] operation <args>*%s%n" ,
538- StorageExample .class .getSimpleName (), actionAndParams );
777+ System .out .printf ("Usage: %s [<project_id>] operation [entity] <args>*%s%n" ,
778+ ComputeExample .class .getSimpleName (), actionAndParams );
539779 }
540780
541781 @ SuppressWarnings ("unchecked" )
0 commit comments