Repository: googleapis/java-storage
Pull request #120 (2020-02-06 18:38:44)

feat: add IAM Conditions support
Add IAM Conditions Support.

Depends on:
https://github.com/googleapis/java-core/pull/110

Pending Review List:
- [x] @jkwlui (IAM Conditions Review)
- [x] @JesseLovelace (Java + Storage Review)




codecov[bot]: # [Codecov](https://codecov.io/gh/googleapis/java-storage/pull/120?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`master@8606ab7`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/googleapis/java-storage/pull/120/graphs/tree.svg?width=650&token=ssm139YOdp&height=150&src=pr)](https://codecov.io/gh/googleapis/java-storage/pull/120?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##             master     #120   +/-   ##
=========================================
  Coverage          ?   63.46%           
  Complexity        ?      537           
=========================================
  Files             ?       30           
  Lines             ?     4752           
  Branches          ?      427           
=========================================
  Hits              ?     3016           
  Misses            ?     1576           
  Partials          ?      160
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/googleapis/java-storage/pull/120?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/googleapis/java-storage/pull/120?src=pr&el=footer). Last update [8606ab7...1dd8343](https://codecov.io/gh/googleapis/java-storage/pull/120?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

frankyn: @elharo could you help cut a release for libraries-bom, @chingor13 released google-cloud-bom which updated java-core dependency to latest release which has IAM Conditions support.

It's blocking the Linkage Monitor.

Patch:
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/PolicyHelper.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/PolicyHelper.java
index aa2901ec..e198d853 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/PolicyHelper.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/PolicyHelper.java
@@ -16,14 +16,14 @@
 
 package com.google.cloud.storage;
 
+import com.google.api.services.storage.model.Expr;
 import com.google.api.services.storage.model.Policy.Bindings;
-import com.google.cloud.Identity;
+import com.google.cloud.Binding;
+import com.google.cloud.Condition;
 import com.google.cloud.Policy;
-import com.google.cloud.Role;
+import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * Helper for converting between the Policy model provided by the API and the Policy model provided
@@ -35,29 +35,46 @@ class PolicyHelper {
     Policy.Builder policyBuilder = Policy.newBuilder();
     List<Bindings> bindings = apiPolicy.getBindings();
     if (null != bindings && !bindings.isEmpty()) {
+      ImmutableList.Builder<Binding> coreBindings = ImmutableList.builder();
       for (Bindings binding : bindings) {
-        for (String member : binding.getMembers()) {
-          policyBuilder.addIdentity(Role.of(binding.getRole()), Identity.valueOf(member));
+        Binding.Builder bindingBuilder = Binding.newBuilder();
+        bindingBuilder.setRole(binding.getRole());
+        bindingBuilder.setMembers(binding.getMembers());
+        if (binding.getCondition() != null) {
+          Condition.Builder conditionBuilder = Condition.newBuilder();
+          conditionBuilder.setTitle(binding.getCondition().getTitle());
+          conditionBuilder.setDescription(binding.getCondition().getDescription());
+          conditionBuilder.setExpression(binding.getCondition().getExpression());
+          bindingBuilder.setCondition(conditionBuilder.build());
         }
+        coreBindings.add(bindingBuilder.build());
       }
+      policyBuilder.setBindings(coreBindings.build());
     } else {
       throw new IllegalStateException("Missing required bindings.");
     }
-    return policyBuilder.setEtag(apiPolicy.getEtag()).build();
+    return policyBuilder.setEtag(apiPolicy.getEtag()).setVersion(apiPolicy.getVersion()).build();
   }
 
   static com.google.api.services.storage.model.Policy convertToApiPolicy(Policy policy) {
-    List<Bindings> bindings = new ArrayList<>(policy.getBindings().size());
-    for (Map.Entry<Role, Set<Identity>> entry : policy.getBindings().entrySet()) {
-      List<String> members = new ArrayList<>(entry.getValue().size());
-      for (Identity identity : entry.getValue()) {
-        members.add(identity.strValue());
+    List<Bindings> bindings = new ArrayList<>(policy.getBindingsList().size());
+    for (Binding binding : policy.getBindingsList()) {
+      Bindings apiBinding = new Bindings();
+      apiBinding.setRole(binding.getRole());
+      apiBinding.setMembers(new ArrayList<>(binding.getMembers()));
+      if (binding.getCondition() != null) {
+        Expr expr = new Expr();
+        expr.setTitle(binding.getCondition().getTitle());
+        expr.setDescription(binding.getCondition().getDescription());
+        expr.setExpression(binding.getCondition().getExpression());
+        apiBinding.setCondition(expr);
       }
-      bindings.add(new Bindings().setMembers(members).setRole(entry.getKey().getValue()));
+      bindings.add(apiBinding);
     }
     return new com.google.api.services.storage.model.Policy()
         .setBindings(bindings)
-        .setEtag(policy.getEtag());
+        .setEtag(policy.getEtag())
+        .setVersion(policy.getVersion());
   }
 
   private PolicyHelper() {
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java
index 6c1949f3..c95213d8 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java
@@ -269,6 +269,10 @@ public interface Storage extends Service<StorageOptions> {
     public static BucketSourceOption userProject(String userProject) {
       return new BucketSourceOption(StorageRpc.Option.USER_PROJECT, userProject);
     }
+
+    public static BucketSourceOption requestedPolicyVersion(long version) {
+      return new BucketSourceOption(StorageRpc.Option.REQUESTED_POLICY_VERSION, version);
+    }
   }
 
   /** Class for specifying listHmacKeys options */
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
index afd45ae0..af016673 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java
@@ -1376,11 +1376,16 @@ public class HttpStorageRpc implements StorageRpc {
     Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_GET_BUCKET_IAM_POLICY);
     Scope scope = tracer.withSpan(span);
     try {
-      return storage
-          .buckets()
-          .getIamPolicy(bucket)
-          .setUserProject(Option.USER_PROJECT.getString(options))
-          .execute();
+      Storage.Buckets.GetIamPolicy getIamPolicy =
+          storage
+              .buckets()
+              .getIamPolicy(bucket)
+              .setUserProject(Option.USER_PROJECT.getString(options));
+      if (null != Option.REQUESTED_POLICY_VERSION.getLong(options)) {
+        getIamPolicy.setOptionsRequestedPolicyVersion(
+            Option.REQUESTED_POLICY_VERSION.getLong(options).intValue());
+      }
+      return getIamPolicy.execute();
     } catch (IOException ex) {
       span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage()));
       throw translate(ex);
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java
index 439d4b17..36c7a5ff 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java
@@ -64,7 +64,8 @@ public interface StorageRpc extends ServiceRpc {
     USER_PROJECT("userProject"),
     KMS_KEY_NAME("kmsKeyName"),
     SERVICE_ACCOUNT_EMAIL("serviceAccount"),
-    SHOW_DELETED_KEYS("showDeletedKeys");
+    SHOW_DELETED_KEYS("showDeletedKeys"),
+    REQUESTED_POLICY_VERSION("optionsRequestedPolicyVersion");
 
     private final String value;
 


Test patch:
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/PolicyHelperTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/PolicyHelperTest.java
index 2e50797e..dd5c0f78 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/PolicyHelperTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/PolicyHelperTest.java
@@ -41,6 +41,7 @@ public class PolicyHelperTest {
                 Identity.user("test1@gmail.com"),
                 Identity.user("test2@gmail.com"))
             .setEtag(ETAG)
+            .setVersion(1)
             .build();
     com.google.api.services.storage.model.Policy apiPolicy =
         new com.google.api.services.storage.model.Policy()
@@ -53,7 +54,8 @@ public class PolicyHelperTest {
                         .setMembers(
                             ImmutableList.of("user:test1@gmail.com", "user:test2@gmail.com"))
                         .setRole("roles/storage.objectAdmin")))
-            .setEtag(ETAG);
+            .setEtag(ETAG)
+            .setVersion(1);
 
     Policy actualLibPolicy = PolicyHelper.convertFromApiPolicy(apiPolicy);
     com.google.api.services.storage.model.Policy actualApiPolicy =
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java
index 5c809c7b..e1dd06e4 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java
@@ -288,6 +288,7 @@ public class StorageImplTest {
               Identity.user("test1@gmail.com"),
               Identity.user("test2@gmail.com"))
           .setEtag(POLICY_ETAG1)
+          .setVersion(1)
           .build();
 
   private static final ServiceAccount SERVICE_ACCOUNT = ServiceAccount.of("test@google.com");
@@ -302,7 +303,8 @@ public class StorageImplTest {
                   new Bindings()
                       .setMembers(ImmutableList.of("user:test1@gmail.com", "user:test2@gmail.com"))
                       .setRole("roles/storage.objectAdmin")))
-          .setEtag(POLICY_ETAG1);
+          .setEtag(POLICY_ETAG1)
+          .setVersion(1);
 
   private static final String PRIVATE_KEY_STRING =
       "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoG"
@@ -2891,7 +2893,8 @@ public class StorageImplTest {
                     new Bindings()
                         .setMembers(ImmutableList.of("group:test-group@gmail.com"))
                         .setRole("roles/storage.admin")))
-            .setEtag(POLICY_ETAG1);
+            .setEtag(POLICY_ETAG1)
+            .setVersion(1);
     // postCommitApiPolicy is identical but for the etag, which has been updated.
     com.google.api.services.storage.model.Policy postCommitApiPolicy =
         new com.google.api.services.storage.model.Policy()
@@ -2907,7 +2910,8 @@ public class StorageImplTest {
                     new Bindings()
                         .setMembers(ImmutableList.of("group:test-group@gmail.com"))
                         .setRole("roles/storage.admin")))
-            .setEtag(POLICY_ETAG2);
+            .setEtag(POLICY_ETAG2)
+            .setVersion(1);
     Policy postCommitLibPolicy =
         Policy.newBuilder()
             .addIdentity(StorageRoles.objectViewer(), Identity.allUsers())
@@ -2917,6 +2921,7 @@ public class StorageImplTest {
                 Identity.user("test2@gmail.com"))
             .addIdentity(StorageRoles.admin(), Identity.group("test-group@gmail.com"))
             .setEtag(POLICY_ETAG2)
+            .setVersion(1)
             .build();
 
     EasyMock.expect(storageRpcMock.getIamPolicy(BUCKET_NAME1, EMPTY_RPC_OPTIONS))
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 7aad8a74..f9b45c87 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
@@ -35,6 +35,7 @@ import com.google.api.gax.paging.Page;
 import com.google.auth.ServiceAccountSigner;
 import com.google.auth.http.HttpTransportFactory;
 import com.google.auth.oauth2.GoogleCredentials;
+import com.google.cloud.Condition;
 import com.google.cloud.Identity;
 import com.google.cloud.Policy;
 import com.google.cloud.ReadChannel;
@@ -103,6 +104,7 @@ import java.net.URL;
 import java.net.URLConnection;
 import java.nio.ByteBuffer;
 import java.security.Key;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -2341,26 +2343,74 @@ public class ITStorageTest {
   }
 
   @Test
-  public void testBucketPolicy() {
-    testBucketPolicyRequesterPays(true);
-    testBucketPolicyRequesterPays(false);
-  }
-
-  private void testBucketPolicyRequesterPays(boolean requesterPays) {
-    if (requesterPays) {
-      Bucket remoteBucket = storage.get(BUCKET, Storage.BucketGetOption.fields(BucketField.ID));
-      assertNull(remoteBucket.requesterPays());
-      remoteBucket = remoteBucket.toBuilder().setRequesterPays(true).build();
-      Bucket updatedBucket = storage.update(remoteBucket);
-      assertTrue(updatedBucket.requesterPays());
-    }
+  public void testBucketPolicyV1RequesterPays() {
+    Bucket remoteBucket = storage.get(BUCKET, Storage.BucketGetOption.fields(BucketField.ID));
+    assertNull(remoteBucket.requesterPays());
+    remoteBucket = remoteBucket.toBuilder().setRequesterPays(true).build();
+    Bucket updatedBucket = storage.update(remoteBucket);
+    assertTrue(updatedBucket.requesterPays());
 
     String projectId = remoteStorageHelper.getOptions().getProjectId();
 
     Storage.BucketSourceOption[] bucketOptions =
-        requesterPays
-            ? new Storage.BucketSourceOption[] {Storage.BucketSourceOption.userProject(projectId)}
-            : new Storage.BucketSourceOption[] {};
+        new Storage.BucketSourceOption[] {Storage.BucketSourceOption.userProject(projectId)};
+    Identity projectOwner = Identity.projectOwner(projectId);
+    Identity projectEditor = Identity.projectEditor(projectId);
+    Identity projectViewer = Identity.projectViewer(projectId);
+    Map<com.google.cloud.Role, Set<Identity>> bindingsWithoutPublicRead =
+        ImmutableMap.of(
+            StorageRoles.legacyBucketOwner(),
+            new HashSet<>(Arrays.asList(projectOwner, projectEditor)),
+            StorageRoles.legacyBucketReader(),
+            (Set<Identity>) new HashSet<>(Collections.singleton(projectViewer)));
+    Map<com.google.cloud.Role, Set<Identity>> bindingsWithPublicRead =
+        ImmutableMap.of(
+            StorageRoles.legacyBucketOwner(),
+            new HashSet<>(Arrays.asList(projectOwner, projectEditor)),
+            StorageRoles.legacyBucketReader(),
+            new HashSet<>(Collections.singleton(projectViewer)),
+            StorageRoles.legacyObjectReader(),
+            (Set<Identity>) new HashSet<>(Collections.singleton(Identity.allUsers())));
+
+    // Validate getting policy.
+    Policy currentPolicy = storage.getIamPolicy(BUCKET, bucketOptions);
+    assertEquals(bindingsWithoutPublicRead, currentPolicy.getBindings());
+
+    // Validate updating policy.
+    Policy updatedPolicy =
+        storage.setIamPolicy(
+            BUCKET,
+            currentPolicy
+                .toBuilder()
+                .addIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
+                .build(),
+            bucketOptions);
+    assertEquals(bindingsWithPublicRead, updatedPolicy.getBindings());
+    Policy revertedPolicy =
+        storage.setIamPolicy(
+            BUCKET,
+            updatedPolicy
+                .toBuilder()
+                .removeIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
+                .build(),
+            bucketOptions);
+    assertEquals(bindingsWithoutPublicRead, revertedPolicy.getBindings());
+
+    // Validate testing permissions.
+    List<Boolean> expectedPermissions = ImmutableList.of(true, true);
+    assertEquals(
+        expectedPermissions,
+        storage.testIamPermissions(
+            BUCKET,
+            ImmutableList.of("storage.buckets.getIamPolicy", "storage.buckets.setIamPolicy"),
+            bucketOptions));
+  }
+
+  @Test
+  public void testBucketPolicyV1() {
+    String projectId = remoteStorageHelper.getOptions().getProjectId();
+
+    Storage.BucketSourceOption[] bucketOptions = new Storage.BucketSourceOption[] {};
     Identity projectOwner = Identity.projectOwner(projectId);
     Identity projectEditor = Identity.projectEditor(projectId);
     Identity projectViewer = Identity.projectViewer(projectId);
@@ -2413,6 +2463,162 @@ public class ITStorageTest {
             bucketOptions));
   }
 
+  @Test
+  public void testBucketPolicyV3() {
+    // Enable Uniform Bucket-Level Access
+    storage.update(
+        BucketInfo.newBuilder(BUCKET)
+            .setIamConfiguration(
+                BucketInfo.IamConfiguration.newBuilder()
+                    .setIsUniformBucketLevelAccessEnabled(true)
+                    .build())
+            .build());
+    String projectId = remoteStorageHelper.getOptions().getProjectId();
+
+    Storage.BucketSourceOption[] bucketOptions =
+        new Storage.BucketSourceOption[] {Storage.BucketSourceOption.requestedPolicyVersion(3)};
+    Identity projectOwner = Identity.projectOwner(projectId);
+    Identity projectEditor = Identity.projectEditor(projectId);
+    Identity projectViewer = Identity.projectViewer(projectId);
+    List<com.google.cloud.Binding> bindingsWithoutPublicRead =
+        ImmutableList.of(
+            com.google.cloud.Binding.newBuilder()
+                .setRole(StorageRoles.legacyBucketOwner().toString())
+                .setMembers(ImmutableList.of(projectEditor.strValue(), projectOwner.strValue()))
+                .build(),
+            com.google.cloud.Binding.newBuilder()
+                .setRole(StorageRoles.legacyBucketReader().toString())
+                .setMembers(ImmutableList.of(projectViewer.strValue()))
+                .build());
+    List<com.google.cloud.Binding> bindingsWithPublicRead =
+        ImmutableList.of(
+            com.google.cloud.Binding.newBuilder()
+                .setRole(StorageRoles.legacyBucketReader().toString())
+                .setMembers(ImmutableList.of(projectViewer.strValue()))
+                .build(),
+            com.google.cloud.Binding.newBuilder()
+                .setRole(StorageRoles.legacyBucketOwner().toString())
+                .setMembers(ImmutableList.of(projectEditor.strValue(), projectOwner.strValue()))
+                .build(),
+            com.google.cloud.Binding.newBuilder()
+                .setRole(StorageRoles.legacyObjectReader().toString())
+                .setMembers(ImmutableList.of("allUsers"))
+                .build());
+
+    List<com.google.cloud.Binding> bindingsWithConditionalPolicy =
+        ImmutableList.of(
+            com.google.cloud.Binding.newBuilder()
+                .setRole(StorageRoles.legacyBucketReader().toString())
+                .setMembers(ImmutableList.of(projectViewer.strValue()))
+                .build(),
+            com.google.cloud.Binding.newBuilder()
+                .setRole(StorageRoles.legacyBucketOwner().toString())
+                .setMembers(ImmutableList.of(projectEditor.strValue(), projectOwner.strValue()))
+                .build(),
+            com.google.cloud.Binding.newBuilder()
+                .setRole(StorageRoles.legacyObjectReader().toString())
+                .setMembers(
+                    ImmutableList.of(
+                        "serviceAccount:storage-python@spec-test-ruby-samples.iam.gserviceaccount.com"))
+                .setCondition(
+                    Condition.newBuilder()
+                        .setTitle("Title")
+                        .setDescription("Description")
+                        .setExpression(
+                            "resource.name.startsWith(\"projects/_/buckets/bucket-name/objects/prefix-a-\")")
+                        .build())
+                .build());
+
+    // Validate getting policy.
+    Policy currentPolicy = storage.getIamPolicy(BUCKET, bucketOptions);
+    assertEquals(bindingsWithoutPublicRead, currentPolicy.getBindingsList());
+
+    // Validate updating policy.
+    List<com.google.cloud.Binding> currentBindings = new ArrayList(currentPolicy.getBindingsList());
+    currentBindings.add(
+        com.google.cloud.Binding.newBuilder()
+            .setRole(StorageRoles.legacyObjectReader().getValue())
+            .addMembers(Identity.allUsers().strValue())
+            .build());
+    Policy updatedPolicy =
+        storage.setIamPolicy(
+            BUCKET, currentPolicy.toBuilder().setBindings(currentBindings).build(), bucketOptions);
+    assertTrue(
+        bindingsWithPublicRead.size() == updatedPolicy.getBindingsList().size()
+            && bindingsWithPublicRead.containsAll(updatedPolicy.getBindingsList()));
+
+    // Remove a member
+    List<com.google.cloud.Binding> updatedBindings = new ArrayList(updatedPolicy.getBindingsList());
+    for (int i = 0; i < updatedBindings.size(); i++) {
+      com.google.cloud.Binding binding = updatedBindings.get(i);
+      if (binding.getRole().equals(StorageRoles.legacyObjectReader().toString())) {
+        List<String> members = new ArrayList(binding.getMembers());
+        members.remove(Identity.allUsers().strValue());
+        updatedBindings.set(i, binding.toBuilder().setMembers(members).build());
+        break;
+      }
+    }
+
+    Policy revertedPolicy =
+        storage.setIamPolicy(
+            BUCKET, updatedPolicy.toBuilder().setBindings(updatedBindings).build(), bucketOptions);
+
+    assertEquals(bindingsWithoutPublicRead, revertedPolicy.getBindingsList());
+    assertTrue(
+        bindingsWithoutPublicRead.size() == revertedPolicy.getBindingsList().size()
+            && bindingsWithoutPublicRead.containsAll(revertedPolicy.getBindingsList()));
+
+    // Add Conditional Policy
+    List<com.google.cloud.Binding> conditionalBindings =
+        new ArrayList(revertedPolicy.getBindingsList());
+    conditionalBindings.add(
+        com.google.cloud.Binding.newBuilder()
+            .setRole(StorageRoles.legacyObjectReader().toString())
+            .addMembers(
+                "serviceAccount:storage-python@spec-test-ruby-samples.iam.gserviceaccount.com")
+            .setCondition(
+                Condition.newBuilder()
+                    .setTitle("Title")
+                    .setDescription("Description")
+                    .setExpression(
+                        "resource.name.startsWith(\"projects/_/buckets/bucket-name/objects/prefix-a-\")")
+                    .build())
+            .build());
+    Policy conditionalPolicy =
+        storage.setIamPolicy(
+            BUCKET,
+            revertedPolicy.toBuilder().setBindings(conditionalBindings).setVersion(3).build(),
+            bucketOptions);
+    assertTrue(
+        bindingsWithConditionalPolicy.size() == conditionalPolicy.getBindingsList().size()
+            && bindingsWithConditionalPolicy.containsAll(conditionalPolicy.getBindingsList()));
+
+    // Remove Conditional Policy
+    conditionalPolicy =
+        storage.setIamPolicy(
+            BUCKET,
+            conditionalPolicy.toBuilder().setBindings(updatedBindings).setVersion(3).build(),
+            bucketOptions);
+
+    // Validate testing permissions.
+    List<Boolean> expectedPermissions = ImmutableList.of(true, true);
+    assertEquals(
+        expectedPermissions,
+        storage.testIamPermissions(
+            BUCKET,
+            ImmutableList.of("storage.buckets.getIamPolicy", "storage.buckets.setIamPolicy"),
+            bucketOptions));
+
+    // Disable Uniform Bucket-Level Access
+    storage.update(
+        BucketInfo.newBuilder(BUCKET)
+            .setIamConfiguration(
+                BucketInfo.IamConfiguration.newBuilder()
+                    .setIsUniformBucketLevelAccessEnabled(false)
+                    .build())
+            .build());
+  }
+
   @Test
   public void testUpdateBucketLabel() {
     Bucket remoteBucket = storage.get(BUCKET, Storage.BucketGetOption.fields(BucketField.ID));