Skip to content

Commit b9b7c49

Browse files
authored
feat: implement GrpcStorageImpl#getDefaultAcl (#1802)
1 parent 3e3295e commit b9b7c49

File tree

8 files changed

+110
-6
lines changed

8 files changed

+110
-6
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
@@ -1241,7 +1241,7 @@ public List<Acl> listAcls() {
12411241
*
12421242
* @throws StorageException upon failure
12431243
*/
1244-
@TransportCompatibility({Transport.HTTP})
1244+
@TransportCompatibility({Transport.HTTP, Transport.GRPC})
12451245
public Acl getDefaultAcl(Entity entity) {
12461246
return storage.getDefaultAcl(getName(), entity);
12471247
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,10 @@ private Acl.Entity entityDecode(String from) {
473473
private Acl objectAclDecode(ObjectAccessControl from) {
474474
Acl.Role role = Acl.Role.valueOf(from.getRole());
475475
Acl.Entity entity = entityCodec.decode(from.getEntity());
476-
Acl.Builder to = Acl.newBuilder(entity, role).setId(from.getId());
476+
Acl.Builder to = Acl.newBuilder(entity, role);
477+
if (!from.getId().isEmpty()) {
478+
to.setId(from.getId());
479+
}
477480
if (!from.getEtag().isEmpty()) {
478481
to.setEtag(from.getEtag());
479482
}

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

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static com.google.cloud.storage.ByteSizeConstants._16MiB;
2020
import static com.google.cloud.storage.ByteSizeConstants._256KiB;
2121
import static com.google.cloud.storage.GrpcToHttpStatusCodeTranslation.resultRetryAlgorithmToCodes;
22+
import static com.google.cloud.storage.StorageV2ProtoUtils.objectAclEntityOrAltEq;
2223
import static com.google.cloud.storage.Utils.bucketNameCodec;
2324
import static com.google.cloud.storage.Utils.ifNonNull;
2425
import static com.google.cloud.storage.Utils.projectNameCodec;
@@ -54,6 +55,7 @@
5455
import com.google.cloud.storage.UnifiedOpts.BucketListOpt;
5556
import com.google.cloud.storage.UnifiedOpts.BucketSourceOpt;
5657
import com.google.cloud.storage.UnifiedOpts.BucketTargetOpt;
58+
import com.google.cloud.storage.UnifiedOpts.Fields;
5759
import com.google.cloud.storage.UnifiedOpts.HmacKeyListOpt;
5860
import com.google.cloud.storage.UnifiedOpts.HmacKeySourceOpt;
5961
import com.google.cloud.storage.UnifiedOpts.HmacKeyTargetOpt;
@@ -89,6 +91,7 @@
8991
import com.google.storage.v2.ListObjectsRequest;
9092
import com.google.storage.v2.LockBucketRetentionPolicyRequest;
9193
import com.google.storage.v2.Object;
94+
import com.google.storage.v2.ObjectAccessControl;
9295
import com.google.storage.v2.ProjectName;
9396
import com.google.storage.v2.ReadObjectRequest;
9497
import com.google.storage.v2.RewriteObjectRequest;
@@ -125,6 +128,7 @@
125128
import java.util.Arrays;
126129
import java.util.List;
127130
import java.util.Objects;
131+
import java.util.Optional;
128132
import java.util.Set;
129133
import java.util.Spliterator;
130134
import java.util.Spliterators.AbstractSpliterator;
@@ -903,7 +907,45 @@ public List<Acl> listAcls(String bucket) {
903907

904908
@Override
905909
public Acl getDefaultAcl(String bucket, Entity entity) {
906-
return throwNotYetImplemented(fmtMethodName("getDefaultAcl", String.class, Entity.class));
910+
// Specify the read-mask to explicitly include defaultObjectAcl
911+
Fields fields =
912+
UnifiedOpts.fields(
913+
ImmutableSet.of(
914+
BucketField.ACL, // workaround for b/261771961
915+
BucketField.DEFAULT_OBJECT_ACL));
916+
GrpcCallContext grpcCallContext = GrpcCallContext.createDefault();
917+
GetBucketRequest req =
918+
fields
919+
.getBucket()
920+
.apply(GetBucketRequest.newBuilder())
921+
.setName(bucketNameCodec.encode(bucket))
922+
.build();
923+
try {
924+
com.google.storage.v2.Bucket resp =
925+
Retrying.run(
926+
getOptions(),
927+
retryAlgorithmManager.getFor(req),
928+
() -> storageClient.getBucketCallable().call(req, grpcCallContext),
929+
Decoder.identity());
930+
931+
Predicate<ObjectAccessControl> entityPredicate =
932+
objectAclEntityOrAltEq(codecs.entity().encode(entity));
933+
934+
//noinspection DataFlowIssue
935+
Optional<ObjectAccessControl> first =
936+
resp.getDefaultObjectAclList().stream().filter(entityPredicate).findFirst();
937+
938+
// HttpStorageRpc defaults to null if Not Found
939+
return first.map(codecs.objectAcl()::decode).orElse(null);
940+
} catch (NotFoundException e) {
941+
return null;
942+
} catch (StorageException se) {
943+
if (se.getCode() == 404) {
944+
return null;
945+
} else {
946+
throw se;
947+
}
948+
}
907949
}
908950

909951
@Override

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
@@ -3487,7 +3487,7 @@ PostPolicyV4 generateSignedPostPolicyV4(
34873487
*
34883488
* @throws StorageException upon failure
34893489
*/
3490-
@TransportCompatibility({Transport.HTTP})
3490+
@TransportCompatibility({Transport.HTTP, Transport.GRPC})
34913491
Acl getDefaultAcl(String bucket, Entity entity);
34923492

34933493
/**

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import com.google.protobuf.MessageOrBuilder;
2323
import com.google.protobuf.util.JsonFormat;
2424
import com.google.protobuf.util.JsonFormat.Printer;
25+
import com.google.storage.v2.ObjectAccessControl;
2526
import com.google.storage.v2.ReadObjectRequest;
27+
import java.util.function.Predicate;
2628
import org.checkerframework.checker.nullness.qual.NonNull;
2729
import org.checkerframework.checker.nullness.qual.Nullable;
2830

@@ -82,4 +84,12 @@ static String fmtProto(@NonNull final MessageOrBuilder msg) {
8284
throw new RuntimeException(e);
8385
}
8486
}
87+
88+
/**
89+
* When evaluating an {@link ObjectAccessControl} entity, look at both {@code entity} (generally
90+
* project number format) and {@code entity_alt} (generally project id format).
91+
*/
92+
static Predicate<ObjectAccessControl> objectAclEntityOrAltEq(String s) {
93+
return oAcl -> oAcl.getEntity().equals(s) || oAcl.getEntityAlt().equals(s);
94+
}
8595
}

google-cloud-storage/src/test/java/com/google/cloud/storage/StorageV2ProtoUtilsTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import static org.junit.Assert.assertThrows;
2323

2424
import com.google.cloud.storage.jqwik.StorageArbitraries;
25+
import com.google.storage.v2.ObjectAccessControl;
2526
import com.google.storage.v2.ReadObjectRequest;
27+
import java.util.function.Predicate;
2628
import net.jqwik.api.Arbitraries;
2729
import net.jqwik.api.Arbitrary;
2830
import net.jqwik.api.Combinators;
@@ -66,6 +68,20 @@ void validation_limit_gteq_0() {
6668
() -> seekReadObjectRequest(ReadObjectRequest.getDefaultInstance(), null, -1L));
6769
}
6870

71+
@Example
72+
void objectAclEntityIdOrAltEq() {
73+
String entity = "project-viewer-123123";
74+
Predicate<ObjectAccessControl> p = StorageV2ProtoUtils.objectAclEntityOrAltEq(entity);
75+
76+
ObjectAccessControl inAlt =
77+
ObjectAccessControl.newBuilder().setEntity("something").setEntityAlt(entity).build();
78+
ObjectAccessControl inPrimary =
79+
ObjectAccessControl.newBuilder().setEntity(entity).setEntityAlt("something-else").build();
80+
81+
assertThat(p.test(inAlt)).isTrue();
82+
assertThat(p.test(inPrimary)).isTrue();
83+
}
84+
6985
@Property(tries = 100_000)
7086
void seek(@ForAll("seekCases") SeekCase srr) {
7187
Long offset = srr.offset;

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

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.google.cloud.storage.Storage;
4444
import com.google.cloud.storage.Storage.BlobTargetOption;
4545
import com.google.cloud.storage.Storage.BucketField;
46+
import com.google.cloud.storage.Storage.BucketGetOption;
4647
import com.google.cloud.storage.Storage.BucketSourceOption;
4748
import com.google.cloud.storage.Storage.BucketTargetOption;
4849
import com.google.cloud.storage.StorageException;
@@ -125,11 +126,43 @@ private void testBucketAclRequesterPays(
125126
assertNull(storage.getAcl(bucket.getName(), User.ofAllAuthenticatedUsers(), bucketOptions));
126127
}
127128

129+
@Test
130+
public void bucket_defaultAcl_get() {
131+
String bucketName = bucket.getName();
132+
// lookup an entity from the bucket which is known to exist
133+
Bucket bucketWithAcls =
134+
storage.get(
135+
bucketName, BucketGetOption.fields(BucketField.ACL, BucketField.DEFAULT_OBJECT_ACL));
136+
137+
Acl actual = bucketWithAcls.getDefaultAcl().iterator().next();
138+
139+
Acl acl = retry429s(() -> storage.getDefaultAcl(bucketName, actual.getEntity()), storage);
140+
141+
assertThat(acl).isEqualTo(actual);
142+
}
143+
144+
/** When a bucket does exist, but an acl for the specified entity is not defined return null */
145+
@Test
146+
public void bucket_defaultAcl_get_notFoundReturnsNull() {
147+
Acl acl = retry429s(() -> storage.getDefaultAcl(bucket.getName(), User.ofAllUsers()), storage);
148+
149+
assertThat(acl).isNull();
150+
}
151+
152+
/** When a bucket doesn't exist, return null for the acl value */
153+
@Test
154+
public void bucket_defaultAcl_get_bucket404() {
155+
Acl acl =
156+
retry429s(() -> storage.getDefaultAcl(bucket.getName() + "x", User.ofAllUsers()), storage);
157+
158+
assertThat(acl).isNull();
159+
}
160+
128161
@Test
129162
@CrossRun.Ignore(transports = Transport.GRPC)
130163
public void testBucketDefaultAcl() {
131164
// TODO: break this test up into each of the respective scenarios
132-
// 1. get default ACL for specific entity
165+
// DONE ~1. get default ACL for specific entity~
133166
// 2. Delete a default ACL for a specific entity
134167
// 3. Create a default ACL for specific entity
135168
// 4. Update default ACL to change role of a specific entity

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public ImmutableList<?> parameters() {
113113
BucketField.DEFAULT_OBJECT_ACL,
114114
(jsonT, grpcT) -> {
115115
assertThat(jsonT.getDefaultAcl()).isNotEmpty();
116-
assertThat(grpcT.getDefaultAcl()).isNull();
116+
assertThat(grpcT.getDefaultAcl()).isNull(); // workaround for b/261771961
117117
}),
118118
new Args<>(BucketField.ENCRYPTION, LazyAssertion.equal()),
119119
new Args<>(

0 commit comments

Comments
 (0)