Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signURL functionality #77

Merged
merged 10 commits into from
May 29, 2015
4 changes: 2 additions & 2 deletions gcloud-java-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client</artifactId>
<version>1.19.0</version>
<version>1.20.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client</artifactId>
<version>1.19.0</version>
<version>1.20.0</version>
<scope>compile</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private Object readResolve() throws ObjectStreamException {
}
}

private static class ServiceAccountAuthCredentials extends AuthCredentials {
public static class ServiceAccountAuthCredentials extends AuthCredentials {

private static final long serialVersionUID = 8007708734318445901L;
private final String account;
Expand Down Expand Up @@ -94,6 +94,14 @@ protected HttpRequestInitializer httpRequestInitializer(
return builder.build();
}

public String account() {
return account;
}

public PrivateKey privateKey() {
return privateKey;
}

@Override
public int hashCode() {
return Objects.hash(account, privateKey);
Expand Down Expand Up @@ -187,7 +195,7 @@ public static AuthCredentials createApplicationDefaults() throws IOException {
return new ApplicationDefaultAuthCredentials();
}

public static AuthCredentials createFor(String account, PrivateKey privateKey) {
public static ServiceAccountAuthCredentials createFor(String account, PrivateKey privateKey) {
return new ServiceAccountAuthCredentials(account, privateKey);
}

Expand Down
11 changes: 11 additions & 0 deletions gcloud-java-examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,15 @@
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.google.gcloud.examples;

import com.google.gcloud.AuthCredentials;
import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials;
import com.google.gcloud.RetryParams;
import com.google.gcloud.spi.StorageRpc.Tuple;
import com.google.gcloud.storage.BatchRequest;
Expand All @@ -27,6 +29,7 @@
import com.google.gcloud.storage.StorageService;
import com.google.gcloud.storage.StorageService.ComposeRequest;
import com.google.gcloud.storage.StorageService.CopyRequest;
import com.google.gcloud.storage.StorageService.SignUrlOption;
import com.google.gcloud.storage.StorageServiceFactory;
import com.google.gcloud.storage.StorageServiceOptions;

Expand All @@ -40,7 +43,14 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -58,7 +68,8 @@
* -Dexec.args="[<project_id>] list [<bucket>]| info [<bucket> [<file>]]|
* download <bucket> <path> [local_file]| upload <local_file> <bucket> [<path>]|
* delete <bucket> <path>+| cp <from_bucket> <from_path> <to_bucket> <to_path>|
* compose <bucket> <from_path>+ <to_path>| update_metadata <bucket> <file> [key=value]*"}
* compose <bucket> <from_path>+ <to_path>| update_metadata <bucket> <file> [key=value]*|
* sign_url <service_account_private_key_file> <service_account_email> <bucket> <path>"}
* </li>
* </ol>
*
Expand All @@ -75,7 +86,7 @@ private static abstract class StorageAction<T> {

abstract void run(StorageService storage, T request) throws Exception;

abstract T parse(String... args) throws IllegalArgumentException, IOException;
abstract T parse(String... args) throws Exception;

protected String params() {
return "";
Expand Down Expand Up @@ -424,7 +435,7 @@ public String params() {
*
* @see <a href="https://cloud.google.com/storage/docs/json_api/v1/objects/update">Objects: update</a>
*/
private static class UpdateMetadata extends StorageAction<Tuple<Blob, Map<String, String>>> {
private static class UpdateMetadataAction extends StorageAction<Tuple<Blob, Map<String, String>>> {

@Override
public void run(StorageService storage, Tuple<Blob, Map<String, String>> tuple)
Expand Down Expand Up @@ -467,6 +478,52 @@ public String params() {
}
}

/**
* This class demonstrates how to sign a url.
* URL will be valid for 1 day.
*
* @see <a href="https://cloud.google.com/storage/docs/access-control#Signed-URLs">Signed URLs</a>
*/
private static class SignUrlAction extends
StorageAction<Tuple<ServiceAccountAuthCredentials, Blob>> {

private static final char[] PASSWORD = "notasecret".toCharArray();

@Override
public void run(StorageService storage, Tuple<ServiceAccountAuthCredentials, Blob> tuple)
throws Exception {
run(storage, tuple.x(), tuple.y());
}

private void run(StorageService storage, ServiceAccountAuthCredentials cred, Blob blob)
throws IOException {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, 1);
long expiration = cal.getTimeInMillis() / 1000;
System.out.println("Signed URL: " +
storage.signUrl(blob, expiration, SignUrlOption.serviceAccount(cred)));
}

@Override
Tuple<ServiceAccountAuthCredentials, Blob> parse(String... args)
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException,
UnrecoverableKeyException {
if (args.length != 4) {
throw new IllegalArgumentException();
}
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(Files.newInputStream(Paths.get(args[0])), PASSWORD);
PrivateKey privateKey = (PrivateKey) keystore.getKey("privatekey", PASSWORD);
ServiceAccountAuthCredentials cred = AuthCredentials.createFor(args[1], privateKey);
return Tuple.of(cred, Blob.of(args[2], args[3]));
}

@Override
public String params() {
return "<service_account_private_key_file> <service_account_email> <bucket> <path>";
}
}

static {
ACTIONS.put("info", new InfoAction());
ACTIONS.put("delete", new DeleteAction());
Expand All @@ -475,7 +532,8 @@ public String params() {
ACTIONS.put("download", new DownloadAction());
ACTIONS.put("cp", new CopyAction());
ACTIONS.put("compose", new ComposeAction());
ACTIONS.put("update_metadata", new UpdateMetadata());
ACTIONS.put("update_metadata", new UpdateMetadataAction());
ACTIONS.put("sign_url", new SignUrlAction());
}

public static void printUsage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ public DefaultStorageRpc(StorageServiceOptions options) {
HttpRequestInitializer initializer = options.httpRequestInitializer();
this.options = options;
storage = new Storage.Builder(transport, new JacksonFactory(), initializer)
.setRootUrl(options.host())
.setApplicationName("gcloud-java")
.build();
// Todo: make sure nulls are being used as Data.asNull()
}

private static StorageServiceException translate(IOException exception) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
/* * Copyright 2015 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.gcloud.spi;import com.google.api.services.storage.model.Bucket;import com.google.api.services.storage.model.StorageObject;import com.google.common.collect.ImmutableList;import com.google.common.collect.ImmutableMap;import com.google.gcloud.storage.StorageServiceException;import java.util.List;import java.util.Map;public interface StorageRpc { enum Option { PREDEFINED_ACL("predefinedAcl"), PREDEFINED_DEFAULT_OBJECT_ACL("predefinedDefaultObjectAcl"), IF_METAGENERATION_MATCH("ifMetagenerationMatch"), IF_METAGENERATION_NOT_MATCH("ifMetagenerationNotMatch"), IF_GENERATION_NOT_MATCH("ifGenerationMatch"), IF_GENERATION_MATCH("ifGenerationNotMatch"), IF_SOURCE_METAGENERATION_MATCH("ifSourceMetagenerationMatch"), IF_SOURCE_METAGENERATION_NOT_MATCH("ifSourceMetagenerationNotMatch"), IF_SOURCE_GENERATION_MATCH("ifSourceGenerationMatch"), IF_SOURCE_GENERATION_NOT_MATCH("ifSourceGenerationNotMatch"), PREFIX("prefix"), MAX_RESULTS("maxResults"), PAGE_TOKEN("pageToken"), DELIMITER("delimiter"), VERSIONS("versions"); private final String value; Option(String value) { this.value = value; } public String value() { return value; } @SuppressWarnings("unchecked") <T> T get(Map<Option, ?> options) { return (T) options.get(this); } String getString(Map<Option, ?> options) { return get(options); } Long getLong(Map<Option, ?> options) { return get(options); } Boolean getBoolean(Map<Option, ?> options) { return get(options); } } class Tuple<X, Y> { private final X x; private final Y y; private Tuple(X x, Y y) { this.x = x; this.y = y; } public static <X, Y> Tuple<X, Y> of(X x, Y y) { return new Tuple<>(x, y); } public X x() { return x; } public Y y() { return y; } } class BatchRequest { public final List<Tuple<StorageObject, Map<Option, ?>>> toDelete; public final List<Tuple<StorageObject, Map<Option, ?>>> toUpdate; public final List<Tuple<StorageObject, Map<Option, ?>>> toGet; public BatchRequest(Iterable<Tuple<StorageObject, Map<Option, ?>>> toDelete, Iterable<Tuple<StorageObject, Map<Option, ?>>> toUpdate, Iterable<Tuple<StorageObject, Map<Option, ?>>> toGet) { this.toDelete = ImmutableList.copyOf(toDelete); this.toUpdate = ImmutableList.copyOf(toUpdate); this.toGet = ImmutableList.copyOf(toGet); } } class BatchResponse { public final Map<StorageObject, Tuple<Boolean, StorageServiceException>> deletes; public final Map<StorageObject, Tuple<StorageObject, StorageServiceException>> updates; public final Map<StorageObject, Tuple<StorageObject, StorageServiceException>> gets; public BatchResponse(Map<StorageObject, Tuple<Boolean, StorageServiceException>> deletes, Map<StorageObject, Tuple<StorageObject, StorageServiceException>> updates, Map<StorageObject, Tuple<StorageObject, StorageServiceException>> gets) { this.deletes = ImmutableMap.copyOf(deletes); this.updates = ImmutableMap.copyOf(updates); this.gets = ImmutableMap.copyOf(gets); } } Bucket create(Bucket bucket, Map<Option, ?> options) throws StorageServiceException; StorageObject create(StorageObject object, byte[] content, Map<Option, ?> options) throws StorageServiceException; Tuple<String, Iterable<Bucket>> list(Map<Option, ?> options) throws StorageServiceException; Tuple<String, Iterable<StorageObject>> list(String bucket, Map<Option, ?> options) throws StorageServiceException; Bucket get(Bucket bucket, Map<Option, ?> options) throws StorageServiceException; StorageObject get(StorageObject object, Map<Option, ?> options) throws StorageServiceException; Bucket patch(Bucket bucket, Map<Option, ?> options) throws StorageServiceException; StorageObject patch(StorageObject storageObject, Map<Option, ?> options) throws StorageServiceException; boolean delete(Bucket bucket, Map<Option, ?> options) throws StorageServiceException; boolean delete(StorageObject object, Map<Option, ?> options) throws StorageServiceException; BatchResponse batch(BatchRequest request) throws StorageServiceException; StorageObject compose(Iterable<StorageObject> sources, StorageObject target, Map<Option, ?> targetOptions) throws StorageServiceException; StorageObject copy(StorageObject source, Map<Option, ?> sourceOptions, StorageObject target, Map<Option, ?> targetOptions) throws StorageServiceException; byte[] load(StorageObject storageObject, Map<Option, ?> options) throws StorageServiceException; byte[] read(StorageObject from, Map<Option, ?> options, long position, int bytes) throws StorageServiceException; String open(StorageObject object, Map<Option, ?> options) throws StorageServiceException; void write(String uploadId, byte[] toWrite, int toWriteOffset, StorageObject dest, long destOffset, int length, boolean last) throws StorageServiceException;}
/* * Copyright 2015 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.gcloud.spi;import com.google.api.services.storage.model.Bucket;import com.google.api.services.storage.model.StorageObject;import com.google.common.collect.ImmutableList;import com.google.common.collect.ImmutableMap;import com.google.gcloud.storage.StorageServiceException;import java.util.List;import java.util.Map;public interface StorageRpc { // These options are part of the Google Cloud storage header options enum Option { PREDEFINED_ACL("predefinedAcl"), PREDEFINED_DEFAULT_OBJECT_ACL("predefinedDefaultObjectAcl"), IF_METAGENERATION_MATCH("ifMetagenerationMatch"), IF_METAGENERATION_NOT_MATCH("ifMetagenerationNotMatch"), IF_GENERATION_NOT_MATCH("ifGenerationMatch"), IF_GENERATION_MATCH("ifGenerationNotMatch"), IF_SOURCE_METAGENERATION_MATCH("ifSourceMetagenerationMatch"), IF_SOURCE_METAGENERATION_NOT_MATCH("ifSourceMetagenerationNotMatch"), IF_SOURCE_GENERATION_MATCH("ifSourceGenerationMatch"), IF_SOURCE_GENERATION_NOT_MATCH("ifSourceGenerationNotMatch"), PREFIX("prefix"), MAX_RESULTS("maxResults"), PAGE_TOKEN("pageToken"), DELIMITER("delimiter"), VERSIONS("versions"); private final String value; Option(String value) { this.value = value; } public String value() { return value; } @SuppressWarnings("unchecked") <T> T get(Map<Option, ?> options) { return (T) options.get(this); } String getString(Map<Option, ?> options) { return get(options); } Long getLong(Map<Option, ?> options) { return get(options); } Boolean getBoolean(Map<Option, ?> options) { return get(options); } } class Tuple<X, Y> { private final X x; private final Y y; private Tuple(X x, Y y) { this.x = x; this.y = y; } public static <X, Y> Tuple<X, Y> of(X x, Y y) { return new Tuple<>(x, y); } public X x() { return x; } public Y y() { return y; } } class BatchRequest { public final List<Tuple<StorageObject, Map<Option, ?>>> toDelete; public final List<Tuple<StorageObject, Map<Option, ?>>> toUpdate; public final List<Tuple<StorageObject, Map<Option, ?>>> toGet; public BatchRequest(Iterable<Tuple<StorageObject, Map<Option, ?>>> toDelete, Iterable<Tuple<StorageObject, Map<Option, ?>>> toUpdate, Iterable<Tuple<StorageObject, Map<Option, ?>>> toGet) { this.toDelete = ImmutableList.copyOf(toDelete); this.toUpdate = ImmutableList.copyOf(toUpdate); this.toGet = ImmutableList.copyOf(toGet); } } class BatchResponse { public final Map<StorageObject, Tuple<Boolean, StorageServiceException>> deletes; public final Map<StorageObject, Tuple<StorageObject, StorageServiceException>> updates; public final Map<StorageObject, Tuple<StorageObject, StorageServiceException>> gets; public BatchResponse(Map<StorageObject, Tuple<Boolean, StorageServiceException>> deletes, Map<StorageObject, Tuple<StorageObject, StorageServiceException>> updates, Map<StorageObject, Tuple<StorageObject, StorageServiceException>> gets) { this.deletes = ImmutableMap.copyOf(deletes); this.updates = ImmutableMap.copyOf(updates); this.gets = ImmutableMap.copyOf(gets); } } Bucket create(Bucket bucket, Map<Option, ?> options) throws StorageServiceException; StorageObject create(StorageObject object, byte[] content, Map<Option, ?> options) throws StorageServiceException; Tuple<String, Iterable<Bucket>> list(Map<Option, ?> options) throws StorageServiceException; Tuple<String, Iterable<StorageObject>> list(String bucket, Map<Option, ?> options) throws StorageServiceException; Bucket get(Bucket bucket, Map<Option, ?> options) throws StorageServiceException; StorageObject get(StorageObject object, Map<Option, ?> options) throws StorageServiceException; Bucket patch(Bucket bucket, Map<Option, ?> options) throws StorageServiceException; StorageObject patch(StorageObject storageObject, Map<Option, ?> options) throws StorageServiceException; boolean delete(Bucket bucket, Map<Option, ?> options) throws StorageServiceException; boolean delete(StorageObject object, Map<Option, ?> options) throws StorageServiceException; BatchResponse batch(BatchRequest request) throws StorageServiceException; StorageObject compose(Iterable<StorageObject> sources, StorageObject target, Map<Option, ?> targetOptions) throws StorageServiceException; StorageObject copy(StorageObject source, Map<Option, ?> sourceOptions, StorageObject target, Map<Option, ?> targetOptions) throws StorageServiceException; byte[] load(StorageObject storageObject, Map<Option, ?> options) throws StorageServiceException; byte[] read(StorageObject from, Map<Option, ?> options, long position, int bytes) throws StorageServiceException; String open(StorageObject object, Map<Option, ?> options) throws StorageServiceException; void write(String uploadId, byte[] toWrite, int toWriteOffset, StorageObject dest, long destOffset, int length, boolean last) throws StorageServiceException;}
Expand Down
Loading