Skip to content

Commit c783277

Browse files
authored
feat: implement GrpcStorageImpl#deleteDefaultAcl (#1807)
1 parent 0af6693 commit c783277

File tree

4 files changed

+124
-58
lines changed

4 files changed

+124
-58
lines changed

google-cloud-storage/src/main/java/com/google/cloud/storage/Bucket.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1266,7 +1266,7 @@ public Acl getDefaultAcl(Entity entity) {
12661266
* @return {@code true} if the ACL was deleted, {@code false} if it was not found
12671267
* @throws StorageException upon failure
12681268
*/
1269-
@TransportCompatibility({Transport.HTTP})
1269+
@TransportCompatibility({Transport.HTTP, Transport.GRPC})
12701270
public boolean deleteDefaultAcl(Entity entity) {
12711271
return storage.deleteDefaultAcl(getName(), entity);
12721272
}

google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,40 @@ public Acl getDefaultAcl(String bucket, Entity entity) {
932932

933933
@Override
934934
public boolean deleteDefaultAcl(String bucket, Entity entity) {
935-
return throwNotYetImplemented(fmtMethodName("deleteDefaultAcl", String.class, Entity.class));
935+
try {
936+
com.google.storage.v2.Bucket resp = getBucketDefaultAcls(bucket);
937+
String encode = codecs.entity().encode(entity);
938+
939+
Predicate<ObjectAccessControl> entityPredicate = objectAclEntityOrAltEq(encode);
940+
941+
List<ObjectAccessControl> currentDefaultAcls = resp.getDefaultObjectAclList();
942+
ImmutableList<ObjectAccessControl> newDefaultAcls =
943+
currentDefaultAcls.stream()
944+
.filter(entityPredicate.negate())
945+
.collect(ImmutableList.toImmutableList());
946+
if (newDefaultAcls.equals(currentDefaultAcls)) {
947+
// we didn't actually filter anything out, no need to send an RPC, simply return false
948+
return false;
949+
}
950+
long metageneration = resp.getMetageneration();
951+
952+
UpdateBucketRequest req = createUpdateRequest(bucket, newDefaultAcls, metageneration);
953+
954+
com.google.storage.v2.Bucket updateResult = updateBucket(req);
955+
// read the response to ensure there is no longer an acl for the specified entity
956+
Optional<ObjectAccessControl> first =
957+
updateResult.getDefaultObjectAclList().stream().filter(entityPredicate).findFirst();
958+
return !first.isPresent();
959+
} catch (NotFoundException e) {
960+
// HttpStorageRpc returns false if the bucket doesn't exist :(
961+
return false;
962+
} catch (StorageException se) {
963+
if (se.getCode() == 404) {
964+
return false;
965+
} else {
966+
throw se;
967+
}
968+
}
936969
}
937970

938971
@Override
@@ -949,36 +982,17 @@ public Acl updateDefaultAcl(String bucket, Acl acl) {
949982

950983
Predicate<ObjectAccessControl> entityPredicate = objectAclEntityOrAltEq(entity);
951984

952-
ImmutableList<ObjectAccessControl> collect =
985+
ImmutableList<ObjectAccessControl> newDefaultAcls =
953986
Streams.concat(
954987
resp.getDefaultObjectAclList().stream().filter(entityPredicate.negate()),
955988
Stream.of(encode))
956989
.collect(ImmutableList.toImmutableList());
957990

958-
com.google.storage.v2.Bucket update =
959-
com.google.storage.v2.Bucket.newBuilder()
960-
.setName(bucketNameCodec.encode(bucket))
961-
.addAllDefaultObjectAcl(collect)
962-
.build();
963-
Opts<BucketTargetOpt> opts =
964-
Opts.from(
965-
UnifiedOpts.fields(ImmutableSet.of(BucketField.DEFAULT_OBJECT_ACL)),
966-
UnifiedOpts.metagenerationMatch(resp.getMetageneration()));
967991
UpdateBucketRequest req =
968-
opts.updateBucketsRequest()
969-
.apply(UpdateBucketRequest.newBuilder())
970-
.setBucket(update)
971-
.build();
992+
createUpdateRequest(bucket, newDefaultAcls, resp.getMetageneration());
972993

973-
GrpcCallContext grpcCallContext = GrpcCallContext.createDefault();
974-
com.google.storage.v2.Bucket updateResult =
975-
Retrying.run(
976-
getOptions(),
977-
retryAlgorithmManager.getFor(req),
978-
() -> storageClient.updateBucketCallable().call(req, grpcCallContext),
979-
Decoder.identity());
994+
com.google.storage.v2.Bucket updateResult = updateBucket(req);
980995

981-
//noinspection DataFlowIssue
982996
Optional<Acl> first =
983997
updateResult.getDefaultObjectAclList().stream()
984998
.filter(entityPredicate)
@@ -1488,4 +1502,30 @@ private com.google.storage.v2.Bucket getBucketDefaultAcls(String bucketName) {
14881502
() -> storageClient.getBucketCallable().call(req, grpcCallContext),
14891503
Decoder.identity());
14901504
}
1505+
1506+
private com.google.storage.v2.Bucket updateBucket(UpdateBucketRequest req) {
1507+
GrpcCallContext grpcCallContext = GrpcCallContext.createDefault();
1508+
return Retrying.run(
1509+
getOptions(),
1510+
retryAlgorithmManager.getFor(req),
1511+
() -> storageClient.updateBucketCallable().call(req, grpcCallContext),
1512+
Decoder.identity());
1513+
}
1514+
1515+
private static UpdateBucketRequest createUpdateRequest(
1516+
String bucket, ImmutableList<ObjectAccessControl> newDefaultAcls, long metageneration) {
1517+
com.google.storage.v2.Bucket update =
1518+
com.google.storage.v2.Bucket.newBuilder()
1519+
.setName(bucketNameCodec.encode(bucket))
1520+
.addAllDefaultObjectAcl(newDefaultAcls)
1521+
.build();
1522+
Opts<BucketTargetOpt> opts =
1523+
Opts.from(
1524+
UnifiedOpts.fields(ImmutableSet.of(BucketField.DEFAULT_OBJECT_ACL)),
1525+
UnifiedOpts.metagenerationMatch(metageneration));
1526+
return opts.updateBucketsRequest()
1527+
.apply(UpdateBucketRequest.newBuilder())
1528+
.setBucket(update)
1529+
.build();
1530+
}
14911531
}

google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3511,7 +3511,7 @@ PostPolicyV4 generateSignedPostPolicyV4(
35113511
* @return {@code true} if the ACL was deleted, {@code false} if it was not found
35123512
* @throws StorageException upon failure
35133513
*/
3514-
@TransportCompatibility({Transport.HTTP})
3514+
@TransportCompatibility({Transport.HTTP, Transport.GRPC})
35153515
boolean deleteDefaultAcl(String bucket, Entity entity);
35163516

35173517
/**

google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITAccessTest.java

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import java.util.Iterator;
7171
import java.util.List;
7272
import java.util.Map;
73+
import java.util.Optional;
7374
import java.util.Set;
7475
import java.util.concurrent.Callable;
7576
import java.util.function.Predicate;
@@ -286,40 +287,65 @@ public void bucket_defaultAcl_update_bucket404() {
286287
}
287288

288289
@Test
289-
@CrossRun.Ignore(transports = Transport.GRPC)
290-
public void testBucketDefaultAcl() {
291-
// TODO: break this test up into each of the respective scenarios
292-
// 2. Delete a default ACL for a specific entity
293-
// 4. Update default ACL to change role of a specific entity
294-
295-
// according to https://cloud.google.com/storage/docs/access-control/lists#default
296-
// it can take up to 30 seconds for default acl updates to propagate
297-
// Since this test is performing so many mutations to default acls there are several calls
298-
// that are otherwise non-idempotent wrapped with retries.
299-
assertNull(
300-
retry429s(
301-
() -> storage.getDefaultAcl(bucket.getName(), User.ofAllAuthenticatedUsers()),
302-
storage));
303-
assertFalse(
304-
retry429s(
305-
() -> storage.deleteDefaultAcl(bucket.getName(), User.ofAllAuthenticatedUsers()),
306-
storage));
307-
Acl acl = Acl.of(User.ofAllAuthenticatedUsers(), Role.READER);
308-
assertNotNull(retry429s(() -> storage.createDefaultAcl(bucket.getName(), acl), storage));
309-
Acl updatedAcl =
310-
retry429s(
311-
() ->
312-
storage.updateDefaultAcl(
313-
bucket.getName(), acl.toBuilder().setRole(Role.OWNER).build()),
314-
storage);
315-
assertEquals(Role.OWNER, updatedAcl.getRole());
316-
Set<Acl> acls = new HashSet<>(storage.listDefaultAcls(bucket.getName()));
317-
assertTrue(acls.contains(updatedAcl));
318-
assertTrue(
290+
public void bucket_defaultAcl_delete() throws Exception {
291+
BucketInfo bucketInfo = BucketInfo.newBuilder(generator.randomBucketName()).build();
292+
try (TemporaryBucket tempB =
293+
TemporaryBucket.newBuilder().setBucketInfo(bucketInfo).setStorage(storage).build()) {
294+
BucketInfo bucket = tempB.getBucket();
295+
296+
List<Acl> defaultAcls = bucket.getDefaultAcl();
297+
assertThat(defaultAcls).isNotEmpty();
298+
299+
Predicate<Acl> isProjectEditor = hasProjectRole(ProjectRole.VIEWERS);
300+
301+
//noinspection OptionalGetWithoutIsPresent
302+
Acl projectViewerAsReader =
303+
defaultAcls.stream().filter(hasRole(Role.READER).and(isProjectEditor)).findFirst().get();
304+
305+
Entity entity = projectViewerAsReader.getEntity();
306+
307+
boolean actual = retry429s(() -> storage.deleteDefaultAcl(bucket.getName(), entity), storage);
308+
309+
assertThat(actual).isTrue();
310+
311+
Bucket bucketUpdated =
312+
storage.get(bucket.getName(), BucketGetOption.fields(BucketField.values()));
313+
assertThat(bucketUpdated.getMetageneration()).isNotEqualTo(bucket.getMetageneration());
314+
315+
// etags change when deletes happen, drop before our comparison
316+
List<Acl> expectedAcls =
317+
dropEtags(
318+
bucket.getDefaultAcl().stream()
319+
.filter(isProjectEditor.negate())
320+
.collect(Collectors.toList()));
321+
List<Acl> actualAcls = dropEtags(bucketUpdated.getDefaultAcl());
322+
assertThat(actualAcls).containsAtLeastElementsIn(expectedAcls);
323+
Optional<Entity> search =
324+
actualAcls.stream().map(Acl::getEntity).filter(e -> e.equals(entity)).findAny();
325+
assertThat(search.isPresent()).isFalse();
326+
}
327+
}
328+
329+
@Test
330+
public void bucket_defaultAcl_delete_bucket404() {
331+
boolean actual =
319332
retry429s(
320-
() -> storage.deleteDefaultAcl(bucket.getName(), User.ofAllAuthenticatedUsers()),
321-
storage));
322-
assertNull(storage.getDefaultAcl(bucket.getName(), User.ofAllAuthenticatedUsers()));
333+
() -> storage.deleteDefaultAcl(bucket.getName() + "x", User.ofAllUsers()), storage);
334+
335+
assertThat(actual).isEqualTo(false);
336+
}
337+
338+
@Test
339+
public void bucket_defaultAcl_delete_noExistingAcl() throws Exception {
340+
BucketInfo bucketInfo = BucketInfo.newBuilder(generator.randomBucketName()).build();
341+
try (TemporaryBucket tempB =
342+
TemporaryBucket.newBuilder().setBucketInfo(bucketInfo).setStorage(storage).build()) {
343+
BucketInfo bucket = tempB.getBucket();
344+
boolean actual =
345+
retry429s(() -> storage.deleteDefaultAcl(bucket.getName(), User.ofAllUsers()), storage);
346+
347+
assertThat(actual).isEqualTo(false);
348+
}
323349
}
324350

325351
@Test

0 commit comments

Comments
 (0)