diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java
index f4f85639062b..24b8bfc5f8ef 100644
--- a/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java
+++ b/google-cloud-clients/google-cloud-bigtable-admin/src/main/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClient.java
@@ -19,6 +19,7 @@
import com.google.api.core.ApiFunction;
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
+import com.google.api.resourcenames.ResourceName;
import com.google.bigtable.admin.v2.AppProfileName;
import com.google.bigtable.admin.v2.ClusterName;
import com.google.bigtable.admin.v2.DeleteAppProfileRequest;
@@ -27,6 +28,8 @@
import com.google.bigtable.admin.v2.ListAppProfilesRequest;
import com.google.bigtable.admin.v2.LocationName;
import com.google.bigtable.admin.v2.ProjectName;
+import com.google.cloud.Policy;
+import com.google.cloud.Policy.DefaultMarshaller;
import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListAppProfilesPage;
import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListAppProfilesPagedResponse;
import com.google.cloud.bigtable.admin.v2.models.AppProfile;
@@ -46,8 +49,13 @@
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.UncheckedExecutionException;
+import com.google.iam.v1.GetIamPolicyRequest;
+import com.google.iam.v1.SetIamPolicyRequest;
+import com.google.iam.v1.TestIamPermissionsRequest;
+import com.google.iam.v1.TestIamPermissionsResponse;
import com.google.protobuf.Empty;
import java.io.IOException;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nonnull;
@@ -992,6 +1000,284 @@ public Void apply(Empty input) {
);
}
+ /**
+ * Gets the IAM access control policy for the specified instance.
+ *
+ *
Sample code:
+ *
+ *
{@code
+ * Policy policy = client.getIamPolicy("my-instance");
+ * for(Map.Entry> entry : policy.getBindings().entrySet()) {
+ * System.out.printf("Role: %s Identities: %s\n", entry.getKey(), entry.getValue());
+ * }
+ * }
+ *
+ * @see Instance-level
+ * IAM management
+ */
+ @SuppressWarnings("WeakerAccess")
+ public Policy getIamPolicy(String instanceId) {
+ return awaitFuture(getIamPolicyAsync(instanceId));
+ }
+
+ /**
+ * Asynchronously gets the IAM access control policy for the specified instance.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * ApiFuture policyFuture = client.getIamPolicyAsync("my-instance");
+ *
+ * ApiFutures.addCallback(policyFuture,
+ * new ApiFutureCallback() {
+ * public void onSuccess(Policy policy) {
+ * for (Entry> entry : policy.getBindings().entrySet()) {
+ * System.out.printf("Role: %s Identities: %s\n", entry.getKey(), entry.getValue());
+ * }
+ * }
+ *
+ * public void onFailure(Throwable t) {
+ * t.printStackTrace();
+ * }
+ * },
+ * MoreExecutors.directExecutor());
+ * }
+ *
+ * @see Instance-level IAM management
+ */
+ @SuppressWarnings("WeakerAccess")
+ public ApiFuture getIamPolicyAsync(String instanceId) {
+ InstanceName name = InstanceName.of(projectName.getProject(), instanceId);
+
+ GetIamPolicyRequest request = GetIamPolicyRequest.newBuilder()
+ .setResource(name.toString())
+ .build();
+
+ final IamPolicyMarshaller marshaller = new IamPolicyMarshaller();
+
+ return ApiFutures.transform(
+ stub.getIamPolicyCallable().futureCall(request),
+ new ApiFunction() {
+ @Override
+ public Policy apply(com.google.iam.v1.Policy proto) {
+ return marshaller.fromPb(proto);
+ }
+ },
+ MoreExecutors.directExecutor()
+ );
+ }
+
+ /**
+ * Replaces the IAM policy associated with the specified instance.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * Policy newPolicy = client.setIamPolicy("my-instance",
+ * Policy.newBuilder()
+ * .addIdentity(Role.of("bigtable.user"), Identity.user("someone@example.com"))
+ * .addIdentity(Role.of("bigtable.admin"), Identity.group("admins@example.com"))
+ * .build());
+ * }
+ *
+ * @see Instance-level IAM management
+ */
+ @SuppressWarnings("WeakerAccess")
+ public Policy setIamPolicy(String instanceId, Policy policy) {
+ return awaitFuture(setIamPolicyAsync(instanceId, policy));
+ }
+
+ /**
+ * Asynchronously replaces the IAM policy associated with the specified instance.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * ApiFuture newPolicyFuture = client.setIamPolicyAsync("my-instance",
+ * Policy.newBuilder()
+ * .addIdentity(Role.of("bigtable.user"), Identity.user("someone@example.com"))
+ * .addIdentity(Role.of("bigtable.admin"), Identity.group("admins@example.com"))
+ * .build());
+ *
+ * ApiFutures.addCallback(policyFuture,
+ * new ApiFutureCallback() {
+ * public void onSuccess(Policy policy) {
+ * for (Entry> entry : policy.getBindings().entrySet()) {
+ * System.out.printf("Role: %s Identities: %s\n", entry.getKey(), entry.getValue());
+ * }
+ * }
+ *
+ * public void onFailure(Throwable t) {
+ * t.printStackTrace();
+ * }
+ * },
+ * MoreExecutors.directExecutor());
+ * }
+ *
+ * @see Instance-level IAM management
+ */
+ @SuppressWarnings("WeakerAccess")
+ public ApiFuture setIamPolicyAsync(String instanceId, Policy policy) {
+ InstanceName name = InstanceName.of(projectName.getProject(), instanceId);
+ final IamPolicyMarshaller marshaller = new IamPolicyMarshaller();
+
+ SetIamPolicyRequest request = SetIamPolicyRequest.newBuilder()
+ .setResource(name.toString())
+ .setPolicy(marshaller.toPb(policy))
+ .build();
+
+ return ApiFutures.transform(
+ stub.setIamPolicyCallable().futureCall(request),
+ new ApiFunction() {
+ @Override
+ public Policy apply(com.google.iam.v1.Policy proto) {
+ return marshaller.fromPb(proto);
+ }
+ },
+ MoreExecutors.directExecutor()
+ );
+ }
+
+ /**
+ * Tests whether the caller has the given permissions for the specified instance.
+ * Returns a subset of the specified permissions that the caller has.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * List grantedPermissions = client.testIamPermission("my-instance",
+ * "bigtable.tables.readRows", "bigtable.tables.mutateRows");
+ * }
+ *
+ * System.out.println("Has read access: " + grantedPermissions.contains("bigtable.tables.readRows"));
+ * System.out.println("Has write access: " + grantedPermissions.contains("bigtable.tables.mutateRows"));
+ *
+ * @see Cloud Bigtable permissions
+ */
+ @SuppressWarnings("WeakerAccess")
+ public List testIamPermission(String instanceId, String... permissions) {
+ return testIamPermission(InstanceName.of(projectName.getProject(), instanceId), permissions);
+ }
+
+ /**
+ * Asynchronously tests whether the caller has the given permissions for the specified instance.
+ * Returns a subset of the specified permissions that the caller has.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * ApiFuture> grantedPermissionsFuture = client.testIamPermission("my-instance",
+ * "bigtable.tables.readRows", "bigtable.tables.mutateRows");
+ *
+ * ApiFutures.addCallback(grantedPermissionsFuture,
+ * new ApiFutureCallback>() {
+ * public void onSuccess(List grantedPermissions) {
+ * System.out.println("Has read access: " + grantedPermissions.contains("bigtable.tables.readRows"));
+ * System.out.println("Has write access: " + grantedPermissions.contains("bigtable.tables.mutateRows"));
+ * }
+ *
+ * public void onFailure(Throwable t) {
+ * t.printStackTrace();
+ * }
+ * },
+ * MoreExecutors.directExecutor());
+ * }
+ *
+ * @see Cloud Bigtable permissions
+ */
+ @SuppressWarnings("WeakerAccess")
+ public ApiFuture> testIamPermissionAsync(String instanceId, String... permissions) {
+ return testIamPermissionAsync(InstanceName.of(projectName.getProject(), instanceId), permissions);
+ }
+
+ /**
+ * Tests whether the caller has the given permissions for the specified absolute resource name
+ * (note that the current project of the client is ignored).
+ *
+ * Returns a subset of the specified permissions that the caller has.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * List grantedPermissions = client.testIamPermission(
+ * TableName.of("my-project", "my-instance", "my-table"),
+ * "bigtable.tables.readRows", "bigtable.tables.mutateRows");
+ *
+ * System.out.println("Has read access: " + grantedPermissions.contains("bigtable.tables.readRows"));
+ * System.out.println("Has write access: " + grantedPermissions.contains("bigtable.tables.mutateRows"));
+ * }
+ *
+ * @see Cloud Bigtable permissions
+ */
+ @SuppressWarnings("WeakerAccess")
+ public List testIamPermission(ResourceName resourceName, String... permissions) {
+ return awaitFuture(testIamPermissionAsync(resourceName, permissions));
+ }
+
+
+ /**
+ * Asynchronously tests whether the caller has the given permissions for the the specified
+ * absolute resource name (note that the current project of the client is ignored).
+ * Returns a subset of the specified permissions that the caller has.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * ApiFuture> grantedPermissionsFuture = client.testIamPermission(
+ * TableName.of("my-project", "my-instance", "my-table"),
+ * "bigtable.tables.readRows", "bigtable.tables.mutateRows");
+ *
+ * ApiFutures.addCallback(grantedPermissionsFuture,
+ * new ApiFutureCallback>() {
+ * public void onSuccess(List grantedPermissions) {
+ * System.out.println("Has read access: " + grantedPermissions.contains("bigtable.tables.readRows"));
+ * System.out.println("Has write access: " + grantedPermissions.contains("bigtable.tables.mutateRows"));
+ * }
+ *
+ * public void onFailure(Throwable t) {
+ * t.printStackTrace();
+ * }
+ * },
+ * MoreExecutors.directExecutor());
+ * }
+ *
+ * @see Cloud Bigtable permissions
+ */
+ @SuppressWarnings("WeakerAccess")
+ public ApiFuture> testIamPermissionAsync(ResourceName resourceName, String... permissions) {
+ TestIamPermissionsRequest request = TestIamPermissionsRequest.newBuilder()
+ .setResource(resourceName.toString())
+ .addAllPermissions(Arrays.asList(permissions))
+ .build();
+
+ return ApiFutures.transform(
+ stub.testIamPermissionsCallable().futureCall(request),
+ new ApiFunction>() {
+ @Override
+ public List apply(TestIamPermissionsResponse input) {
+ return input.getPermissionsList();
+ }
+ },
+ MoreExecutors.directExecutor()
+ );
+ }
+
+ /**
+ * Simple adapter to expose {@link DefaultMarshaller} to this class. It enables this client to
+ * convert to/from IAM wrappers and protobufs.
+ */
+ private static class IamPolicyMarshaller extends DefaultMarshaller {
+ @Override
+ public Policy fromPb(com.google.iam.v1.Policy policyPb) {
+ return super.fromPb(policyPb);
+ }
+
+ @Override
+ public com.google.iam.v1.Policy toPb(Policy policy) {
+ return super.toPb(policy);
+ }
+ }
+
/**
* Awaits the result of a future, taking care to propagate errors while maintaining the call site
* in a suppressed exception. This allows semantic errors to be caught across threads, while
diff --git a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java
index 487191f19768..afee494a1449 100644
--- a/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java
+++ b/google-cloud-clients/google-cloud-bigtable-admin/src/test/java/com/google/cloud/bigtable/admin/v2/BigtableInstanceAdminClientTest.java
@@ -36,6 +36,9 @@
import com.google.bigtable.admin.v2.StorageType;
import com.google.bigtable.admin.v2.UpdateClusterMetadata;
import com.google.bigtable.admin.v2.UpdateInstanceMetadata;
+import com.google.cloud.Identity;
+import com.google.cloud.Policy;
+import com.google.cloud.Role;
import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListAppProfilesPage;
import com.google.cloud.bigtable.admin.v2.BaseBigtableInstanceAdminClient.ListAppProfilesPagedResponse;
import com.google.cloud.bigtable.admin.v2.models.AppProfile;
@@ -51,6 +54,8 @@
import com.google.cloud.bigtable.admin.v2.models.UpdateInstanceRequest;
import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub;
import com.google.common.collect.Lists;
+import com.google.common.io.BaseEncoding;
+import com.google.protobuf.ByteString;
import com.google.protobuf.Empty;
import com.google.protobuf.FieldMask;
import io.grpc.Status.Code;
@@ -114,6 +119,12 @@ public class BigtableInstanceAdminClientTest {
@Mock
private UnaryCallable mockDeleteAppProfileCallable;
+ @Mock
+ private UnaryCallable mockGetIamPolicyCallable;
+ @Mock
+ private UnaryCallable mockSetIamPolicyCallable;
+ @Mock
+ private UnaryCallable mockTestIamPermissionsCallable;
@Before
public void setUp() {
@@ -139,6 +150,10 @@ public void setUp() {
Mockito.when(mockStub.updateAppProfileOperationCallable())
.thenReturn(mockUpdateAppProfileCallable);
Mockito.when(mockStub.deleteAppProfileCallable()).thenReturn(mockDeleteAppProfileCallable);
+
+ Mockito.when(mockStub.getIamPolicyCallable()).thenReturn(mockGetIamPolicyCallable);
+ Mockito.when(mockStub.setIamPolicyCallable()).thenReturn(mockSetIamPolicyCallable);
+ Mockito.when(mockStub.testIamPermissionsCallable()).thenReturn(mockTestIamPermissionsCallable);
}
@Test
@@ -726,4 +741,106 @@ private void mockOperationResult(
.immediateOperationFuture(operationSnapshot);
Mockito.when(callable.futureCall(request)).thenReturn(operationFuture);
}
+
+ @Test
+ public void testGetIamPolicy() {
+ // Setup
+ com.google.iam.v1.GetIamPolicyRequest expectedRequest =
+ com.google.iam.v1.GetIamPolicyRequest.newBuilder()
+ .setResource(INSTANCE_NAME.toString())
+ .build();
+
+ com.google.iam.v1.Policy expectedResponse =
+ com.google.iam.v1.Policy.newBuilder()
+ .addBindings(
+ com.google.iam.v1.Binding.newBuilder()
+ .setRole("roles/bigtable.user")
+ .addMembers("user:someone@example.com")
+ )
+ .setEtag(ByteString.copyFromUtf8("my-etag"))
+ .build();
+
+ Mockito.when(mockGetIamPolicyCallable.futureCall(expectedRequest))
+ .thenReturn(ApiFutures.immediateFuture(expectedResponse));
+
+ // Execute
+ Policy actualResult = adminClient.getIamPolicy(INSTANCE_NAME.getInstance());
+
+ // Verify
+ assertThat(actualResult).isEqualTo(
+ Policy.newBuilder()
+ .addIdentity(Role.of("bigtable.user"), Identity.user("someone@example.com"))
+ .setEtag(BaseEncoding.base64().encode("my-etag".getBytes()))
+ .build()
+ );
+ }
+
+ @Test
+ public void testSetIamPolicy() {
+ // Setup
+ com.google.iam.v1.SetIamPolicyRequest expectedRequest =
+ com.google.iam.v1.SetIamPolicyRequest.newBuilder()
+ .setResource(INSTANCE_NAME.toString())
+ .setPolicy(
+ com.google.iam.v1.Policy.newBuilder()
+ .addBindings(
+ com.google.iam.v1.Binding.newBuilder()
+ .setRole("roles/bigtable.user")
+ .addMembers("user:someone@example.com")
+ )
+ )
+ .build();
+
+ com.google.iam.v1.Policy expectedResponse =
+ com.google.iam.v1.Policy.newBuilder()
+ .addBindings(
+ com.google.iam.v1.Binding.newBuilder()
+ .setRole("roles/bigtable.user")
+ .addMembers("user:someone@example.com")
+ )
+ .setEtag(ByteString.copyFromUtf8("my-etag"))
+ .build();
+
+ Mockito.when(mockSetIamPolicyCallable.futureCall(expectedRequest))
+ .thenReturn(ApiFutures.immediateFuture(expectedResponse));
+
+ // Execute
+ Policy actualResult = adminClient.setIamPolicy(INSTANCE_NAME.getInstance(),
+ Policy.newBuilder()
+ .addIdentity(Role.of("bigtable.user"), Identity.user("someone@example.com"))
+ .build());
+
+ // Verify
+ assertThat(actualResult).isEqualTo(
+ Policy.newBuilder()
+ .addIdentity(Role.of("bigtable.user"), Identity.user("someone@example.com"))
+ .setEtag(BaseEncoding.base64().encode("my-etag".getBytes()))
+ .build()
+ );
+ }
+
+ @Test
+ public void testTestIamPermissions() {
+ // Setup
+ com.google.iam.v1.TestIamPermissionsRequest expectedRequest =
+ com.google.iam.v1.TestIamPermissionsRequest.newBuilder()
+ .setResource(INSTANCE_NAME.toString())
+ .addPermissions("bigtable.tables.readRows")
+ .build();
+
+ com.google.iam.v1.TestIamPermissionsResponse expectedResponse =
+ com.google.iam.v1.TestIamPermissionsResponse.newBuilder()
+ .addPermissions("bigtable.tables.readRows")
+ .build();
+
+ Mockito.when(mockTestIamPermissionsCallable.futureCall(expectedRequest))
+ .thenReturn(ApiFutures.immediateFuture(expectedResponse));
+
+ // Execute
+ List actualResult = adminClient.testIamPermission(INSTANCE_NAME.getInstance(),
+ "bigtable.tables.readRows");
+
+ // Verify
+ assertThat(actualResult).containsExactly("bigtable.tables.readRows");
+ }
}