From 11ffccc19666d74093ecba888b11c82dfe49b9ed Mon Sep 17 00:00:00 2001 From: Jesse Lovelace Date: Mon, 3 Jun 2019 13:18:33 -0700 Subject: [PATCH 1/2] Storage: HMAC service account support --- .../com/google/cloud/storage/HmacKey.java | 268 ++++++++++++++++++ .../com/google/cloud/storage/Storage.java | 14 + .../com/google/cloud/storage/StorageImpl.java | 168 +++++++++++ .../cloud/storage/spi/v1/HttpStorageRpc.java | 97 +++++++ .../storage/spi/v1/HttpStorageRpcSpans.java | 7 + .../cloud/storage/spi/v1/StorageRpc.java | 13 + .../cloud/storage/it/ITStorageTest.java | 78 +++++ 7 files changed, 645 insertions(+) create mode 100644 google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/HmacKey.java diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/HmacKey.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/HmacKey.java new file mode 100644 index 000000000000..f73330e88b75 --- /dev/null +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/HmacKey.java @@ -0,0 +1,268 @@ +package com.google.cloud.storage; + +import com.google.api.client.util.DateTime; + +import java.io.Serializable; +import java.util.Objects; + +public class HmacKey implements Serializable { + + private static final long serialVersionUID = -1809610424373783062L; + private final String secretKey; + private final HmacKeyMetadata metadata; + + private HmacKey(Builder builder) { + this.secretKey = builder.secretKey; + this.metadata = builder.metadata; + } + + public static Builder newBuilder(String secretKey) { + return new Builder(secretKey); + } + + public static class Builder { + private String secretKey; + private HmacKeyMetadata metadata; + + private Builder(String secretKey) { + this.secretKey = secretKey; + } + + public Builder setSecretKey(String secretKey) { + this.secretKey = secretKey; + return this; + } + + public Builder setMetadata(HmacKeyMetadata metadata) { + this.metadata = metadata; + return this; + } + + public HmacKey build() { + return new HmacKey(this); + } + } + + public String getSecretKey() { + return secretKey; + } + + public HmacKeyMetadata getMetadata() { + return metadata; + } + + com.google.api.services.storage.model.HmacKey toPb() { + com.google.api.services.storage.model.HmacKey hmacKey = + new com.google.api.services.storage.model.HmacKey(); + hmacKey.setSecret(this.secretKey); + + if (metadata != null) { + hmacKey.setMetadata(metadata.toPb()); + } + + return hmacKey; + } + + static HmacKey fromPb(com.google.api.services.storage.model.HmacKey hmacKey) { + return HmacKey.newBuilder(hmacKey.getSecret()) + .setMetadata(HmacKeyMetadata.fromPb(hmacKey.getMetadata())) + .build(); + } + + public enum HmacKeyState { + ACTIVE("ACTIVE"), + INACTIVE("INACTIVE"), + DELETED("DELETED"); + + private final String state; + + HmacKeyState(String state) { + this.state = state; + } + } + + public static class HmacKeyMetadata { + + private final String accessId; + private final String etag; + private final String id; + private final String projectId; + private final ServiceAccount serviceAccount; + private final HmacKeyState state; + private final Long createTime; + private final Long updateTime; + + private HmacKeyMetadata(Builder builder) { + this.accessId = builder.accessId; + this.etag = builder.etag; + this.id = builder.id; + this.projectId = builder.projectId; + this.serviceAccount = builder.serviceAccount; + this.state = builder.state; + this.createTime = builder.createTime; + this.updateTime = builder.updateTime; + } + + public static Builder newBuilder(ServiceAccount serviceAccount) { + return new Builder(serviceAccount); + } + + public Builder toBuilder() { + return new Builder(this); + } + + @Override + public int hashCode() { + return Objects.hash(accessId, projectId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final HmacKeyMetadata other = (HmacKeyMetadata) obj; + return Objects.equals(this.accessId, other.accessId) + && Objects.equals(this.etag, other.etag) + && Objects.equals(this.id, other.id) + && Objects.equals(this.projectId, other.projectId) + && Objects.equals(this.serviceAccount, other.serviceAccount) + && Objects.equals(this.state, other.state) + && Objects.equals(this.createTime, other.createTime) + && Objects.equals(this.updateTime, other.updateTime); + } + + public com.google.api.services.storage.model.HmacKeyMetadata toPb() { + com.google.api.services.storage.model.HmacKeyMetadata metadata = + new com.google.api.services.storage.model.HmacKeyMetadata(); + metadata.setAccessId(this.accessId); + metadata.setEtag(this.etag); + metadata.setId(this.id); + metadata.setProjectId(this.projectId); + metadata.setServiceAccountEmail( + this.serviceAccount == null ? null : this.serviceAccount.getEmail()); + metadata.setState(this.state == null ? null : this.state.toString()); + metadata.setTimeCreated(this.createTime == null ? null : new DateTime(this.createTime)); + metadata.setUpdated(this.updateTime == null ? null : new DateTime(this.updateTime)); + + return metadata; + } + + static HmacKeyMetadata fromPb(com.google.api.services.storage.model.HmacKeyMetadata metadata) { + return newBuilder(ServiceAccount.of(metadata.getServiceAccountEmail())) + .setAccessId(metadata.getAccessId()) + .setCreateTime(metadata.getTimeCreated().getValue()) + .setEtag(metadata.getEtag()) + .setId(metadata.getId()) + .setProjectId(metadata.getProjectId()) + .setState(HmacKeyState.valueOf(metadata.getState())) + .setUpdateTime(metadata.getUpdated().getValue()) + .build(); + } + + public String getAccessId() { + return accessId; + } + + public String getEtag() { + return etag; + } + + public String getId() { + return id; + } + + public String getProjectId() { + return projectId; + } + + public ServiceAccount getServiceAccount() { + return serviceAccount; + } + + public HmacKeyState getState() { + return state; + } + + public Long getCreateTime() { + return createTime; + } + + public Long getUpdateTime() { + return updateTime; + } + + public static class Builder { + private String accessId; + private String etag; + private String id; + private String projectId; + private ServiceAccount serviceAccount; + private HmacKeyState state; + private Long createTime; + private Long updateTime; + + private Builder(ServiceAccount serviceAccount) { + this.serviceAccount = serviceAccount; + } + + private Builder(HmacKeyMetadata metadata) { + this.accessId = metadata.accessId; + this.etag = metadata.etag; + this.id = metadata.id; + this.projectId = metadata.projectId; + this.serviceAccount = metadata.serviceAccount; + this.state = metadata.state; + this.createTime = metadata.createTime; + this.updateTime = metadata.updateTime; + } + + public Builder setAccessId(String accessId) { + this.accessId = accessId; + return this; + } + + public Builder setEtag(String etag) { + this.etag = etag; + return this; + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setServiceAccount(ServiceAccount serviceAccount) { + this.serviceAccount = serviceAccount; + return this; + } + + public Builder setState(HmacKeyState state) { + this.state = state; + return this; + } + + public Builder setCreateTime(long createTime) { + this.createTime = createTime; + return this; + } + + public Builder setProjectId(String projectId) { + this.projectId = projectId; + return this; + } + + public HmacKeyMetadata build() { + return new HmacKeyMetadata(this); + } + + public Builder setUpdateTime(long updateTime) { + this.updateTime = updateTime; + return this; + } + } + } +} diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java index 75f2d7ca1811..7a38c524fd46 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java @@ -35,6 +35,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.io.BaseEncoding; +import com.google.cloud.storage.HmacKey.HmacKeyMetadata; import java.io.InputStream; import java.io.Serializable; import java.net.URL; @@ -2708,6 +2709,19 @@ Blob create( */ List listAcls(BlobId blob); + HmacKey createHmacKey(ServiceAccount serviceAccount); + + Page listHmacKeys( + ServiceAccount serviceAccount, String pageToken, Long maxResults); + + Page listHmacKeys(ServiceAccount serviceAccount); + + HmacKeyMetadata getHmacKey(String accessId); + + void deleteHmacKey(String accessId); + + HmacKeyMetadata updateHmacKeyState( + final HmacKeyMetadata hmacKeyMetadata, final HmacKey.HmacKeyState state); /** * Gets the IAM policy for the provided bucket. * diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java index 6f127ac5fb0c..2daf723a3b9f 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java @@ -63,6 +63,7 @@ import com.google.common.io.BaseEncoding; import com.google.common.net.UrlEscapers; import com.google.common.primitives.Ints; +import com.google.cloud.storage.HmacKey.HmacKeyMetadata; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -297,6 +298,31 @@ public Page getNextPage() { } } + private static class HmacKeyMetadataPageFetcher implements NextPageFetcher { + + private static final long serialVersionUID = 308012320541700881L; + private final StorageOptions serviceOptions; + private final ServiceAccount serviceAccount; + private final String nextPageToken; + private final Long maxResults; + + HmacKeyMetadataPageFetcher( + StorageOptions serviceOptions, + ServiceAccount serviceAccount, + String nextPageToken, + Long maxResults) { + this.serviceOptions = serviceOptions; + this.serviceAccount = serviceAccount; + this.nextPageToken = nextPageToken; + this.maxResults = maxResults; + } + + @Override + public Page getNextPage() { + return listHmacKeys(serviceOptions, serviceAccount, nextPageToken, maxResults); + } + } + @Override public Page list(BucketListOption... options) { return listBuckets(getOptions(), optionMap(options)); @@ -1163,6 +1189,148 @@ public List call() { } } + public HmacKey createHmacKey(final ServiceAccount serviceAccount) { + try { + return HmacKey.fromPb( + runWithRetries( + new Callable() { + @Override + public com.google.api.services.storage.model.HmacKey call() { + return storageRpc.createHmacKey(serviceAccount.getEmail()); + } + }, + getOptions().getRetrySettings(), + EXCEPTION_HANDLER, + getOptions().getClock())); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + + @Override + public Page listHmacKeys( + final ServiceAccount serviceAccount, final String pageToken, final Long maxResults) { + return listHmacKeys(getOptions(), serviceAccount, pageToken, maxResults); + } + + public Page listHmacKeys(final ServiceAccount serviceAccount) { + return listHmacKeys(getOptions(), serviceAccount, null, null); + } + + @Override + public HmacKeyMetadata getHmacKey(final String accessId) { + try { + return HmacKeyMetadata.fromPb( + runWithRetries( + new Callable() { + @Override + public com.google.api.services.storage.model.HmacKeyMetadata call() { + return storageRpc.getHmacKey(accessId); + } + }, + getOptions().getRetrySettings(), + EXCEPTION_HANDLER, + getOptions().getClock())); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + + private HmacKeyMetadata updateHmacKey(final HmacKeyMetadata hmacKeyMetadata) { + try { + return HmacKeyMetadata.fromPb( + runWithRetries( + new Callable() { + @Override + public com.google.api.services.storage.model.HmacKeyMetadata call() { + return storageRpc.updateHmacKey(hmacKeyMetadata.toPb()); + } + }, + getOptions().getRetrySettings(), + EXCEPTION_HANDLER, + getOptions().getClock())); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + + @Override + public HmacKeyMetadata updateHmacKeyState( + final HmacKeyMetadata hmacKeyMetadata, final HmacKey.HmacKeyState state) { + HmacKeyMetadata updatedMetadata = + HmacKeyMetadata.newBuilder(hmacKeyMetadata.getServiceAccount()) + .setProjectId(hmacKeyMetadata.getProjectId()) + .setAccessId(hmacKeyMetadata.getAccessId()) + .setState(state) + .build(); + return updateHmacKey(updatedMetadata); + } + + @Override + public void deleteHmacKey(final String accessId) { + try { + runWithRetries( + new Callable() { + @Override + public Void call() { + storageRpc.deleteHmacKey(accessId); + return null; + } + }, + getOptions().getRetrySettings(), + EXCEPTION_HANDLER, + getOptions().getClock()); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + + private static Page listHmacKeys( + final StorageOptions serviceOptions, + final ServiceAccount serviceAccount, + final String pageToken, + final Long maxResults) { + try { + Tuple> result = + runWithRetries( + new Callable< + Tuple< + String, Iterable>>() { + @Override + public Tuple< + String, Iterable> + call() { + return serviceOptions + .getStorageRpcV1() + .listHmacKeys(serviceAccount.getEmail(), pageToken, maxResults); + } + }, + serviceOptions.getRetrySettings(), + EXCEPTION_HANDLER, + serviceOptions.getClock()); + String cursor = result.x(); + final Iterable metadata = + result.y() == null + ? ImmutableList.of() + : Iterables.transform( + result.y(), + new Function< + com.google.api.services.storage.model.HmacKeyMetadata, HmacKeyMetadata>() { + @Override + public HmacKeyMetadata apply( + com.google.api.services.storage.model.HmacKeyMetadata metadataPb) { + return HmacKeyMetadata.fromPb(metadataPb); + } + }); + return new PageImpl<>( + new HmacKeyMetadataPageFetcher(serviceOptions, serviceAccount, pageToken, maxResults), + cursor, + metadata); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + @Override public Policy getIamPolicy(final String bucket, BucketSourceOption... options) { try { diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java index d162b33c5c7f..ce34268a7516 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java @@ -46,6 +46,9 @@ import com.google.api.services.storage.model.Buckets; import com.google.api.services.storage.model.ComposeRequest; import com.google.api.services.storage.model.ComposeRequest.SourceObjects.ObjectPreconditions; +import com.google.api.services.storage.model.HmacKey; +import com.google.api.services.storage.model.HmacKeyMetadata; +import com.google.api.services.storage.model.HmacKeysMetadata; import com.google.api.services.storage.model.Notification; import com.google.api.services.storage.model.ObjectAccessControl; import com.google.api.services.storage.model.Objects; @@ -1231,6 +1234,100 @@ public List listAcls(String bucket, String object, Long gen } } + @Override + public HmacKey createHmacKey(String serviceAccountEmail) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_CREATE_HMAC_KEY); + Scope scope = tracer.withSpan(span); + try { + return storage + .projects() + .hmacKeys() + .create(this.options.getProjectId(), serviceAccountEmail) + .execute(); + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + throw translate(ex); + } finally { + scope.close(); + span.end(); + } + } + + @Override + public Tuple> listHmacKeys( + String serviceAccountEmail, String pageToken, Long maxResults) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_LIST_HMAC_KEYS); + Scope scope = tracer.withSpan(span); + try { + HmacKeysMetadata hmacKeysMetadata = + storage + .projects() + .hmacKeys() + .list(this.options.getProjectId()) + .setServiceAccountEmail(serviceAccountEmail) + .setPageToken(pageToken) + .setMaxResults(maxResults) + .execute(); + return Tuple.>of( + hmacKeysMetadata.getNextPageToken(), hmacKeysMetadata.getItems()); + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + throw translate(ex); + } finally { + scope.close(); + span.end(); + } + } + + @Override + public HmacKeyMetadata getHmacKey(String accessId) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_GET_HMAC_KEY); + Scope scope = tracer.withSpan(span); + try { + return storage.projects().hmacKeys().get(this.options.getProjectId(), accessId).execute(); + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + throw translate(ex); + } finally { + scope.close(); + span.end(); + } + } + + @Override + public HmacKeyMetadata updateHmacKey(HmacKeyMetadata hmacKeyMetadata) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_UPDATE_HMAC_KEY); + Scope scope = tracer.withSpan(span); + try { + return storage + .projects() + .hmacKeys() + .update(options.getProjectId(), hmacKeyMetadata.getAccessId(), hmacKeyMetadata) + .execute(); + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + throw translate(ex); + } finally { + scope.close(); + span.end(); + } + } + + @Override + public void deleteHmacKey(String accessId) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_DELETE_HMAC_KEY); + Scope scope = tracer.withSpan(span); + try { + storage.projects().hmacKeys().delete(options.getProjectId(), accessId).execute(); + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + throw translate(ex); + } finally { + scope.close(); + span.end(); + } + } + @Override public Policy getIamPolicy(String bucket, Map options) { Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_GET_BUCKET_IAM_POLICY); diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java index 490542f861cb..ff3be9c07262 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java @@ -72,6 +72,13 @@ class HttpStorageRpcSpans { static final String SPAN_NAME_PATCH_OBJECT_ACL = getTraceSpanName("patchAcl(ObjectAccessControl)"); static final String SPAN_NAME_LIST_OBJECT_ACLS = getTraceSpanName("listAcls(String,String,Long)"); + static final String SPAN_NAME_CREATE_HMAC_KEY = getTraceSpanName("createHmacKey(String)"); + static final String SPAN_NAME_GET_HMAC_KEY = getTraceSpanName("getHmacKey(String)"); + static final String SPAN_NAME_DELETE_HMAC_KEY = getTraceSpanName("deleteHmacKey(String)"); + static final String SPAN_NAME_LIST_HMAC_KEYS = + getTraceSpanName("listHmacKeys(String,String,Long)"); + static final String SPAN_NAME_UPDATE_HMAC_KEY = + getTraceSpanName("updateHmacKey(HmacKeyMetadata)"); static final String SPAN_NAME_GET_BUCKET_IAM_POLICY = getTraceSpanName("getIamPolicy(String,Map)"); static final String SPAN_NAME_SET_BUCKET_IAM_POLICY = diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java index c85958176d69..f02aaf29026d 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java @@ -19,6 +19,8 @@ import com.google.api.core.InternalApi; import com.google.api.services.storage.model.Bucket; import com.google.api.services.storage.model.BucketAccessControl; +import com.google.api.services.storage.model.HmacKey; +import com.google.api.services.storage.model.HmacKeyMetadata; import com.google.api.services.storage.model.Notification; import com.google.api.services.storage.model.ObjectAccessControl; import com.google.api.services.storage.model.Policy; @@ -434,6 +436,17 @@ void write( */ List listAcls(String bucket, String object, Long generation); + HmacKey createHmacKey(String serviceAccountEmail); + + Tuple> listHmacKeys( + String serviceAccountEmail, String pageToken, Long maxResults); + + HmacKeyMetadata updateHmacKey(HmacKeyMetadata hmacKeyMetadata); + + HmacKeyMetadata getHmacKey(String accessId); + + void deleteHmacKey(String accessId); + /** * Returns the IAM policy for the specified bucket. * diff --git a/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java index 8b2d627b23db..11b7892cf1bc 100644 --- a/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java +++ b/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java @@ -64,6 +64,7 @@ import com.google.cloud.storage.BucketInfo.LifecycleRule.LifecycleAction; import com.google.cloud.storage.BucketInfo.LifecycleRule.LifecycleCondition; import com.google.cloud.storage.CopyWriter; +import com.google.cloud.storage.HmacKey; import com.google.cloud.storage.HttpMethod; import com.google.cloud.storage.ServiceAccount; import com.google.cloud.storage.Storage; @@ -2108,6 +2109,83 @@ public void testBlobAcl() { } } + @Test + public void testHmacKey() { + ServiceAccount serviceAccount = + ServiceAccount.of("it-service-account@gcloud-devel.iam.gserviceaccount.com"); + try { + + HmacKey hmacKey = storage.createHmacKey(serviceAccount); + String secretKey = hmacKey.getSecretKey(); + assertNotNull(secretKey); + HmacKey.HmacKeyMetadata metadata = hmacKey.getMetadata(); + String accessId = metadata.getAccessId(); + + assertNotNull(accessId); + assertNotNull(metadata.getEtag()); + assertNotNull(metadata.getId()); + assertEquals(remoteStorageHelper.getOptions().getProjectId(), metadata.getProjectId()); + assertEquals(serviceAccount.getEmail(), metadata.getServiceAccount().getEmail()); + assertEquals(HmacKey.HmacKeyState.ACTIVE, metadata.getState()); + assertNotNull(metadata.getCreateTime()); + assertNotNull(metadata.getUpdateTime()); + + Page metadatas = storage.listHmacKeys(serviceAccount); + boolean createdHmacKeyIsInList = false; + for (HmacKey.HmacKeyMetadata hmacKeyMetadata : metadatas.iterateAll()) { + if (accessId.equals(hmacKeyMetadata.getAccessId())) { + createdHmacKeyIsInList = true; + break; + } + } + + if (!createdHmacKeyIsInList) { + fail("Created an HMAC key but it didn't show up in list()"); + } + + HmacKey.HmacKeyMetadata getResult = storage.getHmacKey(accessId); + assertEquals(metadata, getResult); + + storage.updateHmacKeyState(metadata, HmacKey.HmacKeyState.INACTIVE); + + storage.deleteHmacKey(accessId); + + metadatas = storage.listHmacKeys(serviceAccount); + createdHmacKeyIsInList = false; + for (HmacKey.HmacKeyMetadata hmacKeyMetadata : metadatas.iterateAll()) { + if (accessId.equals(hmacKeyMetadata.getAccessId())) { + createdHmacKeyIsInList = true; + break; + } + } + + if (createdHmacKeyIsInList) { + fail("Deleted an HMAC key but it showed up in list()"); + } + + storage.createHmacKey(serviceAccount); + storage.createHmacKey(serviceAccount); + storage.createHmacKey(serviceAccount); + storage.createHmacKey(serviceAccount); + + metadatas = storage.listHmacKeys(serviceAccount, null, 2L); + + String nextPageToken = metadatas.getNextPageToken(); + + assertEquals(2, Iterators.size(metadatas.getValues().iterator())); + + metadatas = storage.listHmacKeys(serviceAccount, nextPageToken, 2L); + + assertEquals(2, Iterators.size(metadatas.getValues().iterator())); + } finally { + Page metadatas = storage.listHmacKeys(serviceAccount); + for (HmacKey.HmacKeyMetadata hmacKeyMetadata : metadatas.iterateAll()) { + storage.updateHmacKeyState(hmacKeyMetadata, HmacKey.HmacKeyState.INACTIVE); + storage.deleteHmacKey(hmacKeyMetadata.getAccessId()); + } + } + } + @Test public void testReadCompressedBlob() throws IOException { String blobName = "test-read-compressed-blob"; From fe560aaeae2903214a2796ca123d7bc249049a8a Mon Sep 17 00:00:00 2001 From: Jesse Lovelace Date: Mon, 3 Jun 2019 13:18:33 -0700 Subject: [PATCH 2/2] Storage: HMAC service account support --- .../com/google/cloud/storage/HmacKey.java | 287 ++++++++++++++++++ .../com/google/cloud/storage/Storage.java | 14 + .../com/google/cloud/storage/StorageImpl.java | 168 ++++++++++ .../cloud/storage/spi/v1/HttpStorageRpc.java | 97 ++++++ .../storage/spi/v1/HttpStorageRpcSpans.java | 7 + .../cloud/storage/spi/v1/StorageRpc.java | 13 + .../cloud/storage/it/ITStorageTest.java | 78 +++++ 7 files changed, 664 insertions(+) create mode 100644 google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/HmacKey.java diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/HmacKey.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/HmacKey.java new file mode 100644 index 000000000000..bb5deae8b0e8 --- /dev/null +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/HmacKey.java @@ -0,0 +1,287 @@ +package com.google.cloud.storage; + +import com.google.api.client.util.DateTime; + +import java.io.Serializable; +import java.util.Objects; + +public class HmacKey implements Serializable { + + private static final long serialVersionUID = -1809610424373783062L; + private final String secretKey; + private final HmacKeyMetadata metadata; + + private HmacKey(Builder builder) { + this.secretKey = builder.secretKey; + this.metadata = builder.metadata; + } + + public static Builder newBuilder(String secretKey) { + return new Builder(secretKey); + } + + public static class Builder { + private String secretKey; + private HmacKeyMetadata metadata; + + private Builder(String secretKey) { + this.secretKey = secretKey; + } + + public Builder setSecretKey(String secretKey) { + this.secretKey = secretKey; + return this; + } + + public Builder setMetadata(HmacKeyMetadata metadata) { + this.metadata = metadata; + return this; + } + + public HmacKey build() { + return new HmacKey(this); + } + } + + public String getSecretKey() { + return secretKey; + } + + public HmacKeyMetadata getMetadata() { + return metadata; + } + + @Override + public int hashCode() { + return Objects.hash(secretKey, metadata); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final HmacKeyMetadata other = (HmacKeyMetadata) obj; + return Objects.equals(this.secretKey, secretKey) + && Objects.equals(this.metadata, metadata); + } + + com.google.api.services.storage.model.HmacKey toPb() { + com.google.api.services.storage.model.HmacKey hmacKey = + new com.google.api.services.storage.model.HmacKey(); + hmacKey.setSecret(this.secretKey); + + if (metadata != null) { + hmacKey.setMetadata(metadata.toPb()); + } + + return hmacKey; + } + + static HmacKey fromPb(com.google.api.services.storage.model.HmacKey hmacKey) { + return HmacKey.newBuilder(hmacKey.getSecret()) + .setMetadata(HmacKeyMetadata.fromPb(hmacKey.getMetadata())) + .build(); + } + + public enum HmacKeyState { + ACTIVE("ACTIVE"), + INACTIVE("INACTIVE"), + DELETED("DELETED"); + + private final String state; + + HmacKeyState(String state) { + this.state = state; + } + } + + public static class HmacKeyMetadata implements Serializable { + + private static final long serialVersionUID = 4571684785352640737L; + private final String accessId; + private final String etag; + private final String id; + private final String projectId; + private final ServiceAccount serviceAccount; + private final HmacKeyState state; + private final Long createTime; + private final Long updateTime; + + private HmacKeyMetadata(Builder builder) { + this.accessId = builder.accessId; + this.etag = builder.etag; + this.id = builder.id; + this.projectId = builder.projectId; + this.serviceAccount = builder.serviceAccount; + this.state = builder.state; + this.createTime = builder.createTime; + this.updateTime = builder.updateTime; + } + + public static Builder newBuilder(ServiceAccount serviceAccount) { + return new Builder(serviceAccount); + } + + public Builder toBuilder() { + return new Builder(this); + } + + @Override + public int hashCode() { + return Objects.hash(accessId, projectId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final HmacKeyMetadata other = (HmacKeyMetadata) obj; + return Objects.equals(this.accessId, other.accessId) + && Objects.equals(this.etag, other.etag) + && Objects.equals(this.id, other.id) + && Objects.equals(this.projectId, other.projectId) + && Objects.equals(this.serviceAccount, other.serviceAccount) + && Objects.equals(this.state, other.state) + && Objects.equals(this.createTime, other.createTime) + && Objects.equals(this.updateTime, other.updateTime); + } + + public com.google.api.services.storage.model.HmacKeyMetadata toPb() { + com.google.api.services.storage.model.HmacKeyMetadata metadata = + new com.google.api.services.storage.model.HmacKeyMetadata(); + metadata.setAccessId(this.accessId); + metadata.setEtag(this.etag); + metadata.setId(this.id); + metadata.setProjectId(this.projectId); + metadata.setServiceAccountEmail( + this.serviceAccount == null ? null : this.serviceAccount.getEmail()); + metadata.setState(this.state == null ? null : this.state.toString()); + metadata.setTimeCreated(this.createTime == null ? null : new DateTime(this.createTime)); + metadata.setUpdated(this.updateTime == null ? null : new DateTime(this.updateTime)); + + return metadata; + } + + static HmacKeyMetadata fromPb(com.google.api.services.storage.model.HmacKeyMetadata metadata) { + return newBuilder(ServiceAccount.of(metadata.getServiceAccountEmail())) + .setAccessId(metadata.getAccessId()) + .setCreateTime(metadata.getTimeCreated().getValue()) + .setEtag(metadata.getEtag()) + .setId(metadata.getId()) + .setProjectId(metadata.getProjectId()) + .setState(HmacKeyState.valueOf(metadata.getState())) + .setUpdateTime(metadata.getUpdated().getValue()) + .build(); + } + + public String getAccessId() { + return accessId; + } + + public String getEtag() { + return etag; + } + + public String getId() { + return id; + } + + public String getProjectId() { + return projectId; + } + + public ServiceAccount getServiceAccount() { + return serviceAccount; + } + + public HmacKeyState getState() { + return state; + } + + public Long getCreateTime() { + return createTime; + } + + public Long getUpdateTime() { + return updateTime; + } + + public static class Builder { + private String accessId; + private String etag; + private String id; + private String projectId; + private ServiceAccount serviceAccount; + private HmacKeyState state; + private Long createTime; + private Long updateTime; + + private Builder(ServiceAccount serviceAccount) { + this.serviceAccount = serviceAccount; + } + + private Builder(HmacKeyMetadata metadata) { + this.accessId = metadata.accessId; + this.etag = metadata.etag; + this.id = metadata.id; + this.projectId = metadata.projectId; + this.serviceAccount = metadata.serviceAccount; + this.state = metadata.state; + this.createTime = metadata.createTime; + this.updateTime = metadata.updateTime; + } + + public Builder setAccessId(String accessId) { + this.accessId = accessId; + return this; + } + + public Builder setEtag(String etag) { + this.etag = etag; + return this; + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setServiceAccount(ServiceAccount serviceAccount) { + this.serviceAccount = serviceAccount; + return this; + } + + public Builder setState(HmacKeyState state) { + this.state = state; + return this; + } + + public Builder setCreateTime(long createTime) { + this.createTime = createTime; + return this; + } + + public Builder setProjectId(String projectId) { + this.projectId = projectId; + return this; + } + + public HmacKeyMetadata build() { + return new HmacKeyMetadata(this); + } + + public Builder setUpdateTime(long updateTime) { + this.updateTime = updateTime; + return this; + } + } + } +} diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java index 75f2d7ca1811..7a38c524fd46 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/Storage.java @@ -35,6 +35,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.io.BaseEncoding; +import com.google.cloud.storage.HmacKey.HmacKeyMetadata; import java.io.InputStream; import java.io.Serializable; import java.net.URL; @@ -2708,6 +2709,19 @@ Blob create( */ List listAcls(BlobId blob); + HmacKey createHmacKey(ServiceAccount serviceAccount); + + Page listHmacKeys( + ServiceAccount serviceAccount, String pageToken, Long maxResults); + + Page listHmacKeys(ServiceAccount serviceAccount); + + HmacKeyMetadata getHmacKey(String accessId); + + void deleteHmacKey(String accessId); + + HmacKeyMetadata updateHmacKeyState( + final HmacKeyMetadata hmacKeyMetadata, final HmacKey.HmacKeyState state); /** * Gets the IAM policy for the provided bucket. * diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java index 6f127ac5fb0c..2daf723a3b9f 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java @@ -63,6 +63,7 @@ import com.google.common.io.BaseEncoding; import com.google.common.net.UrlEscapers; import com.google.common.primitives.Ints; +import com.google.cloud.storage.HmacKey.HmacKeyMetadata; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -297,6 +298,31 @@ public Page getNextPage() { } } + private static class HmacKeyMetadataPageFetcher implements NextPageFetcher { + + private static final long serialVersionUID = 308012320541700881L; + private final StorageOptions serviceOptions; + private final ServiceAccount serviceAccount; + private final String nextPageToken; + private final Long maxResults; + + HmacKeyMetadataPageFetcher( + StorageOptions serviceOptions, + ServiceAccount serviceAccount, + String nextPageToken, + Long maxResults) { + this.serviceOptions = serviceOptions; + this.serviceAccount = serviceAccount; + this.nextPageToken = nextPageToken; + this.maxResults = maxResults; + } + + @Override + public Page getNextPage() { + return listHmacKeys(serviceOptions, serviceAccount, nextPageToken, maxResults); + } + } + @Override public Page list(BucketListOption... options) { return listBuckets(getOptions(), optionMap(options)); @@ -1163,6 +1189,148 @@ public List call() { } } + public HmacKey createHmacKey(final ServiceAccount serviceAccount) { + try { + return HmacKey.fromPb( + runWithRetries( + new Callable() { + @Override + public com.google.api.services.storage.model.HmacKey call() { + return storageRpc.createHmacKey(serviceAccount.getEmail()); + } + }, + getOptions().getRetrySettings(), + EXCEPTION_HANDLER, + getOptions().getClock())); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + + @Override + public Page listHmacKeys( + final ServiceAccount serviceAccount, final String pageToken, final Long maxResults) { + return listHmacKeys(getOptions(), serviceAccount, pageToken, maxResults); + } + + public Page listHmacKeys(final ServiceAccount serviceAccount) { + return listHmacKeys(getOptions(), serviceAccount, null, null); + } + + @Override + public HmacKeyMetadata getHmacKey(final String accessId) { + try { + return HmacKeyMetadata.fromPb( + runWithRetries( + new Callable() { + @Override + public com.google.api.services.storage.model.HmacKeyMetadata call() { + return storageRpc.getHmacKey(accessId); + } + }, + getOptions().getRetrySettings(), + EXCEPTION_HANDLER, + getOptions().getClock())); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + + private HmacKeyMetadata updateHmacKey(final HmacKeyMetadata hmacKeyMetadata) { + try { + return HmacKeyMetadata.fromPb( + runWithRetries( + new Callable() { + @Override + public com.google.api.services.storage.model.HmacKeyMetadata call() { + return storageRpc.updateHmacKey(hmacKeyMetadata.toPb()); + } + }, + getOptions().getRetrySettings(), + EXCEPTION_HANDLER, + getOptions().getClock())); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + + @Override + public HmacKeyMetadata updateHmacKeyState( + final HmacKeyMetadata hmacKeyMetadata, final HmacKey.HmacKeyState state) { + HmacKeyMetadata updatedMetadata = + HmacKeyMetadata.newBuilder(hmacKeyMetadata.getServiceAccount()) + .setProjectId(hmacKeyMetadata.getProjectId()) + .setAccessId(hmacKeyMetadata.getAccessId()) + .setState(state) + .build(); + return updateHmacKey(updatedMetadata); + } + + @Override + public void deleteHmacKey(final String accessId) { + try { + runWithRetries( + new Callable() { + @Override + public Void call() { + storageRpc.deleteHmacKey(accessId); + return null; + } + }, + getOptions().getRetrySettings(), + EXCEPTION_HANDLER, + getOptions().getClock()); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + + private static Page listHmacKeys( + final StorageOptions serviceOptions, + final ServiceAccount serviceAccount, + final String pageToken, + final Long maxResults) { + try { + Tuple> result = + runWithRetries( + new Callable< + Tuple< + String, Iterable>>() { + @Override + public Tuple< + String, Iterable> + call() { + return serviceOptions + .getStorageRpcV1() + .listHmacKeys(serviceAccount.getEmail(), pageToken, maxResults); + } + }, + serviceOptions.getRetrySettings(), + EXCEPTION_HANDLER, + serviceOptions.getClock()); + String cursor = result.x(); + final Iterable metadata = + result.y() == null + ? ImmutableList.of() + : Iterables.transform( + result.y(), + new Function< + com.google.api.services.storage.model.HmacKeyMetadata, HmacKeyMetadata>() { + @Override + public HmacKeyMetadata apply( + com.google.api.services.storage.model.HmacKeyMetadata metadataPb) { + return HmacKeyMetadata.fromPb(metadataPb); + } + }); + return new PageImpl<>( + new HmacKeyMetadataPageFetcher(serviceOptions, serviceAccount, pageToken, maxResults), + cursor, + metadata); + } catch (RetryHelperException e) { + throw StorageException.translateAndThrow(e); + } + } + @Override public Policy getIamPolicy(final String bucket, BucketSourceOption... options) { try { diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java index d162b33c5c7f..ce34268a7516 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpc.java @@ -46,6 +46,9 @@ import com.google.api.services.storage.model.Buckets; import com.google.api.services.storage.model.ComposeRequest; import com.google.api.services.storage.model.ComposeRequest.SourceObjects.ObjectPreconditions; +import com.google.api.services.storage.model.HmacKey; +import com.google.api.services.storage.model.HmacKeyMetadata; +import com.google.api.services.storage.model.HmacKeysMetadata; import com.google.api.services.storage.model.Notification; import com.google.api.services.storage.model.ObjectAccessControl; import com.google.api.services.storage.model.Objects; @@ -1231,6 +1234,100 @@ public List listAcls(String bucket, String object, Long gen } } + @Override + public HmacKey createHmacKey(String serviceAccountEmail) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_CREATE_HMAC_KEY); + Scope scope = tracer.withSpan(span); + try { + return storage + .projects() + .hmacKeys() + .create(this.options.getProjectId(), serviceAccountEmail) + .execute(); + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + throw translate(ex); + } finally { + scope.close(); + span.end(); + } + } + + @Override + public Tuple> listHmacKeys( + String serviceAccountEmail, String pageToken, Long maxResults) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_LIST_HMAC_KEYS); + Scope scope = tracer.withSpan(span); + try { + HmacKeysMetadata hmacKeysMetadata = + storage + .projects() + .hmacKeys() + .list(this.options.getProjectId()) + .setServiceAccountEmail(serviceAccountEmail) + .setPageToken(pageToken) + .setMaxResults(maxResults) + .execute(); + return Tuple.>of( + hmacKeysMetadata.getNextPageToken(), hmacKeysMetadata.getItems()); + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + throw translate(ex); + } finally { + scope.close(); + span.end(); + } + } + + @Override + public HmacKeyMetadata getHmacKey(String accessId) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_GET_HMAC_KEY); + Scope scope = tracer.withSpan(span); + try { + return storage.projects().hmacKeys().get(this.options.getProjectId(), accessId).execute(); + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + throw translate(ex); + } finally { + scope.close(); + span.end(); + } + } + + @Override + public HmacKeyMetadata updateHmacKey(HmacKeyMetadata hmacKeyMetadata) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_UPDATE_HMAC_KEY); + Scope scope = tracer.withSpan(span); + try { + return storage + .projects() + .hmacKeys() + .update(options.getProjectId(), hmacKeyMetadata.getAccessId(), hmacKeyMetadata) + .execute(); + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + throw translate(ex); + } finally { + scope.close(); + span.end(); + } + } + + @Override + public void deleteHmacKey(String accessId) { + Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_DELETE_HMAC_KEY); + Scope scope = tracer.withSpan(span); + try { + storage.projects().hmacKeys().delete(options.getProjectId(), accessId).execute(); + } catch (IOException ex) { + span.setStatus(Status.UNKNOWN.withDescription(ex.getMessage())); + throw translate(ex); + } finally { + scope.close(); + span.end(); + } + } + @Override public Policy getIamPolicy(String bucket, Map options) { Span span = startSpan(HttpStorageRpcSpans.SPAN_NAME_GET_BUCKET_IAM_POLICY); diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java index 490542f861cb..ff3be9c07262 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/HttpStorageRpcSpans.java @@ -72,6 +72,13 @@ class HttpStorageRpcSpans { static final String SPAN_NAME_PATCH_OBJECT_ACL = getTraceSpanName("patchAcl(ObjectAccessControl)"); static final String SPAN_NAME_LIST_OBJECT_ACLS = getTraceSpanName("listAcls(String,String,Long)"); + static final String SPAN_NAME_CREATE_HMAC_KEY = getTraceSpanName("createHmacKey(String)"); + static final String SPAN_NAME_GET_HMAC_KEY = getTraceSpanName("getHmacKey(String)"); + static final String SPAN_NAME_DELETE_HMAC_KEY = getTraceSpanName("deleteHmacKey(String)"); + static final String SPAN_NAME_LIST_HMAC_KEYS = + getTraceSpanName("listHmacKeys(String,String,Long)"); + static final String SPAN_NAME_UPDATE_HMAC_KEY = + getTraceSpanName("updateHmacKey(HmacKeyMetadata)"); static final String SPAN_NAME_GET_BUCKET_IAM_POLICY = getTraceSpanName("getIamPolicy(String,Map)"); static final String SPAN_NAME_SET_BUCKET_IAM_POLICY = diff --git a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java index c85958176d69..f02aaf29026d 100644 --- a/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java +++ b/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/spi/v1/StorageRpc.java @@ -19,6 +19,8 @@ import com.google.api.core.InternalApi; import com.google.api.services.storage.model.Bucket; import com.google.api.services.storage.model.BucketAccessControl; +import com.google.api.services.storage.model.HmacKey; +import com.google.api.services.storage.model.HmacKeyMetadata; import com.google.api.services.storage.model.Notification; import com.google.api.services.storage.model.ObjectAccessControl; import com.google.api.services.storage.model.Policy; @@ -434,6 +436,17 @@ void write( */ List listAcls(String bucket, String object, Long generation); + HmacKey createHmacKey(String serviceAccountEmail); + + Tuple> listHmacKeys( + String serviceAccountEmail, String pageToken, Long maxResults); + + HmacKeyMetadata updateHmacKey(HmacKeyMetadata hmacKeyMetadata); + + HmacKeyMetadata getHmacKey(String accessId); + + void deleteHmacKey(String accessId); + /** * Returns the IAM policy for the specified bucket. * diff --git a/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java b/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java index 8b2d627b23db..11b7892cf1bc 100644 --- a/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java +++ b/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java @@ -64,6 +64,7 @@ import com.google.cloud.storage.BucketInfo.LifecycleRule.LifecycleAction; import com.google.cloud.storage.BucketInfo.LifecycleRule.LifecycleCondition; import com.google.cloud.storage.CopyWriter; +import com.google.cloud.storage.HmacKey; import com.google.cloud.storage.HttpMethod; import com.google.cloud.storage.ServiceAccount; import com.google.cloud.storage.Storage; @@ -2108,6 +2109,83 @@ public void testBlobAcl() { } } + @Test + public void testHmacKey() { + ServiceAccount serviceAccount = + ServiceAccount.of("it-service-account@gcloud-devel.iam.gserviceaccount.com"); + try { + + HmacKey hmacKey = storage.createHmacKey(serviceAccount); + String secretKey = hmacKey.getSecretKey(); + assertNotNull(secretKey); + HmacKey.HmacKeyMetadata metadata = hmacKey.getMetadata(); + String accessId = metadata.getAccessId(); + + assertNotNull(accessId); + assertNotNull(metadata.getEtag()); + assertNotNull(metadata.getId()); + assertEquals(remoteStorageHelper.getOptions().getProjectId(), metadata.getProjectId()); + assertEquals(serviceAccount.getEmail(), metadata.getServiceAccount().getEmail()); + assertEquals(HmacKey.HmacKeyState.ACTIVE, metadata.getState()); + assertNotNull(metadata.getCreateTime()); + assertNotNull(metadata.getUpdateTime()); + + Page metadatas = storage.listHmacKeys(serviceAccount); + boolean createdHmacKeyIsInList = false; + for (HmacKey.HmacKeyMetadata hmacKeyMetadata : metadatas.iterateAll()) { + if (accessId.equals(hmacKeyMetadata.getAccessId())) { + createdHmacKeyIsInList = true; + break; + } + } + + if (!createdHmacKeyIsInList) { + fail("Created an HMAC key but it didn't show up in list()"); + } + + HmacKey.HmacKeyMetadata getResult = storage.getHmacKey(accessId); + assertEquals(metadata, getResult); + + storage.updateHmacKeyState(metadata, HmacKey.HmacKeyState.INACTIVE); + + storage.deleteHmacKey(accessId); + + metadatas = storage.listHmacKeys(serviceAccount); + createdHmacKeyIsInList = false; + for (HmacKey.HmacKeyMetadata hmacKeyMetadata : metadatas.iterateAll()) { + if (accessId.equals(hmacKeyMetadata.getAccessId())) { + createdHmacKeyIsInList = true; + break; + } + } + + if (createdHmacKeyIsInList) { + fail("Deleted an HMAC key but it showed up in list()"); + } + + storage.createHmacKey(serviceAccount); + storage.createHmacKey(serviceAccount); + storage.createHmacKey(serviceAccount); + storage.createHmacKey(serviceAccount); + + metadatas = storage.listHmacKeys(serviceAccount, null, 2L); + + String nextPageToken = metadatas.getNextPageToken(); + + assertEquals(2, Iterators.size(metadatas.getValues().iterator())); + + metadatas = storage.listHmacKeys(serviceAccount, nextPageToken, 2L); + + assertEquals(2, Iterators.size(metadatas.getValues().iterator())); + } finally { + Page metadatas = storage.listHmacKeys(serviceAccount); + for (HmacKey.HmacKeyMetadata hmacKeyMetadata : metadatas.iterateAll()) { + storage.updateHmacKeyState(hmacKeyMetadata, HmacKey.HmacKeyState.INACTIVE); + storage.deleteHmacKey(hmacKeyMetadata.getAccessId()); + } + } + } + @Test public void testReadCompressedBlob() throws IOException { String blobName = "test-read-compressed-blob";