diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java
index c13395238b..cca80482fe 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/BucketInfo.java
@@ -105,17 +105,56 @@ public com.google.api.services.storage.model.Bucket apply(BucketInfo bucketInfo)
private static final Logger log = Logger.getLogger(BucketInfo.class.getName());
+ /**
+ * Public Access Prevention enum with expected values.
+ *
+ * @see public-access-prevention
+ */
+ public enum PublicAccessPrevention {
+ ENFORCED("enforced"),
+ /** Default value for Public Access Prevention */
+ UNSPECIFIED("unspecified"),
+ /**
+ * If the api returns a value that isn't defined in {@link PublicAccessPrevention} this value
+ * will be returned.
+ */
+ UNKNOWN(null);
+
+ private final String value;
+
+ PublicAccessPrevention(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static PublicAccessPrevention parse(String value) {
+ String upper = value.toUpperCase();
+ try {
+ return valueOf(upper);
+ } catch (IllegalArgumentException ignore) {
+ return UNKNOWN;
+ }
+ }
+ }
+
/**
* The Bucket's IAM Configuration.
*
* @see uniform
* bucket-level access
+ * @see public-access-prevention
*/
public static class IamConfiguration implements Serializable {
private static final long serialVersionUID = -8671736104909424616L;
- private Boolean isUniformBucketLevelAccessEnabled;
- private Long uniformBucketLevelAccessLockedTime;
+ private final Boolean isUniformBucketLevelAccessEnabled;
+ private final Long uniformBucketLevelAccessLockedTime;
+ private final PublicAccessPrevention publicAccessPrevention;
@Override
public boolean equals(Object o) {
@@ -129,12 +168,16 @@ public boolean equals(Object o) {
@Override
public int hashCode() {
- return Objects.hash(isUniformBucketLevelAccessEnabled, uniformBucketLevelAccessLockedTime);
+ return Objects.hash(
+ isUniformBucketLevelAccessEnabled,
+ uniformBucketLevelAccessLockedTime,
+ publicAccessPrevention);
}
private IamConfiguration(Builder builder) {
this.isUniformBucketLevelAccessEnabled = builder.isUniformBucketLevelAccessEnabled;
this.uniformBucketLevelAccessLockedTime = builder.uniformBucketLevelAccessLockedTime;
+ this.publicAccessPrevention = builder.publicAccessPrevention;
}
public static Builder newBuilder() {
@@ -145,6 +188,7 @@ public Builder toBuilder() {
Builder builder = new Builder();
builder.isUniformBucketLevelAccessEnabled = isUniformBucketLevelAccessEnabled;
builder.uniformBucketLevelAccessLockedTime = uniformBucketLevelAccessLockedTime;
+ builder.publicAccessPrevention = publicAccessPrevention;
return builder;
}
@@ -168,6 +212,11 @@ public Long getUniformBucketLevelAccessLockedTime() {
return uniformBucketLevelAccessLockedTime;
}
+ /** Returns the Public Access Prevention. * */
+ public PublicAccessPrevention getPublicAccessPrevention() {
+ return publicAccessPrevention;
+ }
+
Bucket.IamConfiguration toPb() {
Bucket.IamConfiguration iamConfiguration = new Bucket.IamConfiguration();
@@ -180,6 +229,8 @@ Bucket.IamConfiguration toPb() {
: new DateTime(uniformBucketLevelAccessLockedTime));
iamConfiguration.setUniformBucketLevelAccess(uniformBucketLevelAccess);
+ iamConfiguration.setPublicAccessPrevention(
+ publicAccessPrevention == null ? null : publicAccessPrevention.getValue());
return iamConfiguration;
}
@@ -188,10 +239,17 @@ static IamConfiguration fromPb(Bucket.IamConfiguration iamConfiguration) {
Bucket.IamConfiguration.UniformBucketLevelAccess uniformBucketLevelAccess =
iamConfiguration.getUniformBucketLevelAccess();
DateTime lockedTime = uniformBucketLevelAccess.getLockedTime();
+ String publicAccessPrevention = iamConfiguration.getPublicAccessPrevention();
+
+ PublicAccessPrevention publicAccessPreventionValue = null;
+ if (publicAccessPrevention != null) {
+ publicAccessPreventionValue = PublicAccessPrevention.parse(publicAccessPrevention);
+ }
return newBuilder()
.setIsUniformBucketLevelAccessEnabled(uniformBucketLevelAccess.getEnabled())
.setUniformBucketLevelAccessLockedTime(lockedTime == null ? null : lockedTime.getValue())
+ .setPublicAccessPrevention(publicAccessPreventionValue)
.build();
}
@@ -199,6 +257,7 @@ static IamConfiguration fromPb(Bucket.IamConfiguration iamConfiguration) {
public static class Builder {
private Boolean isUniformBucketLevelAccessEnabled;
private Long uniformBucketLevelAccessLockedTime;
+ private PublicAccessPrevention publicAccessPrevention;
/** Deprecated in favor of setIsUniformBucketLevelAccessEnabled(). */
@Deprecated
@@ -239,6 +298,18 @@ Builder setUniformBucketLevelAccessLockedTime(Long uniformBucketLevelAccessLocke
return this;
}
+ /**
+ * Sets the bucket's Public Access Prevention configuration. Currently supported options are
+ * {@link PublicAccessPrevention#UNSPECIFIED} or {@link PublicAccessPrevention#ENFORCED}
+ *
+ * @see public-access-prevention
+ */
+ public Builder setPublicAccessPrevention(PublicAccessPrevention publicAccessPrevention) {
+ this.publicAccessPrevention = publicAccessPrevention;
+ return this;
+ }
+
/** Builds an {@code IamConfiguration} object */
public IamConfiguration build() {
return new IamConfiguration(this);
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java
index d72901c17e..c74625a154 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketInfoTest.java
@@ -18,10 +18,13 @@
import static com.google.cloud.storage.Acl.Project.ProjectRole.VIEWERS;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import com.google.api.client.json.JsonGenerator;
+import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.DateTime;
import com.google.api.services.storage.model.Bucket;
import com.google.api.services.storage.model.Bucket.Lifecycle;
@@ -33,14 +36,18 @@
import com.google.cloud.storage.BucketInfo.CreatedBeforeDeleteRule;
import com.google.cloud.storage.BucketInfo.DeleteRule;
import com.google.cloud.storage.BucketInfo.DeleteRule.Type;
+import com.google.cloud.storage.BucketInfo.IamConfiguration;
import com.google.cloud.storage.BucketInfo.IsLiveDeleteRule;
import com.google.cloud.storage.BucketInfo.LifecycleRule;
import com.google.cloud.storage.BucketInfo.LifecycleRule.LifecycleAction;
import com.google.cloud.storage.BucketInfo.LifecycleRule.LifecycleCondition;
import com.google.cloud.storage.BucketInfo.NumNewerVersionsDeleteRule;
+import com.google.cloud.storage.BucketInfo.PublicAccessPrevention;
import com.google.cloud.storage.BucketInfo.RawDeleteRule;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -79,6 +86,7 @@ public class BucketInfoTest {
BucketInfo.IamConfiguration.newBuilder()
.setIsUniformBucketLevelAccessEnabled(true)
.setUniformBucketLevelAccessLockedTime(System.currentTimeMillis())
+ .setPublicAccessPrevention(BucketInfo.PublicAccessPrevention.ENFORCED)
.build();
private static final BucketInfo.Logging LOGGING =
BucketInfo.Logging.newBuilder()
@@ -363,11 +371,45 @@ public void testIamConfiguration() {
BucketInfo.IamConfiguration.newBuilder()
.setIsUniformBucketLevelAccessEnabled(true)
.setUniformBucketLevelAccessLockedTime(System.currentTimeMillis())
+ .setPublicAccessPrevention(BucketInfo.PublicAccessPrevention.ENFORCED)
.build()
.toPb();
assertEquals(Boolean.TRUE, iamConfiguration.getUniformBucketLevelAccess().getEnabled());
assertNotNull(iamConfiguration.getUniformBucketLevelAccess().getLockedTime());
+ assertEquals(
+ BucketInfo.PublicAccessPrevention.ENFORCED.getValue(),
+ iamConfiguration.getPublicAccessPrevention());
+ }
+
+ @Test
+ public void testPublicAccessPrevention_ensureAbsentWhenUnknown() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonGenerator jsonGenerator =
+ JacksonFactory.getDefaultInstance().createJsonGenerator(stringWriter);
+
+ jsonGenerator.serialize(
+ BucketInfo.IamConfiguration.newBuilder()
+ .setIsUniformBucketLevelAccessEnabled(true)
+ .setUniformBucketLevelAccessLockedTime(System.currentTimeMillis())
+ .setPublicAccessPrevention(PublicAccessPrevention.UNKNOWN)
+ .build()
+ .toPb());
+ jsonGenerator.flush();
+
+ assertFalse(stringWriter.getBuffer().toString().contains("publicAccessPrevention"));
+ }
+
+ @Test
+ public void testPapValueOfIamConfiguration() {
+ Bucket.IamConfiguration iamConfiguration = new Bucket.IamConfiguration();
+ Bucket.IamConfiguration.UniformBucketLevelAccess uniformBucketLevelAccess =
+ new Bucket.IamConfiguration.UniformBucketLevelAccess();
+ iamConfiguration.setUniformBucketLevelAccess(uniformBucketLevelAccess);
+ iamConfiguration.setPublicAccessPrevention("random-string");
+ IamConfiguration fromPb = IamConfiguration.fromPb(iamConfiguration);
+
+ assertEquals(PublicAccessPrevention.UNKNOWN, fromPb.getPublicAccessPrevention());
}
@Test
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
index f696a28560..146bcbe694 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
@@ -3235,6 +3235,151 @@ public void testEnableAndDisableUniformBucketLevelAccessOnExistingBucket() throw
}
}
+ private Bucket generatePublicAccessPreventionBucket(String bucketName, boolean enforced) {
+ return storage.create(
+ Bucket.newBuilder(bucketName)
+ .setIamConfiguration(
+ BucketInfo.IamConfiguration.newBuilder()
+ .setPublicAccessPrevention(
+ enforced
+ ? BucketInfo.PublicAccessPrevention.ENFORCED
+ : BucketInfo.PublicAccessPrevention.UNSPECIFIED)
+ .build())
+ .build());
+ }
+
+ @Test
+ public void testEnforcedPublicAccessPreventionOnBucket() throws Exception {
+ String papBucket = RemoteStorageHelper.generateBucketName();
+ try {
+ Bucket bucket = generatePublicAccessPreventionBucket(papBucket, true);
+ // Making bucket public should fail.
+ try {
+ storage.setIamPolicy(
+ papBucket,
+ Policy.newBuilder()
+ .setVersion(3)
+ .setBindings(
+ ImmutableList.of(
+ com.google.cloud.Binding.newBuilder()
+ .setRole("roles/storage.objectViewer")
+ .addMembers("allUsers")
+ .build()))
+ .build());
+ fail("pap: expected adding allUsers policy to bucket should fail");
+ } catch (StorageException storageException) {
+ // Creating a bucket with roles/storage.objectViewer is not
+ // allowed when publicAccessPrevention is enabled.
+ assertEquals(storageException.getCode(), 412);
+ }
+
+ // Making object public via ACL should fail.
+ try {
+ // Create a public object
+ bucket.create(
+ "pap-test-object",
+ "".getBytes(),
+ Bucket.BlobTargetOption.predefinedAcl(Storage.PredefinedAcl.PUBLIC_READ));
+ fail("pap: expected adding allUsers ACL to object should fail");
+ } catch (StorageException storageException) {
+ // Creating an object with allUsers roles/storage.viewer permission
+ // is not allowed. When Public Access Prevention is enabled.
+ assertEquals(storageException.getCode(), 412);
+ }
+ } finally {
+ RemoteStorageHelper.forceDelete(storage, papBucket, 1, TimeUnit.MINUTES);
+ }
+ }
+
+ @Test
+ public void testUnspecifiedPublicAccessPreventionOnBucket() throws Exception {
+ String papBucket = RemoteStorageHelper.generateBucketName();
+ try {
+ Bucket bucket = generatePublicAccessPreventionBucket(papBucket, false);
+
+ // Now, making object public or making bucket public should succeed.
+ try {
+ // Create a public object
+ bucket.create(
+ "pap-test-object",
+ "".getBytes(),
+ Bucket.BlobTargetOption.predefinedAcl(Storage.PredefinedAcl.PUBLIC_READ));
+ } catch (StorageException storageException) {
+ fail("pap: expected adding allUsers ACL to object to succeed");
+ }
+
+ // Now, making bucket public should succeed.
+ try {
+ storage.setIamPolicy(
+ papBucket,
+ Policy.newBuilder()
+ .setVersion(3)
+ .setBindings(
+ ImmutableList.of(
+ com.google.cloud.Binding.newBuilder()
+ .setRole("roles/storage.objectViewer")
+ .addMembers("allUsers")
+ .build()))
+ .build());
+ } catch (StorageException storageException) {
+ fail("pap: expected adding allUsers policy to bucket to succeed");
+ }
+ } finally {
+ RemoteStorageHelper.forceDelete(storage, papBucket, 1, TimeUnit.MINUTES);
+ }
+ }
+
+ @Test
+ public void testUBLAWithPublicAccessPreventionOnBucket() throws Exception {
+ String papBucket = RemoteStorageHelper.generateBucketName();
+ try {
+ Bucket bucket = generatePublicAccessPreventionBucket(papBucket, false);
+ assertEquals(
+ bucket.getIamConfiguration().getPublicAccessPrevention(),
+ BucketInfo.PublicAccessPrevention.UNSPECIFIED);
+ assertFalse(bucket.getIamConfiguration().isUniformBucketLevelAccessEnabled());
+ assertFalse(bucket.getIamConfiguration().isBucketPolicyOnlyEnabled());
+
+ // Update PAP setting to ENFORCED and should not affect UBLA setting.
+ bucket
+ .toBuilder()
+ .setIamConfiguration(
+ bucket
+ .getIamConfiguration()
+ .toBuilder()
+ .setPublicAccessPrevention(BucketInfo.PublicAccessPrevention.ENFORCED)
+ .build())
+ .build()
+ .update();
+ bucket = storage.get(papBucket, Storage.BucketGetOption.fields(BucketField.IAMCONFIGURATION));
+ assertEquals(
+ bucket.getIamConfiguration().getPublicAccessPrevention(),
+ BucketInfo.PublicAccessPrevention.ENFORCED);
+ assertFalse(bucket.getIamConfiguration().isUniformBucketLevelAccessEnabled());
+ assertFalse(bucket.getIamConfiguration().isBucketPolicyOnlyEnabled());
+
+ // Updating UBLA should not affect PAP setting.
+ bucket =
+ bucket
+ .toBuilder()
+ .setIamConfiguration(
+ bucket
+ .getIamConfiguration()
+ .toBuilder()
+ .setIsUniformBucketLevelAccessEnabled(true)
+ .build())
+ .build()
+ .update();
+ assertTrue(bucket.getIamConfiguration().isUniformBucketLevelAccessEnabled());
+ assertTrue(bucket.getIamConfiguration().isBucketPolicyOnlyEnabled());
+ assertEquals(
+ bucket.getIamConfiguration().getPublicAccessPrevention(),
+ BucketInfo.PublicAccessPrevention.ENFORCED);
+ } finally {
+ RemoteStorageHelper.forceDelete(storage, papBucket, 1, TimeUnit.MINUTES);
+ }
+ }
+
@Test
public void testUploadUsingSignedURL() throws Exception {
String blobName = "test-signed-url-upload";