From f8cad99f0de0690eaf040ea58e6e60aa50682ae9 Mon Sep 17 00:00:00 2001 From: BenWhitehead Date: Fri, 16 Dec 2022 14:53:36 -0500 Subject: [PATCH] chore(test): expand StorageInstance vetoing to apply to bucket acl operations (#1819) * chore(test): simplify StorageInstance vetoing implementation Rather than using a runtime proxy to do invocation method argument matching we are now defining a concrete class to override the methods it needs to provide enforcement for. This is simpler in that it uses standard java language implementation approach which is helped by compiler and IDEs. It is at the expense of needing to define the new ~500 line AbstractStorageDecorator class. * test: veto attempts to mutate bucket default acls * test: veto attempts to mutate bucket acls * test: veto lockRetentionPolicy --- .../google/cloud/storage/it/ITAccessTest.java | 96 ---- .../runner/registry/AbstractStorageProxy.java | 490 ++++++++++++++++++ .../it/runner/registry/StorageInstance.java | 158 ++++-- 3 files changed, 606 insertions(+), 138 deletions(-) create mode 100644 google-cloud-storage/src/test/java/com/google/cloud/storage/it/runner/registry/AbstractStorageProxy.java diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITAccessTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITAccessTest.java index e1619887d0..99b29818d3 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITAccessTest.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITAccessTest.java @@ -35,7 +35,6 @@ import com.google.cloud.storage.Acl.Role; import com.google.cloud.storage.Acl.User; import com.google.cloud.storage.Blob; -import com.google.cloud.storage.BlobId; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.Bucket; import com.google.cloud.storage.BucketInfo; @@ -45,7 +44,6 @@ import com.google.cloud.storage.Storage.BlobTargetOption; import com.google.cloud.storage.Storage.BucketField; import com.google.cloud.storage.Storage.BucketGetOption; -import com.google.cloud.storage.Storage.BucketSourceOption; import com.google.cloud.storage.Storage.BucketTargetOption; import com.google.cloud.storage.StorageException; import com.google.cloud.storage.StorageRoles; @@ -63,7 +61,6 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -97,37 +94,6 @@ public class ITAccessTest { @Inject public Generator generator; - @Test - @CrossRun.Ignore(transports = Transport.GRPC) - public void bucketAcl_requesterPays_true() { - String projectId = storage.getOptions().getProjectId(); - testBucketAclRequesterPays(requesterPaysBucket, BucketSourceOption.userProject(projectId)); - } - - @Test - @CrossRun.Ignore(transports = Transport.GRPC) - public void bucketAcl_requesterPays_false() { - testBucketAclRequesterPays(bucket); - } - - private void testBucketAclRequesterPays( - BucketInfo bucket, Storage.BucketSourceOption... bucketOptions) { - // TODO: break into individual tests - assertNull(storage.getAcl(bucket.getName(), User.ofAllAuthenticatedUsers(), bucketOptions)); - assertFalse(storage.deleteAcl(bucket.getName(), User.ofAllAuthenticatedUsers(), bucketOptions)); - Acl acl = Acl.of(User.ofAllAuthenticatedUsers(), Role.READER); - assertNotNull(storage.createAcl(bucket.getName(), acl, bucketOptions)); - Acl updatedAcl = - storage.updateAcl( - bucket.getName(), acl.toBuilder().setRole(Role.WRITER).build(), bucketOptions); - assertEquals(Role.WRITER, updatedAcl.getRole()); - Set acls = new HashSet<>(); - acls.addAll(storage.listAcls(bucket.getName(), bucketOptions)); - assertTrue(acls.contains(updatedAcl)); - assertTrue(storage.deleteAcl(bucket.getName(), User.ofAllAuthenticatedUsers(), bucketOptions)); - assertNull(storage.getAcl(bucket.getName(), User.ofAllAuthenticatedUsers(), bucketOptions)); - } - @Test public void bucket_defaultAcl_get() { String bucketName = bucket.getName(); @@ -1061,68 +1027,6 @@ public void testEnableAndDisableBucketPolicyOnlyOnExistingBucket() throws Except } } - @Test - @CrossRun.Ignore(transports = Transport.GRPC) - public void testBlobAcl() { - // TODO: break this test up into each of the respective scenarios - // 1. get ACL for specific entity - // 2. Create an ACL for specific entity - // 3. Update ACL to change role of a specific entity - // 4. List ACLs for an object - // 5. Delete an ACL for a specific entity - // 6. Attempt to get an acl for an object that doesn't exist - // 7. Attempt to delete an acl for an object that doesn't exist - // 8. Attempt to create an acl for an object that doesn't exist - // 9. Attempt to update an acl for an object that doesn't exist - // 10. Attempt to list acls for an object that doesn't exist - BlobId blobId = BlobId.of(bucket.getName(), "test-blob-acl"); - BlobInfo blob = BlobInfo.newBuilder(blobId).build(); - storage.create(blob); - assertNull(storage.getAcl(blobId, User.ofAllAuthenticatedUsers())); - Acl acl = Acl.of(User.ofAllAuthenticatedUsers(), Role.READER); - assertNotNull(storage.createAcl(blobId, acl)); - Acl updatedAcl = storage.updateAcl(blobId, acl.toBuilder().setRole(Role.OWNER).build()); - assertEquals(Role.OWNER, updatedAcl.getRole()); - Set acls = new HashSet<>(storage.listAcls(blobId)); - assertTrue(acls.contains(updatedAcl)); - assertTrue(storage.deleteAcl(blobId, User.ofAllAuthenticatedUsers())); - assertNull(storage.getAcl(blobId, User.ofAllAuthenticatedUsers())); - // test non-existing blob - BlobId otherBlobId = BlobId.of(bucket.getName(), "test-blob-acl", -1L); - try { - assertNull(storage.getAcl(otherBlobId, User.ofAllAuthenticatedUsers())); - fail("Expected an 'Invalid argument' exception"); - } catch (StorageException e) { - assertThat(e.getMessage()).contains("Invalid argument"); - } - - try { - assertFalse(storage.deleteAcl(otherBlobId, User.ofAllAuthenticatedUsers())); - fail("Expected an 'Invalid argument' exception"); - } catch (StorageException e) { - assertThat(e.getMessage()).contains("Invalid argument"); - } - - try { - storage.createAcl(otherBlobId, acl); - fail("Expected StorageException"); - } catch (StorageException ex) { - // expected - } - try { - storage.updateAcl(otherBlobId, acl); - fail("Expected StorageException"); - } catch (StorageException ex) { - // expected - } - try { - storage.listAcls(otherBlobId); - fail("Expected StorageException"); - } catch (StorageException ex) { - // expected - } - } - static ImmutableList dropEtags(List defaultAcls) { return defaultAcls.stream() .map(ITAccessTest::dropEtag) diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/runner/registry/AbstractStorageProxy.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/runner/registry/AbstractStorageProxy.java new file mode 100644 index 0000000000..11440cb190 --- /dev/null +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/runner/registry/AbstractStorageProxy.java @@ -0,0 +1,490 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.storage.it.runner.registry; + +import com.google.api.gax.paging.Page; +import com.google.cloud.Policy; +import com.google.cloud.ReadChannel; +import com.google.cloud.WriteChannel; +import com.google.cloud.storage.Acl; +import com.google.cloud.storage.Acl.Entity; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Bucket; +import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.CopyWriter; +import com.google.cloud.storage.HmacKey; +import com.google.cloud.storage.HmacKey.HmacKeyMetadata; +import com.google.cloud.storage.HmacKey.HmacKeyState; +import com.google.cloud.storage.Notification; +import com.google.cloud.storage.NotificationInfo; +import com.google.cloud.storage.PostPolicyV4; +import com.google.cloud.storage.PostPolicyV4.PostConditionsV4; +import com.google.cloud.storage.PostPolicyV4.PostFieldsV4; +import com.google.cloud.storage.ServiceAccount; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageBatch; +import com.google.cloud.storage.StorageOptions; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Define a simplistic class which implements {@link Storage} while also delegating all calls to an + * underlying instance of {@link Storage}. When this class is extended it can then override + * individual methods rather than the entire Storage interface. + */ +abstract class AbstractStorageProxy implements Storage { + + protected final Storage delegate; + + protected AbstractStorageProxy(Storage delegate) { + this.delegate = delegate; + } + + @Override + public Bucket create(BucketInfo bucketInfo, BucketTargetOption... options) { + return delegate.create(bucketInfo, options); + } + + @Override + public Blob create(BlobInfo blobInfo, BlobTargetOption... options) { + return delegate.create(blobInfo, options); + } + + @Override + public Blob create(BlobInfo blobInfo, byte[] content, BlobTargetOption... options) { + return delegate.create(blobInfo, content, options); + } + + @Override + public Blob create( + BlobInfo blobInfo, byte[] content, int offset, int length, BlobTargetOption... options) { + return delegate.create(blobInfo, content, offset, length, options); + } + + @Override + @Deprecated + public Blob create(BlobInfo blobInfo, InputStream content, BlobWriteOption... options) { + return delegate.create(blobInfo, content, options); + } + + @Override + public Blob createFrom(BlobInfo blobInfo, Path path, BlobWriteOption... options) + throws IOException { + return delegate.createFrom(blobInfo, path, options); + } + + @Override + public Blob createFrom(BlobInfo blobInfo, Path path, int bufferSize, BlobWriteOption... options) + throws IOException { + return delegate.createFrom(blobInfo, path, bufferSize, options); + } + + @Override + public Blob createFrom(BlobInfo blobInfo, InputStream content, BlobWriteOption... options) + throws IOException { + return delegate.createFrom(blobInfo, content, options); + } + + @Override + public Blob createFrom( + BlobInfo blobInfo, InputStream content, int bufferSize, BlobWriteOption... options) + throws IOException { + return delegate.createFrom(blobInfo, content, bufferSize, options); + } + + @Override + public Bucket get(String bucket, BucketGetOption... options) { + return delegate.get(bucket, options); + } + + @Override + public Bucket lockRetentionPolicy(BucketInfo bucket, BucketTargetOption... options) { + return delegate.lockRetentionPolicy(bucket, options); + } + + @Override + public Blob get(String bucket, String blob, BlobGetOption... options) { + return delegate.get(bucket, blob, options); + } + + @Override + public Blob get(BlobId blob, BlobGetOption... options) { + return delegate.get(blob, options); + } + + @Override + public Blob get(BlobId blob) { + return delegate.get(blob); + } + + @Override + public Page list(BucketListOption... options) { + return delegate.list(options); + } + + @Override + public Page list(String bucket, BlobListOption... options) { + return delegate.list(bucket, options); + } + + @Override + public Bucket update(BucketInfo bucketInfo, BucketTargetOption... options) { + return delegate.update(bucketInfo, options); + } + + @Override + public Blob update(BlobInfo blobInfo, BlobTargetOption... options) { + return delegate.update(blobInfo, options); + } + + @Override + public Blob update(BlobInfo blobInfo) { + return delegate.update(blobInfo); + } + + @Override + public boolean delete(String bucket, BucketSourceOption... options) { + return delegate.delete(bucket, options); + } + + @Override + public boolean delete(String bucket, String blob, BlobSourceOption... options) { + return delegate.delete(bucket, blob, options); + } + + @Override + public boolean delete(BlobId blob, BlobSourceOption... options) { + return delegate.delete(blob, options); + } + + @Override + public boolean delete(BlobId blob) { + return delegate.delete(blob); + } + + @Override + public Blob compose(ComposeRequest composeRequest) { + return delegate.compose(composeRequest); + } + + @Override + public CopyWriter copy(CopyRequest copyRequest) { + return delegate.copy(copyRequest); + } + + @Override + public byte[] readAllBytes(String bucket, String blob, BlobSourceOption... options) { + return delegate.readAllBytes(bucket, blob, options); + } + + @Override + public byte[] readAllBytes(BlobId blob, BlobSourceOption... options) { + return delegate.readAllBytes(blob, options); + } + + @Override + public StorageBatch batch() { + return delegate.batch(); + } + + @Override + public ReadChannel reader(String bucket, String blob, BlobSourceOption... options) { + return delegate.reader(bucket, blob, options); + } + + @Override + public ReadChannel reader(BlobId blob, BlobSourceOption... options) { + return delegate.reader(blob, options); + } + + @Override + public void downloadTo(BlobId blob, Path path, BlobSourceOption... options) { + delegate.downloadTo(blob, path, options); + } + + @Override + public void downloadTo(BlobId blob, OutputStream outputStream, BlobSourceOption... options) { + delegate.downloadTo(blob, outputStream, options); + } + + @Override + public WriteChannel writer(BlobInfo blobInfo, BlobWriteOption... options) { + return delegate.writer(blobInfo, options); + } + + @Override + public WriteChannel writer(URL signedURL) { + return delegate.writer(signedURL); + } + + @Override + public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options) { + return delegate.signUrl(blobInfo, duration, unit, options); + } + + @Override + public PostPolicyV4 generateSignedPostPolicyV4( + BlobInfo blobInfo, + long duration, + TimeUnit unit, + PostFieldsV4 fields, + PostConditionsV4 conditions, + PostPolicyV4Option... options) { + return delegate.generateSignedPostPolicyV4( + blobInfo, duration, unit, fields, conditions, options); + } + + @Override + public PostPolicyV4 generateSignedPostPolicyV4( + BlobInfo blobInfo, + long duration, + TimeUnit unit, + PostFieldsV4 fields, + PostPolicyV4Option... options) { + return delegate.generateSignedPostPolicyV4(blobInfo, duration, unit, fields, options); + } + + @Override + public PostPolicyV4 generateSignedPostPolicyV4( + BlobInfo blobInfo, + long duration, + TimeUnit unit, + PostConditionsV4 conditions, + PostPolicyV4Option... options) { + return delegate.generateSignedPostPolicyV4(blobInfo, duration, unit, conditions, options); + } + + @Override + public PostPolicyV4 generateSignedPostPolicyV4( + BlobInfo blobInfo, long duration, TimeUnit unit, PostPolicyV4Option... options) { + return delegate.generateSignedPostPolicyV4(blobInfo, duration, unit, options); + } + + @Override + public List get(BlobId... blobIds) { + return delegate.get(blobIds); + } + + @Override + public List get(Iterable blobIds) { + return delegate.get(blobIds); + } + + @Override + public List update(BlobInfo... blobInfos) { + return delegate.update(blobInfos); + } + + @Override + public List update(Iterable blobInfos) { + return delegate.update(blobInfos); + } + + @Override + public List delete(BlobId... blobIds) { + return delegate.delete(blobIds); + } + + @Override + public List delete(Iterable blobIds) { + return delegate.delete(blobIds); + } + + @Override + public Acl getAcl(String bucket, Entity entity, BucketSourceOption... options) { + return delegate.getAcl(bucket, entity, options); + } + + @Override + public Acl getAcl(String bucket, Entity entity) { + return delegate.getAcl(bucket, entity); + } + + @Override + public boolean deleteAcl(String bucket, Entity entity, BucketSourceOption... options) { + return delegate.deleteAcl(bucket, entity, options); + } + + @Override + public boolean deleteAcl(String bucket, Entity entity) { + return delegate.deleteAcl(bucket, entity); + } + + @Override + public Acl createAcl(String bucket, Acl acl, BucketSourceOption... options) { + return delegate.createAcl(bucket, acl, options); + } + + @Override + public Acl createAcl(String bucket, Acl acl) { + return delegate.createAcl(bucket, acl); + } + + @Override + public Acl updateAcl(String bucket, Acl acl, BucketSourceOption... options) { + return delegate.updateAcl(bucket, acl, options); + } + + @Override + public Acl updateAcl(String bucket, Acl acl) { + return delegate.updateAcl(bucket, acl); + } + + @Override + public List listAcls(String bucket, BucketSourceOption... options) { + return delegate.listAcls(bucket, options); + } + + @Override + public List listAcls(String bucket) { + return delegate.listAcls(bucket); + } + + @Override + public Acl getDefaultAcl(String bucket, Entity entity) { + return delegate.getDefaultAcl(bucket, entity); + } + + @Override + public boolean deleteDefaultAcl(String bucket, Entity entity) { + return delegate.deleteDefaultAcl(bucket, entity); + } + + @Override + public Acl createDefaultAcl(String bucket, Acl acl) { + return delegate.createDefaultAcl(bucket, acl); + } + + @Override + public Acl updateDefaultAcl(String bucket, Acl acl) { + return delegate.updateDefaultAcl(bucket, acl); + } + + @Override + public List listDefaultAcls(String bucket) { + return delegate.listDefaultAcls(bucket); + } + + @Override + public Acl getAcl(BlobId blob, Entity entity) { + return delegate.getAcl(blob, entity); + } + + @Override + public boolean deleteAcl(BlobId blob, Entity entity) { + return delegate.deleteAcl(blob, entity); + } + + @Override + public Acl createAcl(BlobId blob, Acl acl) { + return delegate.createAcl(blob, acl); + } + + @Override + public Acl updateAcl(BlobId blob, Acl acl) { + return delegate.updateAcl(blob, acl); + } + + @Override + public List listAcls(BlobId blob) { + return delegate.listAcls(blob); + } + + @Override + public HmacKey createHmacKey(ServiceAccount serviceAccount, CreateHmacKeyOption... options) { + return delegate.createHmacKey(serviceAccount, options); + } + + @Override + public Page listHmacKeys(ListHmacKeysOption... options) { + return delegate.listHmacKeys(options); + } + + @Override + public HmacKeyMetadata getHmacKey(String accessId, GetHmacKeyOption... options) { + return delegate.getHmacKey(accessId, options); + } + + @Override + public void deleteHmacKey(HmacKeyMetadata hmacKeyMetadata, DeleteHmacKeyOption... options) { + delegate.deleteHmacKey(hmacKeyMetadata, options); + } + + @Override + public HmacKeyMetadata updateHmacKeyState( + HmacKeyMetadata hmacKeyMetadata, HmacKeyState state, UpdateHmacKeyOption... options) { + return delegate.updateHmacKeyState(hmacKeyMetadata, state, options); + } + + @Override + public Policy getIamPolicy(String bucket, BucketSourceOption... options) { + return delegate.getIamPolicy(bucket, options); + } + + @Override + public Policy setIamPolicy(String bucket, Policy policy, BucketSourceOption... options) { + return delegate.setIamPolicy(bucket, policy, options); + } + + @Override + public List testIamPermissions( + String bucket, List permissions, BucketSourceOption... options) { + return delegate.testIamPermissions(bucket, permissions, options); + } + + @Override + public ServiceAccount getServiceAccount(String projectId) { + return delegate.getServiceAccount(projectId); + } + + @Override + public Notification createNotification(String bucket, NotificationInfo notificationInfo) { + return delegate.createNotification(bucket, notificationInfo); + } + + @Override + public Notification getNotification(String bucket, String notificationId) { + return delegate.getNotification(bucket, notificationId); + } + + @Override + public List listNotifications(String bucket) { + return delegate.listNotifications(bucket); + } + + @Override + public boolean deleteNotification(String bucket, String notificationId) { + return delegate.deleteNotification(bucket, notificationId); + } + + @Override + public void close() throws Exception { + delegate.close(); + } + + @Override + public StorageOptions getOptions() { + return delegate.getOptions(); + } +} diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/runner/registry/StorageInstance.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/runner/registry/StorageInstance.java index 59bab51064..b653dea056 100644 --- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/runner/registry/StorageInstance.java +++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/runner/registry/StorageInstance.java @@ -16,14 +16,13 @@ package com.google.cloud.storage.it.runner.registry; +import com.google.cloud.Policy; +import com.google.cloud.storage.Acl; +import com.google.cloud.storage.Acl.Entity; +import com.google.cloud.storage.Bucket; import com.google.cloud.storage.BucketInfo; import com.google.cloud.storage.Storage; -import com.google.cloud.storage.Storage.BucketSourceOption; -import com.google.cloud.storage.Storage.BucketTargetOption; import com.google.cloud.storage.StorageOptions; -import com.google.common.reflect.Reflection; -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; final class StorageInstance implements ManagedLifecycle { @@ -48,45 +47,10 @@ public Object get() { return proxy; } - private static final Object[] updateParameters = {BucketInfo.class, BucketTargetOption[].class}; - private static final Object[] deleteParameters = {String.class, BucketSourceOption[].class}; - @Override public void start() { storage = options.getService(); - // Define a proxy which can veto calls which attempt to mutate protected buckets - // this helps guard against a test trying to mutate the global bucket rather than creating its - // own bucket. - proxy = - Reflection.newProxy( - Storage.class, - ((proxy1, method, args) -> { - String methodName = method.getName(); - Class[] parameterTypes = method.getParameterTypes(); - if (methodName.equals("update") - && Arrays.deepEquals(parameterTypes, updateParameters)) { - BucketInfo bucketInfo = (BucketInfo) args[0]; - if (protectedBucketNames.isProtected(bucketInfo.getName())) { - throw err(bucketInfo.getName()); - } - } else if (methodName.equals("delete") - && Arrays.deepEquals(parameterTypes, deleteParameters)) { - String bucketName = (String) args[0]; - if (protectedBucketNames.isProtected(bucketName)) { - throw err(bucketName); - } - } else if (methodName.equals("setIamPolicy")) { - String bucketName = (String) args[0]; - if (protectedBucketNames.isProtected(bucketName)) { - throw err(bucketName); - } - } - try { - return method.invoke(storage, args); - } catch (InvocationTargetException e) { - throw e.getCause(); - } - })); + proxy = new VetoingStorageProxy(); } @Override @@ -103,9 +67,119 @@ private static VetoedBucketUpdateException err(String bucketName) { return new VetoedBucketUpdateException("Attempted to modify global bucket: " + bucketName); } - private static final class VetoedBucketUpdateException extends RuntimeException { + private static class VetoException extends RuntimeException { + private VetoException(String message) { + super(message); + } + } + + private static final class VetoedBucketUpdateException extends VetoException { private VetoedBucketUpdateException(String message) { super(message); } } + + /** + * Define a proxy which can veto calls attempting to mutate protected buckets. this helps guard + * against a test trying to mutate the global bucket rather than creating its own bucket. + */ + private final class VetoingStorageProxy extends AbstractStorageProxy { + + private VetoingStorageProxy() { + super(storage); + } + + @Override + public Bucket update(BucketInfo bucketInfo, BucketTargetOption... options) { + checkBucketProtected(bucketInfo); + return super.update(bucketInfo, options); + } + + @Override + public boolean delete(String bucket, BucketSourceOption... options) { + checkBucketProtected(bucket); + return super.delete(bucket, options); + } + + @Override + public Policy setIamPolicy(String bucket, Policy policy, BucketSourceOption... options) { + checkBucketProtected(bucket); + return super.setIamPolicy(bucket, policy, options); + } + + @Override + public boolean deleteDefaultAcl(String bucket, Entity entity) { + checkBucketProtected(bucket); + return super.deleteDefaultAcl(bucket, entity); + } + + @Override + public Acl createDefaultAcl(String bucket, Acl acl) { + checkBucketProtected(bucket); + return super.createDefaultAcl(bucket, acl); + } + + @Override + public Acl updateDefaultAcl(String bucket, Acl acl) { + checkBucketProtected(bucket); + return super.updateDefaultAcl(bucket, acl); + } + + @Override + public boolean deleteAcl(String bucket, Entity entity, BucketSourceOption... options) { + checkBucketProtected(bucket); + return super.deleteAcl(bucket, entity, options); + } + + @Override + public boolean deleteAcl(String bucket, Entity entity) { + checkBucketProtected(bucket); + return super.deleteAcl(bucket, entity); + } + + @Override + public Acl createAcl(String bucket, Acl acl, BucketSourceOption... options) { + checkBucketProtected(bucket); + return super.createAcl(bucket, acl, options); + } + + @Override + public Acl createAcl(String bucket, Acl acl) { + checkBucketProtected(bucket); + return super.createAcl(bucket, acl); + } + + @Override + public Acl updateAcl(String bucket, Acl acl, BucketSourceOption... options) { + checkBucketProtected(bucket); + return super.updateAcl(bucket, acl, options); + } + + @Override + public Acl updateAcl(String bucket, Acl acl) { + checkBucketProtected(bucket); + return super.updateAcl(bucket, acl); + } + + @Override + public Bucket lockRetentionPolicy(BucketInfo bucket, BucketTargetOption... options) { + checkBucketProtected(bucket); + return super.lockRetentionPolicy(bucket, options); + } + + @Override + public void close() throws Exception { + throw new VetoException("Called #close() on global Storage instance"); + } + + private void checkBucketProtected(BucketInfo bucket) { + checkBucketProtected(bucket.getName()); + } + + private void checkBucketProtected(String bucketName) { + if (protectedBucketNames.isProtected(bucketName)) { + throw err(bucketName); + } + } + } }