8000 Add Identity Access Management (IAM) to the Storage API by rybosome · Pull Request #1812 · googleapis/google-cloud-java · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.storage.Storage;
import com.google.cloud.storage.StorageException;
import com.google.cloud.storage.spi.v1.RpcBatch;
Expand Down Expand Up @@ -61,6 +63,7 @@
* <li>continueRewrite
* <li>createBatch
* <li>checksums, etags
* <li>IAM operations</li>
* </ul>
* </ul>
*/
Expand Down Expand Up @@ -443,4 +446,19 @@ private static boolean processedAsFolder(StorageObject so, String delimiter, Str
folders.put(folderName, fakeFolder);
return true;
}

@Override
public Policy getPolicy(String bucket) {
throw new UnsupportedOperationException();
}

@Override
public Policy updatePolicy(String bucket, Policy policy) {
throw new UnsupportedOperationException();
}

@Override
public TestIamPermissionsResponse testPermissions(String bucket, List<String> permissions) {
throw new UnsupportedOperationException();
}
}
3 changes: 1 addition & 2 deletions google-cloud-core/src/main/java/com/google/cloud/Policy.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@ public final Builder removeIdentity(Role role, Identity first, Identity... other
return this;
}
< 8000 /td>

/**
* Sets the policy's etag.
*
Expand All @@ -214,7 +213,7 @@ public final Builder removeIdentity(Role role, Identity first, Identity... other
* applied to the same version of the policy. If no etag is provided in the call to
* setIamPolicy, then the existing policy is overwritten blindly.
*/
protected final Builder setEtag(String etag) {
public final Builder setEtag(String etag) {
this.etag = etag;
return this;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.api.services.storage.model.Policy.Bindings;
import com.google.cloud.Identity;
import com.google.cloud.Policy;
import com.google.cloud.Role;

/**
* Helper for converting between the Policy model provided by the API and the Policy model provided
* by this library.
*/
class PolicyHelper {

static Policy convertFromApiPolicy(com.google.api.services.storage.model.Policy apiPolicy) {
Policy.Builder policyBuilder = Policy.newBuilder();
for (Bindings binding : apiPolicy.getBindings()) {
for (String member : binding.getMembers()) {
policyBuilder.addIdentity(Role.of(binding.getRole()), Identity.valueOf(member));
}
}
return policyBuilder.setEtag(apiPolicy.getEtag()).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());
}
bindings.add(new Bindings().setMembers(members).setRole(entry.getKey().getValue()));
}
return new com.google.api.services.storage.model.Policy()
.setBindings(bindings)
.setEtag(policy.getEtag());
}

private PolicyHelper() {
// Intentionally left blank.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,25 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.security.Key;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import com.google.auth.ServiceAccountSigner;
import com.google.auth.ServiceAccountSigner.SigningException;
import com.google.cloud.FieldSelector;
import com.google.cloud.FieldSelector.Helper;
import com.google.cloud.Page;
import com.google.cloud.Policy;
import com.google.cloud.ReadChannel;
import com.google.cloud.Service;
import com.google.cloud.WriteChannel;
Expand All @@ -35,19 +49,6 @@
import com.google.common.collect.Lists;
import com.google.common.io.BaseEncoding;

import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.security.Key;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
* An interface for Google Cloud Storage.
*
Expand Down Expand Up @@ -2369,4 +2370,57 @@ public static Builder newBuilder() {
* @throws StorageException upon failure
*/
List<Acl> listAcls(BlobId blob);

/**
* Gets the IAM policy for the provided bucket.
*
* <p>Example of getting the IAM policy for a bucket.
* <pre> {@code
* String bucketName = "my_unique_bucket";
* Policy policy = storage.getPolicy(bucketName);
* }</pre>
*
* @throws StorageException upon failure
*/
Policy getPolicy(String bucket);

/**
* Updates the IAM policy on the specified bucket.
*
* <p>Example of updating the IAM policy on a bucket.
* <pre>{@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());
* }</pre>
*
* @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.
*
* <p>Example of testing permissions on a bucket.
* <pre> {@code
* String bucketName = "my_unique_bucket";
* List<Boolean> response =
* storage.testPermissions(
* bucket,
* ImmutableList.of("storage.buckets.get", "storage.buckets.getIamPolicy"));
* for (boolean hasPermission : response) {
* // Do something with permission test response
* }
* }</pre>
*
* @throws StorageException upon failure
*/
List<Boolean> testPermissions(String bucket, List<String> permissions);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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<StorageOptions> implements Storage {

private static final byte[] EMPTY_BYTE_ARRAY = {};
Expand Down Expand Up @@ -854,6 +860,58 @@ public List<ObjectAccessControl> call() {
throw StorageException.translateAndThrow(e);
}
}

@Override
public Policy getPolicy(final String bucket) {
try {
return convertFromApiPolicy(runWithRetries(new Callable<com.google.api.services.storage.model.Policy>() {
@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<com.google.api.services.storage.model.Policy>() {
@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<Boolean> testPermissions(final String bucket, final List<String> permissions) {
try {
TestIamPermissionsResponse response = runWithRetries(new Callable<TestIamPermissionsResponse>() {
@Override
public TestIamPermissionsResponse call() {
return storageRpc.testPermissions(bucket, permissions);
}
}, getOptions().getRetrySettings(), EXCEPTION_HANDLER, getOptions().getClock());
final Set<String> heldPermissions =
response.getPermissions() != null
? ImmutableSet.copyOf(response.getPermissions())
: ImmutableSet.<String>of();
return Lists.transform(permissions, new Function<String, Boolean>() {
@Override
public Boolean apply(String permission) {
return heldPermissions.contains(permission);
}
});
} catch (RetryHelperException e) {
throw StorageException.translateAndThrow(e);
}
}

private static <T> void addToOptionMap(StorageRpc.Option option, T defaultValue,
Map<StorageRpc.Option, Object> map) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -834,4 +836,31 @@ public List<ObjectAccessControl> 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<String> permissions) {
try {
return storage.buckets().testIamPermissions(bucket, permissions).execute();
} catch (IOException ex) {
throw translate(ex);
}
}
}
Loading
0