2020import com .google .cloud .AuthCredentials .ServiceAccountAuthCredentials ;
2121import com .google .cloud .ReadChannel ;
2222import com .google .cloud .WriteChannel ;
23+ import com .google .cloud .storage .Acl ;
2324import com .google .cloud .storage .Blob ;
2425import com .google .cloud .storage .BlobId ;
2526import com .google .cloud .storage .BlobInfo ;
3031import com .google .cloud .storage .Storage .CopyRequest ;
3132import com .google .cloud .storage .Storage .SignUrlOption ;
3233import com .google .cloud .storage .StorageOptions ;
34+ import com .google .cloud .storage .spi .StorageRpc ;
3335import com .google .cloud .storage .spi .StorageRpc .Tuple ;
36+ import com .google .common .collect .ImmutableMap ;
3437
3538import java .io .FileOutputStream ;
3639import java .io .IOException ;
5154import java .util .Arrays ;
5255import java .util .HashMap ;
5356import java .util .Iterator ;
57+ import java .util .LinkedList ;
5458import java .util .List ;
5559import java .util .Map ;
5660import java .util .concurrent .TimeUnit ;
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 *
8795public 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
@@ -512,6 +563,188 @@ public String params() {
512563 }
513564 }
514565
566+ private abstract static class AclAction extends StorageAction <Tuple <BlobId , Acl >> {
567+
568+ @ Override
569+ public void run (Storage storage , Tuple <BlobId , Acl > params ) {
570+ BlobId blobId = params .x ();
571+ Acl acl = params .y ();
572+ if (blobId .name ().isEmpty ()) {
573+ Bucket bucket = storage .get (blobId .bucket ());
574+ if (bucket == null ) {
575+ System .out .printf ("Bucket %s does not exist%n" , blobId .bucket ());
576+ return ;
577+ }
578+ bucket .toBuilder ().acl (addAcl (bucket .acl (), acl )).build ().update ();
579+ System .out .printf ("Added ACL %s to bucket %s%n" , acl , blobId .bucket ());
580+ } else {
581+ Blob blob = storage .get (blobId );
582+ if (blob == null ) {
583+ System .out .printf ("Blob %s does not exist%n" , blobId );
584+ return ;
585+ }
586+ blob .toBuilder ().acl (addAcl (blob .acl (), acl )).build ().update ();
587+ System .out .printf ("Added ACL %s to blob %s%n" , acl , blobId );
588+ }
589+ }
590+
591+ private static List <Acl > addAcl (List <Acl > acls , Acl newAcl ) {
592+ List <Acl > newAcls = new LinkedList <>(acls );
593+ newAcls .add (newAcl );
594+ return newAcls ;
595+ }
596+ }
597+
598+ /**
599+ * This class demonstrates how to add an ACL to a blob or a bucket for a group of users
600+ * (identified by the group's email).
601+ *
602+ * @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
603+ * Control Lists (ACLs)</a>
604+ */
605+ private static class AddGroupAclAction extends AclAction {
606+
607+ @ Override
608+ Tuple <BlobId , Acl > parse (String ... args ) {
609+ if (args .length >= 3 ) {
610+ BlobId blob ;
611+ int nextArg ;
612+ if (args .length == 3 ) {
613+ blob = BlobId .of (args [0 ], "" );
614+ nextArg = 1 ;
615+ } else if (args .length == 4 ) {
616+ blob = BlobId .of (args [0 ], args [1 ]);
617+ nextArg = 2 ;
618+ } else {
619+ throw new IllegalArgumentException ("Too many arguments." );
620+ }
621+ String group = args [nextArg ++];
622+ Acl .Role role = Acl .Role .valueOf (args [nextArg ]);
623+ return Tuple .of (blob , Acl .of (new Acl .Group (group ), role ));
624+ }
625+ throw new IllegalArgumentException ("Missing required bucket, groupEmail or role arguments." );
626+ }
627+
628+ @ Override
629+ public String params () {
630+ return "<bucket> <path>? <group> OWNER|READER|WRITER" ;
631+ }
632+ }
633+
634+ /**
635+ * This class demonstrates how to add an ACL to a blob or a bucket for a domain.
636+ *
637+ * @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
638+ * Control Lists (ACLs)</a>
639+ */
640+ private static class AddDomainAclAction extends AclAction {
641+
642+ @ Override
643+ Tuple <BlobId , Acl > parse (String ... args ) {
644+ if (args .length >= 3 ) {
645+ BlobId blob ;
646+ int nextArg ;
647+ if (args .length == 3 ) {
648+ blob = BlobId .of (args [0 ], "" );
649+ nextArg = 1 ;
650+ } else if (args .length == 4 ) {
651+ blob = BlobId .of (args [0 ], args [1 ]);
652+ nextArg = 2 ;
653+ } else {
654+ throw new IllegalArgumentException ("Too many arguments." );
655+ }
656+ String domain = args [nextArg ++];
657+ Acl .Role role = Acl .Role .valueOf (args [nextArg ]);
658+ return Tuple .of (blob , Acl .of (new Acl .Domain (domain ), role ));
659+ }
660+ throw new IllegalArgumentException ("Missing required bucket, domain or role arguments." );
661+ }
662+
663+ @ Override
664+ public String params () {
665+ return "<bucket> <path>? <domain> OWNER|READER|WRITER" ;
666+ }
667+ }
668+
669+ /**
670+ * This class demonstrates how to add an ACL to a blob or a bucket for either a user (if an email
671+ * is provided), all users (if {@code allUsers} is provided), or all authenticated users (if
672+ * {@code allAuthenticatedUsers} is provided).
673+ *
674+ * @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
675+ * Control Lists (ACLs)</a>
676+ */
677+ private static class AddUserAclAction extends AclAction {
678+
679+ @ Override
680+ Tuple <BlobId , Acl > parse (String ... args ) {
681+ if (args .length >= 3 ) {
682+ BlobId blob ;
683+ int nextArg ;
684+ if (args .length == 3 ) {
685+ blob = BlobId .of (args [0 ], "" );
686+ nextArg = 1 ;
687+ } else if (args .length == 4 ) {
688+ blob = BlobId .of (args [0 ], args [1 ]);
689+ nextArg = 2 ;
690+ } else {
691+ throw new IllegalArgumentException ("Too many arguments." );
692+ }
693+ String user = args [nextArg ++];
694+ Acl .Role role = Acl .Role .valueOf (args [nextArg ]);
695+ return Tuple .of (blob , Acl .of (new Acl .User (user ), role ));
696+ }
697+ throw new IllegalArgumentException ("Missing required bucket, userEmail or role arguments." );
698+ }
699+
700+ @ Override
701+ public String params () {
702+ return "<bucket> <path>? <userEmail>|allUsers|allAuthenticatedUsers OWNER|READER|WRITER" ;
703+ }
704+ }
705+
706+ /**
707+ * This class demonstrates how to add an ACL to a blob or a bucket for all users that have a
708+ * specific role in a provided project.
709+ *
710+ * @see <a href="https://cloud.google.com/storage/docs/access-control/lists#permissions">Access
711+ * Control Lists (ACLs)</a>
712+ */
713+ private static class AddProjectAclAction extends AclAction {
714+
715+ @ Override
716+ Tuple <BlobId , Acl > parse (String ... args ) {
717+ if (args .length >= 3 ) {
718+ BlobId blob ;
719+ int nextArg ;
720+ if (args .length == 3 ) {
721+ blob = BlobId .of (args [0 ], "" );
722+ nextArg = 1 ;
723+ } else if (args .length == 4 ) {
724+ blob = BlobId .of (args [0 ], args [1 ]);
725+ nextArg = 2 ;
726+ } else {
727+ throw new IllegalArgumentException ("Too many arguments." );
728+ }
729+ String [] projectAndRole = args [nextArg ++].split (":" );
730+ if (projectAndRole .length != 2 ) {
731+ throw new IllegalArgumentException (
732+ "Project entity must be specified as <projectId>:(OWNERS|READERS|WRITERS)" );
733+ } else {
734+ Acl .Project .ProjectRole projectRole = Acl .Project .ProjectRole .valueOf (projectAndRole [1 ]);
735+ Acl .Role role = Acl .Role .valueOf (args [nextArg ]);
736+ return Tuple .of (blob , Acl .of (new Acl .Project (projectRole , projectAndRole [0 ]), role ));
737+ }
738+ }
739+ throw new IllegalArgumentException ("Missing required bucket, project or role arguments." );
740+ }
741+
742+ @ Override
743+ public String params () {
744+ return "<bucket> <path>? <projectId>:(OWNERS|EDITORS|VIEWERS) OWNER|READER|WRITER" ;
745+ }
746+ }
747+
515748 static {
516749 ACTIONS .put ("info" , new InfoAction ());
517750 ACTIONS .put ("delete" , new DeleteAction ());
@@ -522,6 +755,11 @@ public String params() {
522755 ACTIONS .put ("compose" , new ComposeAction ());
523756 ACTIONS .put ("update_metadata" , new UpdateMetadataAction ());
524757 ACTIONS .put ("sign_url" , new SignUrlAction ());
758+ ACL_ACTIONS .put ("group" , new AddGroupAclAction ());
759+ ACL_ACTIONS .put ("domain" , new AddDomainAclAction ());
760+ ACL_ACTIONS .put ("user" , new AddUserAclAction ());
761+ ACL_ACTIONS .put ("project" , new AddProjectAclAction ());
762+ ACTIONS .put ("add-acl" , new ParentAction (ACL_ACTIONS ));
525763 }
526764
527765 private static void printUsage () {
@@ -531,10 +769,11 @@ private static void printUsage() {
531769
532770 String param = entry .getValue ().params ();
533771 if (param != null && !param .isEmpty ()) {
534- actionAndParams .append (' ' ).append (param );
772+ // Add extra padding for multi-line action
773+ actionAndParams .append (' ' ).append (param .replace ("\n " , "\n \t \t " ));
535774 }
536775 }
537- System .out .printf ("Usage: %s [<project_id>] operation <args>*%s%n" ,
776+ System .out .printf ("Usage: %s [<project_id>] operation [entity] <args>*%s%n" ,
538777 StorageExample .class .getSimpleName (), actionAndParams );
539778 }
540779
0 commit comments