1919import static com .google .cloud .storage .ByteSizeConstants ._16MiB ;
2020import static com .google .cloud .storage .ByteSizeConstants ._256KiB ;
2121import static com .google .cloud .storage .GrpcToHttpStatusCodeTranslation .resultRetryAlgorithmToCodes ;
22+ import static com .google .cloud .storage .StorageV2ProtoUtils .bucketAclEntityOrAltEq ;
2223import static com .google .cloud .storage .StorageV2ProtoUtils .objectAclEntityOrAltEq ;
2324import static com .google .cloud .storage .Utils .bucketNameCodec ;
2425import static com .google .cloud .storage .Utils .ifNonNull ;
7677import com .google .iam .v1 .TestIamPermissionsRequest ;
7778import com .google .protobuf .ByteString ;
7879import com .google .protobuf .FieldMask ;
80+ import com .google .storage .v2 .BucketAccessControl ;
7981import com .google .storage .v2 .ComposeObjectRequest ;
8082import com .google .storage .v2 .ComposeObjectRequest .SourceObject ;
8183import com .google .storage .v2 .CreateBucketRequest ;
@@ -152,6 +154,7 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
152154 StandardOpenOption .WRITE ,
153155 StandardOpenOption .CREATE ,
154156 StandardOpenOption .TRUNCATE_EXISTING );
157+ private static final BucketSourceOption [] EMPTY_BUCKET_SOURCE_OPTIONS = new BucketSourceOption [0 ];
155158
156159 final StorageClient storageClient ;
157160 final GrpcConversions codecs ;
@@ -853,63 +856,148 @@ public List<Boolean> delete(Iterable<BlobId> blobIds) {
853856
854857 @ Override
855858 public Acl getAcl (String bucket , Entity entity , BucketSourceOption ... options ) {
856- return throwNotYetImplemented (
857- fmtMethodName ("getAcl" , String .class , Entity .class , BucketSourceOption [].class ));
859+ try {
860+ Opts <BucketSourceOpt > opts = Opts .unwrap (options );
861+ com .google .storage .v2 .Bucket resp = getBucketWithAcls (bucket , opts );
862+
863+ Predicate <BucketAccessControl > entityPredicate =
864+ bucketAclEntityOrAltEq (codecs .entity ().encode (entity ));
865+
866+ Optional <BucketAccessControl > first =
867+ resp .getAclList ().stream ().filter (entityPredicate ).findFirst ();
868+
869+ // HttpStorageRpc defaults to null if Not Found
870+ return first .map (codecs .bucketAcl ()::decode ).orElse (null );
871+ } catch (NotFoundException e ) {
872+ return null ;
873+ } catch (StorageException se ) {
874+ if (se .getCode () == 404 ) {
875+ return null ;
876+ } else {
877+ throw se ;
878+ }
879+ }
858880 }
859881
860882 @ Override
861883 public Acl getAcl (String bucket , Entity entity ) {
862- return throwNotYetImplemented ( fmtMethodName ( " getAcl" , String . class , Entity . class ) );
884+ return getAcl ( bucket , entity , EMPTY_BUCKET_SOURCE_OPTIONS );
863885 }
864886
865887 @ Override
866888 public boolean deleteAcl (String bucket , Entity entity , BucketSourceOption ... options ) {
867- return throwNotYetImplemented (
868- fmtMethodName ("deleteAcl" , String .class , Entity .class , BucketSourceOption [].class ));
889+ try {
890+ Opts <BucketSourceOpt > opts = Opts .unwrap (options );
891+ com .google .storage .v2 .Bucket resp = getBucketWithAcls (bucket , opts );
892+ String encode = codecs .entity ().encode (entity );
893+
894+ Predicate <BucketAccessControl > entityPredicate = bucketAclEntityOrAltEq (encode );
895+
896+ List <BucketAccessControl > currentAcls = resp .getAclList ();
897+ ImmutableList <BucketAccessControl > newAcls =
898+ currentAcls .stream ()
899+ .filter (entityPredicate .negate ())
900+ .collect (ImmutableList .toImmutableList ());
901+ if (newAcls .equals (currentAcls )) {
902+ // we didn't actually filter anything out, no need to send an RPC, simply return false
903+ return false ;
904+ }
905+ long metageneration = resp .getMetageneration ();
906+
907+ UpdateBucketRequest req = createUpdateAclRequest (bucket , newAcls , metageneration );
908+
909+ com .google .storage .v2 .Bucket updateResult = updateBucket (req );
910+ // read the response to ensure there is no longer an acl for the specified entity
911+ Optional <BucketAccessControl > first =
912+ updateResult .getAclList ().stream ().filter (entityPredicate ).findFirst ();
913+ return !first .isPresent ();
914+ } catch (NotFoundException e ) {
915+ // HttpStorageRpc returns false if the bucket doesn't exist :(
916+ return false ;
917+ } catch (StorageException se ) {
918+ if (se .getCode () == 404 ) {
919+ return false ;
920+ } else {
921+ throw se ;
922+ }
923+ }
869924 }
870925
871926 @ Override
872927 public boolean deleteAcl (String bucket , Entity entity ) {
873- return throwNotYetImplemented ( fmtMethodName ( " deleteAcl" , String . class , Entity . class ) );
928+ return deleteAcl ( bucket , entity , EMPTY_BUCKET_SOURCE_OPTIONS );
874929 }
875930
876931 @ Override
877932 public Acl createAcl (String bucket , Acl acl , BucketSourceOption ... options ) {
878- return throwNotYetImplemented (
879- fmtMethodName ("createAcl" , String .class , Acl .class , BucketSourceOption [].class ));
933+ return updateAcl (bucket , acl , options );
880934 }
881935
882936 @ Override
883937 public Acl createAcl (String bucket , Acl acl ) {
884- return throwNotYetImplemented ( fmtMethodName ( " createAcl" , String . class , Acl . class ) );
938+ return createAcl ( bucket , acl , EMPTY_BUCKET_SOURCE_OPTIONS );
885939 }
886940
887941 @ Override
888942 public Acl updateAcl (String bucket , Acl acl , BucketSourceOption ... options ) {
889- return throwNotYetImplemented (
890- fmtMethodName ("updateAcl" , String .class , Acl .class , BucketSourceOption [].class ));
943+ try {
944+ Opts <BucketSourceOpt > opts = Opts .unwrap (options );
945+ com .google .storage .v2 .Bucket resp = getBucketWithAcls (bucket , opts );
946+ BucketAccessControl encode = codecs .bucketAcl ().encode (acl );
947+ String entity = encode .getEntity ();
948+
949+ Predicate <BucketAccessControl > entityPredicate = bucketAclEntityOrAltEq (entity );
950+
951+ ImmutableList <BucketAccessControl > newDefaultAcls =
952+ Streams .concat (
953+ resp .getAclList ().stream ().filter (entityPredicate .negate ()), Stream .of (encode ))
954+ .collect (ImmutableList .toImmutableList ());
955+
956+ UpdateBucketRequest req =
957+ createUpdateAclRequest (bucket , newDefaultAcls , resp .getMetageneration ());
958+
959+ com .google .storage .v2 .Bucket updateResult = updateBucket (req );
960+
961+ Optional <Acl > first =
962+ updateResult .getAclList ().stream ()
963+ .filter (entityPredicate )
964+ .findFirst ()
965+ .map (codecs .bucketAcl ()::decode );
966+
967+ return first .orElseThrow (
968+ () -> new StorageException (0 , "Acl update call success, but not in response" ));
969+ } catch (NotFoundException e ) {
970+ throw StorageException .coalesce (e );
971+ }
891972 }
892973
893974 @ Override
894975 public Acl updateAcl (String bucket , Acl acl ) {
895- return throwNotYetImplemented ( fmtMethodName ( " updateAcl" , String . class , Acl . class ) );
976+ return updateAcl ( bucket , acl , EMPTY_BUCKET_SOURCE_OPTIONS );
896977 }
897978
898979 @ Override
899980 public List <Acl > listAcls (String bucket , BucketSourceOption ... options ) {
900- return throwNotYetImplemented (
901- fmtMethodName ("listAcls" , String .class , BucketSourceOption [].class ));
981+ try {
982+ Opts <BucketSourceOpt > opts = Opts .unwrap (options );
983+ com .google .storage .v2 .Bucket resp = getBucketWithAcls (bucket , opts );
984+ return resp .getAclList ().stream ()
985+ .map (codecs .bucketAcl ()::decode )
986+ .collect (ImmutableList .toImmutableList ());
987+ } catch (NotFoundException e ) {
988+ throw StorageException .coalesce (e );
989+ }
902990 }
903991
904992 @ Override
905993 public List <Acl > listAcls (String bucket ) {
906- return throwNotYetImplemented ( fmtMethodName ( " listAcls" , String . class ) );
994+ return listAcls ( bucket , EMPTY_BUCKET_SOURCE_OPTIONS );
907995 }
908996
909997 @ Override
910998 public Acl getDefaultAcl (String bucket , Entity entity ) {
911999 try {
912- com .google .storage .v2 .Bucket resp = getBucketDefaultAcls (bucket );
1000+ com .google .storage .v2 .Bucket resp = getBucketWithDefaultAcls (bucket );
9131001
9141002 Predicate <ObjectAccessControl > entityPredicate =
9151003 objectAclEntityOrAltEq (codecs .entity ().encode (entity ));
@@ -933,7 +1021,7 @@ public Acl getDefaultAcl(String bucket, Entity entity) {
9331021 @ Override
9341022 public boolean deleteDefaultAcl (String bucket , Entity entity ) {
9351023 try {
936- com .google .storage .v2 .Bucket resp = getBucketDefaultAcls (bucket );
1024+ com .google .storage .v2 .Bucket resp = getBucketWithDefaultAcls (bucket );
9371025 String encode = codecs .entity ().encode (entity );
9381026
9391027 Predicate <ObjectAccessControl > entityPredicate = objectAclEntityOrAltEq (encode );
@@ -949,7 +1037,8 @@ public boolean deleteDefaultAcl(String bucket, Entity entity) {
9491037 }
9501038 long metageneration = resp .getMetageneration ();
9511039
952- UpdateBucketRequest req = createUpdateRequest (bucket , newDefaultAcls , metageneration );
1040+ UpdateBucketRequest req =
1041+ createUpdateDefaultAclRequest (bucket , newDefaultAcls , metageneration );
9531042
9541043 com .google .storage .v2 .Bucket updateResult = updateBucket (req );
9551044 // read the response to ensure there is no longer an acl for the specified entity
@@ -976,7 +1065,7 @@ public Acl createDefaultAcl(String bucket, Acl acl) {
9761065 @ Override
9771066 public Acl updateDefaultAcl (String bucket , Acl acl ) {
9781067 try {
979- com .google .storage .v2 .Bucket resp = getBucketDefaultAcls (bucket );
1068+ com .google .storage .v2 .Bucket resp = getBucketWithDefaultAcls (bucket );
9801069 ObjectAccessControl encode = codecs .objectAcl ().encode (acl );
9811070 String entity = encode .getEntity ();
9821071
@@ -989,7 +1078,7 @@ public Acl updateDefaultAcl(String bucket, Acl acl) {
9891078 .collect (ImmutableList .toImmutableList ());
9901079
9911080 UpdateBucketRequest req =
992- createUpdateRequest (bucket , newDefaultAcls , resp .getMetageneration ());
1081+ createUpdateDefaultAclRequest (bucket , newDefaultAcls , resp .getMetageneration ());
9931082
9941083 com .google .storage .v2 .Bucket updateResult = updateBucket (req );
9951084
@@ -1009,7 +1098,7 @@ public Acl updateDefaultAcl(String bucket, Acl acl) {
10091098 @ Override
10101099 public List <Acl > listDefaultAcls (String bucket ) {
10111100 try {
1012- com .google .storage .v2 .Bucket resp = getBucketDefaultAcls (bucket );
1101+ com .google .storage .v2 .Bucket resp = getBucketWithDefaultAcls (bucket );
10131102 return resp .getDefaultObjectAclList ().stream ()
10141103 .map (codecs .objectAcl ()::decode )
10151104 .collect (ImmutableList .toImmutableList ());
@@ -1481,7 +1570,7 @@ private SourceObject sourceObjectEncode(SourceBlob from) {
14811570 return to .build ();
14821571 }
14831572
1484- private com .google .storage .v2 .Bucket getBucketDefaultAcls (String bucketName ) {
1573+ private com .google .storage .v2 .Bucket getBucketWithDefaultAcls (String bucketName ) {
14851574 Fields fields =
14861575 UnifiedOpts .fields (
14871576 ImmutableSet .of (
@@ -1503,6 +1592,25 @@ private com.google.storage.v2.Bucket getBucketDefaultAcls(String bucketName) {
15031592 Decoder .identity ());
15041593 }
15051594
1595+ private com .google .storage .v2 .Bucket getBucketWithAcls (
1596+ String bucketName , Opts <BucketSourceOpt > opts ) {
1597+ Fields fields =
1598+ UnifiedOpts .fields (ImmutableSet .of (BucketField .ACL , BucketField .METAGENERATION ));
1599+ GrpcCallContext grpcCallContext = GrpcCallContext .createDefault ();
1600+ Mapper <GetBucketRequest .Builder > mapper = opts .getBucketsRequest ().andThen (fields .getBucket ());
1601+ GetBucketRequest req =
1602+ mapper
1603+ .apply (GetBucketRequest .newBuilder ())
1604+ .setName (bucketNameCodec .encode (bucketName ))
1605+ .build ();
1606+
1607+ return Retrying .run (
1608+ getOptions (),
1609+ retryAlgorithmManager .getFor (req ),
1610+ () -> storageClient .getBucketCallable ().call (req , grpcCallContext ),
1611+ Decoder .identity ());
1612+ }
1613+
15061614 private com .google .storage .v2 .Bucket updateBucket (UpdateBucketRequest req ) {
15071615 GrpcCallContext grpcCallContext = GrpcCallContext .createDefault ();
15081616 return Retrying .run (
@@ -1512,7 +1620,7 @@ private com.google.storage.v2.Bucket updateBucket(UpdateBucketRequest req) {
15121620 Decoder .identity ());
15131621 }
15141622
1515- private static UpdateBucketRequest createUpdateRequest (
1623+ private static UpdateBucketRequest createUpdateDefaultAclRequest (
15161624 String bucket , ImmutableList <ObjectAccessControl > newDefaultAcls , long metageneration ) {
15171625 com .google .storage .v2 .Bucket update =
15181626 com .google .storage .v2 .Bucket .newBuilder ()
@@ -1528,4 +1636,21 @@ private static UpdateBucketRequest createUpdateRequest(
15281636 .setBucket (update )
15291637 .build ();
15301638 }
1639+
1640+ private static UpdateBucketRequest createUpdateAclRequest (
1641+ String bucket , ImmutableList <BucketAccessControl > newDefaultAcls , long metageneration ) {
1642+ com .google .storage .v2 .Bucket update =
1643+ com .google .storage .v2 .Bucket .newBuilder ()
1644+ .setName (bucketNameCodec .encode (bucket ))
1645+ .addAllAcl (newDefaultAcls )
1646+ .build ();
1647+ Opts <BucketTargetOpt > opts =
1648+ Opts .from (
1649+ UnifiedOpts .fields (ImmutableSet .of (BucketField .ACL )),
1650+ UnifiedOpts .metagenerationMatch (metageneration ));
1651+ return opts .updateBucketsRequest ()
1652+ .apply (UpdateBucketRequest .newBuilder ())
1653+ .setBucket (update )
1654+ .build ();
1655+ }
15311656}
0 commit comments