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); + } + } + } }