Skip to content

Commit

Permalink
feat: implement GrpcStorageImpl#createDefaultAcl
Browse files Browse the repository at this point in the history
  • Loading branch information
BenWhitehead committed Dec 8, 2022
1 parent a1b6e66 commit db4e8ff
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,7 @@ public boolean deleteDefaultAcl(Entity entity) {
*
* @throws StorageException upon failure
*/
@TransportCompatibility({Transport.HTTP})
@TransportCompatibility({Transport.HTTP, Transport.GRPC})
public Acl createDefaultAcl(Acl acl) {
return storage.createDefaultAcl(getName(), acl);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.common.io.BaseEncoding;
import com.google.common.io.ByteStreams;
import com.google.iam.v1.GetIamPolicyRequest;
Expand Down Expand Up @@ -936,7 +937,54 @@ public boolean deleteDefaultAcl(String bucket, Entity entity) {

@Override
public Acl createDefaultAcl(String bucket, Acl acl) {
return throwNotYetImplemented(fmtMethodName("createDefaultAcl", String.class, Acl.class));
try {
com.google.storage.v2.Bucket resp = getBucketDefaultAcls(bucket);
ObjectAccessControl encode = codecs.objectAcl().encode(acl);
String entity = encode.getEntity();

Predicate<ObjectAccessControl> entityPredicate = objectAclEntityOrAltEq(entity);

ImmutableList<ObjectAccessControl> collect =
Streams.concat(
resp.getDefaultObjectAclList().stream().filter(entityPredicate.negate()),
Stream.of(encode))
.collect(ImmutableList.toImmutableList());

com.google.storage.v2.Bucket update =
com.google.storage.v2.Bucket.newBuilder()
.setName(bucketNameCodec.encode(bucket))
.addAllDefaultObjectAcl(collect)
.build();
Opts<BucketTargetOpt> opts =
Opts.from(
UnifiedOpts.fields(ImmutableSet.of(BucketField.DEFAULT_OBJECT_ACL)),
UnifiedOpts.metagenerationMatch(resp.getMetageneration()));
UpdateBucketRequest req =
opts.updateBucketsRequest()
.apply(UpdateBucketRequest.newBuilder())
.setBucket(update)
.build();

GrpcCallContext grpcCallContext = GrpcCallContext.createDefault();
com.google.storage.v2.Bucket updateResult =
Retrying.run(
getOptions(),
retryAlgorithmManager.getFor(req),
() -> storageClient.updateBucketCallable().call(req, grpcCallContext),
Decoder.identity());

//noinspection DataFlowIssue
Optional<Acl> first =
updateResult.getDefaultObjectAclList().stream()
.filter(entityPredicate)
.findFirst()
.map(codecs.objectAcl()::decode);

return first.orElseThrow(
() -> new StorageException(404, "Acl update call success, but not in response"));
} catch (NotFoundException e) {
throw StorageException.coalesce(e);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3530,7 +3530,7 @@ PostPolicyV4 generateSignedPostPolicyV4(
*
* @throws StorageException upon failure
*/
@TransportCompatibility({Transport.HTTP})
@TransportCompatibility({Transport.HTTP, Transport.GRPC})
Acl createDefaultAcl(String bucket, Acl acl);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ public Mapper<ListObjectsRequest.Builder> listObjects() {
}

static final class Fields extends RpcOptVal<ImmutableSet<NamedField>>
implements ObjectSourceOpt, ObjectListOpt, BucketSourceOpt, BucketListOpt {
implements ObjectSourceOpt, ObjectListOpt, BucketSourceOpt, BucketTargetOpt, BucketListOpt {

/**
* Apiary and gRPC have differing handling of where the field selector is evaluated relative to
Expand Down Expand Up @@ -752,6 +752,11 @@ public Mapper<ListBucketsRequest.Builder> listBuckets() {
return b -> b.setReadMask(FieldMask.newBuilder().addAllPaths(getPaths()).build());
}

@Override
public Mapper<UpdateBucketRequest.Builder> updateBucket() {
return b -> b.setUpdateMask(FieldMask.newBuilder().addAllPaths(getPaths()).build());
}

@Override
public Mapper<GetObjectRequest.Builder> getObject() {
return b -> b.setReadMask(FieldMask.newBuilder().addAllPaths(getPaths()).build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,49 @@ public void bucket_defaultAcl_list_bucket404() {
assertThat(storageException.getCode()).isEqualTo(404);
}

@Test
public void bucket_defaultAcl_create() throws Exception {
BucketInfo bucketInfo = BucketInfo.newBuilder(generator.randomBucketName()).build();
try (TemporaryBucket tempB =
TemporaryBucket.newBuilder().setBucketInfo(bucketInfo).setStorage(storage).build()) {
BucketInfo bucket = tempB.getBucket();

Acl readAll = Acl.of(User.ofAllAuthenticatedUsers(), Role.READER);
Acl actual = retry429s(() -> storage.createDefaultAcl(bucket.getName(), readAll), storage);

assertThat(actual.getEntity()).isEqualTo(readAll.getEntity());
assertThat(actual.getRole()).isEqualTo(readAll.getRole());
assertThat(actual.getEtag()).isNotEmpty();

Bucket bucketUpdated =
storage.get(bucket.getName(), BucketGetOption.fields(BucketField.values()));
assertThat(bucketUpdated.getMetageneration()).isNotEqualTo(bucket.getMetageneration());

// etags change when updates happen, drop before our comparison
List<Acl> expectedAcls = dropEtags(bucket.getDefaultAcl());
List<Acl> actualAcls = dropEtags(bucketUpdated.getDefaultAcl());
assertThat(actualAcls).containsAtLeastElementsIn(expectedAcls);
}
}

@Test
public void bucket_defaultAcl_create_bucket404() {
Acl readAll = Acl.of(User.ofAllAuthenticatedUsers(), Role.READER);
StorageException storageException =
assertThrows(
StorageException.class,
() ->
retry429s(
() -> storage.createDefaultAcl(bucket.getName() + "x", readAll), storage));

assertThat(storageException.getCode()).isEqualTo(404);
}

@Test
@CrossRun.Ignore(transports = Transport.GRPC)
public void testBucketDefaultAcl() {
// TODO: break this test up into each of the respective scenarios
// 2. Delete a default ACL for a specific entity
// 3. Create a default ACL for specific entity
// 4. Update default ACL to change role of a specific entity

// according to https://cloud.google.com/storage/docs/access-control/lists#default
Expand Down Expand Up @@ -1026,4 +1063,10 @@ public boolean shouldRetry(Throwable previousThrowable, Object previousResponse)
}
}
}

private static ImmutableList<Acl> dropEtags(List<Acl> defaultAcls) {
return defaultAcls.stream()
.map(acl -> Acl.of(acl.getEntity(), acl.getRole()))
.collect(ImmutableList.toImmutableList());
}
}

0 comments on commit db4e8ff

Please sign in to comment.