listAcls(BlobId blob);
+
+ /**
+ * Gets the IAM policy for the provided bucket.
+ *
+ * Example of getting the IAM policy for a bucket.
+ *
{@code
+ * String bucketName = "my_unique_bucket";
+ * Policy policy = storage.getPolicy(bucketName);
+ * }
+ *
+ * @throws StorageException upon failure
+ */
+ Policy getPolicy(String bucket);
+
+ /**
+ * Updates the IAM policy on the specified bucket.
+ *
+ * Example of updating the IAM policy on a bucket.
+ *
{@code
+ * // We want to make all objects in our bucket publicly readable.
+ * String bucketName = "my_unique_bucket";
+ * Policy currentPolicy = storage.getPolicy(bucketName);
+ * Policy updatedPolicy =
+ * storage.updatePolicy(
+ * bucketName,
+ * currentPolicy.toBuilder()
+ * .addIdentity(StorageRoles.objectViewer(), Identity.allUsers())
+ * .build());
+ * }
+ *
+ * @throws StorageException upon failure
+ */
+ Policy updatePolicy(String bucket, Policy policy);
+
+ /**
+ * Tests whether the caller holds the permissions on the specified bucket. Returns a list of
+ * booleans in the same placement and order in which the permissions were specified.
+ *
+ * Example of testing permissions on a bucket.
+ *
{@code
+ * String bucketName = "my_unique_bucket";
+ * List response =
+ * storage.testPermissions(
+ * bucket,
+ * ImmutableList.of("storage.buckets.get", "storage.buckets.getIamPolicy"));
+ * for (boolean hasPermission : response) {
+ * // Do something with permission test response
+ * }
+ * }
+ *
+ * @throws StorageException upon failure
+ */
+ List testPermissions(String bucket, List permissions);
}
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
index c2ec81e683e9..30a41fd81185 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
@@ -17,6 +17,8 @@
package com.google.cloud.storage;
import static com.google.cloud.RetryHelper.runWithRetries;
+import static com.google.cloud.storage.PolicyHelper.convertFromApiPolicy;
+import static com.google.cloud.storage.PolicyHelper.convertToApiPolicy;
import static com.google.cloud.storage.spi.v1.StorageRpc.Option.DELIMITER;
import static com.google.cloud.storage.spi.v1.StorageRpc.Option.IF_GENERATION_MATCH;
import static com.google.cloud.storage.spi.v1.StorageRpc.Option.IF_GENERATION_NOT_MATCH;
@@ -31,15 +33,32 @@
import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
import com.google.api.services.storage.model.BucketAccessControl;
import com.google.api.services.storage.model.ObjectAccessControl;
import com.google.api.services.storage.model.StorageObject;
+import com.google.api.services.storage.model.TestIamPermissionsResponse;
import com.google.auth.ServiceAccountSigner;
import com.google.cloud.BaseService;
import com.google.cloud.BatchResult;
import com.google.cloud.Page;
import com.google.cloud.PageImpl;
import com.google.cloud.PageImpl.NextPageFetcher;
+import com.google.cloud.Policy;
import com.google.cloud.ReadChannel;
import com.google.cloud.RetryHelper.RetryHelperException;
import com.google.cloud.storage.Acl.Entity;
@@ -49,6 +68,7 @@
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -57,20 +77,6 @@
import com.google.common.net.UrlEscapers;
import com.google.common.primitives.Ints;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-
final class StorageImpl extends BaseService implements Storage {
private static final byte[] EMPTY_BYTE_ARRAY = {};
@@ -854,6 +860,58 @@ public List call() {
throw StorageException.translateAndThrow(e);
}
}
+
+ @Override
+ public Policy getPolicy(final String bucket) {
+ try {
+ return convertFromApiPolicy(runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.storage.model.Policy call() {
+ return storageRpc.getPolicy(bucket);
+ }
+ }, getOptions().getRetrySettings(), EXCEPTION_HANDLER, getOptions().getClock()));
+ } catch (RetryHelperException e){
+ throw StorageException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Policy updatePolicy(final String bucket, final Policy policy) {
+ try {
+ return convertFromApiPolicy(runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.storage.model.Policy call() {
+ return storageRpc.updatePolicy(bucket, convertToApiPolicy(policy));
+ }
+ }, getOptions().getRetrySettings(), EXCEPTION_HANDLER, getOptions().getClock()));
+ } catch (RetryHelperException e) {
+ throw StorageException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public List testPermissions(final String bucket, final List permissions) {
+ try {
+ TestIamPermissionsResponse response = runWithRetries(new Callable() {
+ @Override
+ public TestIamPermissionsResponse call() {
+ return storageRpc.testPermissions(bucket, permissions);
+ }
+ }, getOptions().getRetrySettings(), EXCEPTION_HANDLER, getOptions().getClock());
+ final Set heldPermissions =
+ response.getPermissions() != null
+ ? ImmutableSet.copyOf(response.getPermissions())
+ : ImmutableSet.of();
+ return Lists.transform(permissions, new Function() {
+ @Override
+ public Boolean apply(String permission) {
+ return heldPermissions.contains(permission);
+ }
+ });
+ } catch (RetryHelperException e) {
+ throw StorageException.translateAndThrow(e);
+ }
+ }
private static void addToOptionMap(StorageRpc.Option option, T defaultValue,
Map map) {
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 7d11be03b25a..15981d0ac075 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
@@ -48,7 +48,9 @@
import com.google.api.services.storage.model.ComposeRequest.SourceObjects.ObjectPreconditions;
import com.google.api.services.storage.model.ObjectAccessControl;
import com.google.api.services.storage.model.Objects;
+import com.google.api.services.storage.model.Policy;
import com.google.api.services.storage.model.StorageObject;
+import com.google.api.services.storage.model.TestIamPermissionsResponse;
import com.google.cloud.BaseServiceException;
import com.google.cloud.HttpTransportOptions;
import com.google.cloud.storage.StorageException;
@@ -834,4 +836,31 @@ public List listAcls(String bucket, String object, Long gen
throw translate(ex);
}
}
+
+ @Override
+ public Policy getPolicy(String bucket) {
+ try {
+ return storage.buckets().getIamPolicy(bucket).execute();
+ } catch (IOException ex) {
+ throw translate(ex);
+ }
+ }
+
+ @Override
+ public Policy updatePolicy(String bucket, Policy policy) {
+ try {
+ return storage.buckets().setIamPolicy(bucket, policy).execute();
+ } catch (IOException ex) {
+ throw translate(ex);
+ }
+ }
+
+ @Override
+ public TestIamPermissionsResponse testPermissions(String bucket, List permissions) {
+ try {
+ return storage.buckets().testIamPermissions(bucket, permissions).execute();
+ } catch (IOException ex) {
+ 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 91b9601181ea..31a1ca4896aa 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
@@ -19,7 +19,9 @@
import com.google.api.services.storage.model.Bucket;
import com.google.api.services.storage.model.BucketAccessControl;
import com.google.api.services.storage.model.ObjectAccessControl;
+import com.google.api.services.storage.model.Policy;
import com.google.api.services.storage.model.StorageObject;
+import com.google.api.services.storage.model.TestIamPermissionsResponse;
import com.google.cloud.ServiceRpc;
import com.google.cloud.storage.StorageException;
@@ -427,4 +429,25 @@ void write(String uploadId, byte[] toWrite, int toWriteOffset, long destOffset,
* @throws StorageException upon failure
*/
List listAcls(String bucket, String object, Long generation);
+
+ /**
+ * Returns the IAM policy for the specified bucket.
+ *
+ * @throws StorageException upon failure
+ */
+ Policy getPolicy(String bucket);
+
+ /**
+ * Updates the IAM policy for the specified bucket.
+ *
+ * @throws StorageException upon failure
+ */
+ Policy updatePolicy(String bucket, Policy policy);
+
+ /**
+ * Tests whether the caller holds the specified permissions for the specified bucket.
+ *
+ * @throws StorageException upon failure
+ */
+ TestIamPermissionsResponse testPermissions(String bucket, List permissions);
}
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
new file mode 100644
index 000000000000..70b66f7b19d5
--- /dev/null
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/PolicyHelperTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.google.api.services.storage.model.Policy.Bindings;
+import com.google.cloud.Identity;
+import com.google.cloud.Policy;
+import com.google.cloud.storage.testing.ApiPolicyMatcher;
+import com.google.common.collect.ImmutableList;
+
+public class PolicyHelperTest {
+
+ private static final String ETAG = "CAE=";
+
+ @Test
+ public void testEquivalence() {
+ Policy libPolicy =
+ Policy.newBuilder()
+ .addIdentity(StorageRoles.objectViewer(), Identity.allUsers())
+ .addIdentity(
+ StorageRoles.objectAdmin(),
+ Identity.user("test1@gmail.com"),
+ Identity.user("test2@gmail.com"))
+ .setEtag(ETAG)
+ .build();
+ com.google.api.services.storage.model.Policy apiPolicy =
+ new com.google.api.services.storage.model.Policy()
+ .setBindings(ImmutableList.of(
+ new Bindings()
+ .setMembers(ImmutableList.of("allUsers"))
+ .setRole("roles/storage.objectViewer"),
+ new Bindings()
+ .setMembers(
+ ImmutableList.of(
+ "user:test1@gmail.com",
+ "user:test2@gmail.com"))
+ .setRole("roles/storage.objectAdmin")))
+ .setEtag(ETAG);
+
+ Policy actualLibPolicy = PolicyHelper.convertFromApiPolicy(apiPolicy);
+ com.google.api.services.storage.model.Policy actualApiPolicy =
+ PolicyHelper.convertToApiPolicy(libPolicy);
+
+ assertEquals(libPolicy, actualLibPolicy);
+ assertTrue(new ApiPolicyMatcher(apiPolicy).matches(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 397a167503ee..1b72623ee92a 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
@@ -16,6 +16,9 @@
package com.google.cloud.storage;
+import static com.google.cloud.storage.testing.ApiPolicyMatcher.eqApiPolicy;
+import static org.easymock.EasyMock.cmp;
+import static org.easymock.EasyMock.eq;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -25,11 +28,49 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.easymock.LogicalOperator;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.gax.core.ApiClock;
+import com.google.api.services.storage.model.Policy.Bindings;
import com.google.api.services.storage.model.StorageObject;
+import com.google.api.services.storage.model.TestIamPermissionsResponse;
import com.google.auth.oauth2.ServiceAccountCredentials;
+import com.google.cloud.Identity;
import com.google.cloud.Page;
+import com.google.cloud.Policy;
import com.google.cloud.ReadChannel;
import com.google.api.gax.core.RetrySettings;
import com.google.cloud.ServiceOptions;
@@ -53,39 +94,6 @@
import com.google.common.io.BaseEncoding;
import com.google.common.net.UrlEscapers;
-import org.easymock.Capture;
-import org.easymock.EasyMock;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.nio.ByteBuffer;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.spec.EncodedKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import javax.crypto.spec.SecretKeySpec;
-
public class StorageImplTest {
private static final String BUCKET_NAME1 = "b1";
@@ -243,6 +251,33 @@ public class StorageImplTest {
private static final Map ENCRYPTION_KEY_OPTIONS =
ImmutableMap.of(StorageRpc.Option.CUSTOMER_SUPPLIED_KEY, BASE64_KEY);
+ // IAM policies
+ private static final String POLICY_ETAG1 = "CAE=";
+ private static final String POLICY_ETAG2 = "CAI=";
+ private static final Policy LIB_POLICY1 =
+ Policy.newBuilder()
+ .addIdentity(StorageRoles.objectViewer(), Identity.allUsers())
+ .addIdentity(
+ StorageRoles.objectAdmin(),
+ Identity.user("test1@gmail.com"),
+ Identity.user("test2@gmail.com"))
+ .setEtag(POLICY_ETAG1)
+ .build();
+
+ private static final com.google.api.services.storage.model.Policy API_POLICY1 =
+ new com.google.api.services.storage.model.Policy()
+ .setBindings(ImmutableList.of(
+ new Bindings()
+ .setMembers(ImmutableList.of("allUsers"))
+ .setRole("roles/storage.objectViewer"),
+ new Bindings()
+ .setMembers(
+ ImmutableList.of(
+ "user:test1@gmail.com",
+ "user:test2@gmail.com"))
+ .setRole("roles/storage.objectAdmin")))
+ .setEtag(POLICY_ETAG1);
+
private static final String PRIVATE_KEY_STRING =
"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoG"
+ "BAL2xolH1zrISQ8+GzOV29BNjjzq4/HIP8Psd1+cZb81vDklSF+95wB250MSE0BDc81pvIMwj5OmIfLg1NY6uB"
@@ -1947,6 +1982,106 @@ public void testListBlobAcl() {
assertEquals(ImmutableList.of(ACL, OTHER_ACL), acls);
}
+ @Test
+ public void testGetPolicy() {
+ EasyMock.expect(storageRpcMock.getPolicy(BUCKET_NAME1)).andReturn(API_POLICY1);
+ EasyMock.replay(storageRpcMock);
+ initializeService();
+ assertEquals(LIB_POLICY1, storage.getPolicy(BUCKET_NAME1));
+ }
+
+ @Test
+ public void testUpdatePolicy() {
+ com.google.api.services.storage.model.Policy preCommitApiPolicy =
+ new com.google.api.services.storage.model.Policy()
+ .setBindings(ImmutableList.of(
+ new Bindings()
+ .setMembers(ImmutableList.of("allUsers"))
+ .setRole("roles/storage.objectViewer"),
+ new Bindings()
+ .setMembers(
+ ImmutableList.of(
+ "user:test1@gmail.com",
+ "user:test2@gmail.com"))
+ .setRole("roles/storage.objectAdmin"),
+ new Bindings()
+ .setMembers(ImmutableList.of("group:test-group@gmail.com"))
+ .setRole("roles/storage.admin")))
+ .setEtag(POLICY_ETAG1);
+ // 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()
+ .setBindings(ImmutableList.of(
+ new Bindings()
+ .setMembers(ImmutableList.of("allUsers"))
+ .setRole("roles/storage.objectViewer"),
+ new Bindings()
+ .setMembers(
+ ImmutableList.of(
+ "user:test1@gmail.com",
+ "user:test2@gmail.com"))
+ .setRole("roles/storage.objectAdmin"),
+ new Bindings()
+ .setMembers(ImmutableList.of("group:test-group@gmail.com"))
+ .setRole("roles/storage.admin")))
+ .setEtag(POLICY_ETAG2);
+ Policy postCommitLibPolicy =
+ Policy.newBuilder()
+ .addIdentity(StorageRoles.objectViewer(), Identity.allUsers())
+ .addIdentity(
+ StorageRoles.objectAdmin(),
+ Identity.user("test1@gmail.com"),
+ Identity.user("test2@gmail.com"))
+ .addIdentity(StorageRoles.admin(), Identity.group("test-group@gmail.com"))
+ .setEtag(POLICY_ETAG2)
+ .build();
+
+ EasyMock.expect(storageRpcMock.getPolicy(BUCKET_NAME1)).andReturn(API_POLICY1);
+ EasyMock.expect(
+ storageRpcMock.updatePolicy(
+ eq(BUCKET_NAME1),
+ eqApiPolicy(preCommitApiPolicy)))
+ .andReturn(postCommitApiPolicy);
+ EasyMock.replay(storageRpcMock);
+ initializeService();
+
+ Policy currentPolicy = storage.getPolicy(BUCKET_NAME1);
+ Policy updatedPolicy =
+ storage.updatePolicy(
+ BUCKET_NAME1,
+ currentPolicy.toBuilder()
+ .addIdentity(StorageRoles.admin(), Identity.group("test-group@gmail.com"))
+ .build());
+ assertEquals(updatedPolicy, postCommitLibPolicy);
+ }
+
+ @Test
+ public void testTestPermissionsNull() {
+ ImmutableList expectedPermissions = ImmutableList.of(false, false, false);
+ ImmutableList checkedPermissions =
+ ImmutableList.of("storage.buckets.get", "storage.buckets.getIamPolicy", "storage.objects.list");
+
+ EasyMock.expect(storageRpcMock.testPermissions(BUCKET_NAME1, checkedPermissions))
+ .andReturn(new TestIamPermissionsResponse());
+ EasyMock.replay(storageRpcMock);
+ initializeService();
+ assertEquals(expectedPermissions, storage.testPermissions(BUCKET_NAME1, checkedPermissions));
+ }
+
+ @Test
+ public void testTestPermissionsNonNull() {
+ ImmutableList expectedPermissions = ImmutableList.of(true, false, true);
+ ImmutableList checkedPermissions =
+ ImmutableList.of("storage.buckets.get", "storage.buckets.getIamPolicy", "storage.objects.list");
+
+ EasyMock.expect(storageRpcMock.testPermissions(BUCKET_NAME1, checkedPermissions))
+ .andReturn(new TestIamPermissionsResponse()
+ .setPermissions(ImmutableList.of("storage.objects.list", "storage.buckets.get")));
+ EasyMock.replay(storageRpcMock);
+ initializeService();
+ assertEquals(expectedPermissions, storage.testPermissions(BUCKET_NAME1, checkedPermissions));
+ }
+
@Test
public void testRetryableException() {
BlobId blob = BlobId.of(BUCKET_NAME1, BLOB_NAME1);
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 48ae816e8c3d..9215058595a5 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
@@ -16,6 +16,7 @@
package com.google.cloud.storage.it;
+import static com.google.common.collect.Sets.newHashSet;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -25,7 +26,38 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.ByteBuffer;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.GZIPInputStream;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.cloud.Identity;
import com.google.cloud.Page;
+import com.google.cloud.Policy;
import com.google.cloud.ReadChannel;
import com.google.cloud.RestorableState;
import com.google.cloud.WriteChannel;
@@ -46,6 +78,7 @@
import com.google.cloud.storage.StorageBatchResult;
import com.google.cloud.storage.StorageClass;
import com.google.cloud.storage.StorageException;
+import com.google.cloud.storage.StorageRoles;
import com.google.cloud.storage.testing.RemoteStorageHelper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -56,37 +89,9 @@
import com.google.common.io.BaseEncoding;
import com.google.common.io.ByteStreams;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.nio.ByteBuffer;
-import java.security.Key;
-import java.security.NoSuchAlgorithmException;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.zip.GZIPInputStream;
-
-import javax.crypto.spec.SecretKeySpec;
-
public class ITStorageTest {
+ private static RemoteStorageHelper remoteStorageHelper;
private static Storage storage;
private static final Logger log = Logger.getLogger(ITStorageTest.class.getName());
@@ -104,8 +109,8 @@ public class ITStorageTest {
@BeforeClass
public static void beforeClass() throws NoSuchAlgorithmException, InvalidKeySpecException {
- RemoteStorageHelper helper = RemoteStorageHelper.create();
- storage = helper.getOptions().getService();
+ remoteStorageHelper = RemoteStorageHelper.create();
+ storage = remoteStorageHelper.getOptions().getService();
storage.create(BucketInfo.of(BUCKET));
}
@@ -1440,4 +1445,49 @@ public void testReadCompressedBlob() throws IOException {
}
blob.delete();
}
+
+ @Test
+ public void testBucketPolicy() {
+ String projectId = remoteStorageHelper.getOptions().getProjectId();
+ Identity projectOwner = Identity.projectOwner(projectId);
+ Identity projectEditor = Identity.projectEditor(projectId);
+ Identity projectViewer = Identity.projectViewer(projectId);
+ Map> bindingsWithoutPublicRead =
+ ImmutableMap.of(
+ StorageRoles.legacyBucketOwner(), (Set) newHashSet(projectOwner, projectEditor),
+ StorageRoles.legacyBucketReader(), newHashSet(projectViewer));
+ Map> bindingsWithPublicRead =
+ ImmutableMap.of(
+ StorageRoles.legacyBucketOwner(), (Set) newHashSet(projectOwner, projectEditor),
+ StorageRoles.legacyBucketReader(), newHashSet(projectViewer),
+ StorageRoles.legacyObjectReader(), newHashSet(Identity.allUsers()));
+
+ // Validate getting policy.
+ Policy currentPolicy = storage.getPolicy(BUCKET);
+ assertEquals(bindingsWithoutPublicRead, currentPolicy.getBindings());
+
+ // Validate updating policy.
+ Policy updatedPolicy =
+ storage.updatePolicy(
+ BUCKET,
+ currentPolicy.toBuilder()
+ .addIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
+ .build());
+ assertEquals(bindingsWithPublicRead, updatedPolicy.getBindings());
+ Policy revertedPolicy =
+ storage.updatePolicy(
+ BUCKET,
+ updatedPolicy.toBuilder()
+ .removeIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
+ .build());
+ assertEquals(bindingsWithoutPublicRead, revertedPolicy.getBindings());
+
+ // Validate testing permissions.
+ List expectedPermissions = ImmutableList.of(true, true);
+ assertEquals(
+ expectedPermissions,
+ storage.testPermissions(
+ BUCKET,
+ ImmutableList.of("storage.buckets.getIamPolicy", "storage.buckets.setIamPolicy")));
+ }
}
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/testing/ApiPolicyMatcher.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/testing/ApiPolicyMatcher.java
new file mode 100644
index 000000000000..501b61b4294e
--- /dev/null
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/testing/ApiPolicyMatcher.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * 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.testing;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.easymock.EasyMock;
+import org.easymock.IArgumentMatcher;
+
+import com.google.api.services.storage.model.Policy;
+import com.google.api.services.storage.model.Policy.Bindings;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Matches two {@link Policy} instances, which may have lists of {@link Bindings} that are not in
+ * the same order but which are still logically equivalent.
+ */
+public class ApiPolicyMatcher implements IArgumentMatcher {
+
+ private final Map> expectedBindings;
+ private final String expectedEtag;
+
+ public ApiPolicyMatcher(Policy expected) {
+ expectedBindings = toMap(expected.getBindings());
+ expectedEtag = expected.getEtag();
+ }
+
+ @Override
+ public boolean matches(Object object) {
+ if (!(object instanceof Policy)) {
+ return false;
+ }
+ Policy actual = (Policy) object;
+ Map> actualBindings = toMap(actual.getBindings());
+ String actualEtag = actual.getEtag();
+
+ if (expectedEtag == null) {
+ if (actualEtag != null) {
+ return false;
+ }
+ } else {
+ if (!expectedEtag.equals(actual.getEtag())) {
+ return false;
+ }
+ }
+
+ if (expectedBindings.size() != actualBindings.size()) {
+ return false;
+ }
+
+ for (Map.Entry> entry : expectedBindings.entrySet()) {
+ String role = entry.getKey();
+ Set expectedMembers = entry.getValue();
+ Set actualMembers = actualBindings.get(role);
+ if (!expectedMembers.equals(actualMembers)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void appendTo(StringBuffer buffer) {
+ buffer.append("eqApiPolicy(");
+ buffer.append("etag=");
+ buffer.append(expectedEtag);
+ buffer.append(",bindings=");
+ buffer.append(expectedBindings);
+ buffer.append(")");
+ }
+
+ public static Policy eqApiPolicy(Policy in) {
+ EasyMock.reportMatcher(new ApiPolicyMatcher(in));
+ return null;
+ }
+
+ private Map> toMap(List bindings) {
+ Map> mapBindings = new HashMap<>();
+ if (bindings != null) {
+ for (Bindings binding : bindings) {
+ mapBindings.put(binding.getRole(), ImmutableSet.copyOf(binding.getMembers()));
+ }
+ }
+ return mapBindings;
+ }
+}
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/testing/ApiPolicyMatcherTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/testing/ApiPolicyMatcherTest.java
new file mode 100644
index 000000000000..b1b6856ed1f4
--- /dev/null
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/testing/ApiPolicyMatcherTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * 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.testing;
+
+import static com.google.cloud.storage.testing.ApiPolicyMatcher.eqApiPolicy;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import com.google.api.services.storage.model.Policy;
+import com.google.api.services.storage.model.Policy.Bindings;
+import com.google.common.collect.ImmutableList;
+
+public class ApiPolicyMatcherTest {
+
+ private static interface PolicyAcceptor {
+ int accept(Policy policy);
+ }
+
+ private static final String ETAG = "CAE=";
+ private static final Policy API_POLICY_1 =
+ new Policy()
+ .setBindings(ImmutableList.of(
+ new Bindings()
+ .setMembers(ImmutableList.of("allUsers"))
+ .setRole("roles/storage.objectViewer"),
+ new Bindings()
+ .setMembers(
+ ImmutableList.of(
+ "user:test1@gmail.com",
+ "user:test2@gmail.com"))
+ .setRole("roles/storage.objectAdmin"),
+ new Bindings()
+ .setMembers(ImmutableList.of("group:test-group@gmail.com"))
+ .setRole("roles/storage.admin")))
+ .setEtag(ETAG);
+ private static final Policy API_POLICY_2 =
+ new Policy()
+ .setBindings(ImmutableList.of(
+ new Bindings()
+ .setMembers(ImmutableList.of("group:test-group@gmail.com"))
+ .setRole("roles/storage.admin"),
+ new Bindings()
+ .setMembers(ImmutableList.of("allUsers"))
+ .setRole("roles/storage.objectViewer"),
+ new Bindings()
+ .setMembers(
+ ImmutableList.of(
+ "user:test2@gmail.com",
+ "user:test1@gmail.com"))
+ .setRole("roles/storage.objectAdmin")))
+ .setEtag(ETAG);
+ private static final Policy API_POLICY_MISSING_BINDINGS =
+ new Policy().setEtag(ETAG);
+ private static final Policy API_POLICY_MISSING_ETAG =
+ new Policy()
+ .setBindings(ImmutableList.of(
+ new Bindings()
+ .setMembers(ImmutableList.of("group:test-group@gmail.com"))
+ .setRole("roles/storage.admin"),
+ new Bindings()
+ .setMembers(ImmutableList.of("allUsers"))
+ .setRole("roles/storage.objectViewer"),
+ new Bindings()
+ .setMembers(
+ ImmutableList.of(
+ "user:test2@gmail.com",
+ "user:test1@gmail.com"))
+ .setRole("roles/storage.objectAdmin")));
+
+ @Test
+ public void testEquivalence() {
+ assertMatch(API_POLICY_1, API_POLICY_2);
+ assertMatch(API_POLICY_2, API_POLICY_1);
+ assertNoMatch(API_POLICY_1, API_POLICY_MISSING_BINDINGS);
+ assertNoMatch(API_POLICY_MISSING_BINDINGS, API_POLICY_1);
+ assertNoMatch(API_POLICY_1, API_POLICY_MISSING_ETAG);
+ assertNoMatch(API_POLICY_MISSING_ETAG, API_POLICY_1);
+ assertNoMatch(API_POLICY_MISSING_BINDINGS, API_POLICY_MISSING_ETAG);
+ assertNoMatch(API_POLICY_MISSING_ETAG, API_POLICY_MISSING_BINDINGS);
+ }
+
+ private static void assertMatch(Policy expected, Policy actual) {
+ assertTrue(new ApiPolicyMatcher(expected).matches(actual));
+ }
+
+ private static void assertNoMatch(Policy expected, Policy actual) {
+ assertFalse(new ApiPolicyMatcher(expected).matches(actual));
+ }
+
+ @Test
+ public void testStaticMocker() {
+ PolicyAcceptor mock = EasyMock.createMock(PolicyAcceptor.class);
+ EasyMock.expect(mock.accept(eqApiPolicy(API_POLICY_1))).andReturn(0);
+ EasyMock.replay(mock);
+ mock.accept(API_POLICY_2);
+ EasyMock.verify(mock);
+ }
+}