Skip to content

Commit

Permalink
removed overload method and add method withExtHostName in SignUrlOption
Browse files Browse the repository at this point in the history
  • Loading branch information
Praful Makani committed Nov 16, 2018
1 parent 0fd3ff6 commit 5b38ded
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,7 @@ class SignUrlOption implements Serializable {
private final Object value;

enum Option {
HTTP_METHOD, CONTENT_TYPE, MD5, EXT_HEADERS, SERVICE_ACCOUNT_CRED
HTTP_METHOD, CONTENT_TYPE, MD5, EXT_HEADERS, SERVICE_ACCOUNT_CRED,EXT_HOST
}

private SignUrlOption(Option option, Object value) {
Expand Down Expand Up @@ -953,6 +953,13 @@ public static SignUrlOption withExtHeaders(Map<String, String> extHeaders) {
public static SignUrlOption signWith(ServiceAccountSigner signer) {
return new SignUrlOption(Option.SERVICE_ACCOUNT_CRED, signer);
}

/**
* Provides a host name to sign the URL. If not provided than host name will be default
*/
public static SignUrlOption withExtHostName(String extHostName){
return new SignUrlOption(Option.EXT_HOST, extHostName);
}
}

/**
Expand Down Expand Up @@ -2107,6 +2114,7 @@ public static Builder newBuilder() {
* granularity supported is 1 second, finer granularities will be truncated
* @param unit time unit of the {@code duration} parameter
* @param options optional URL signing options
* {@code SignUrlOption.withExtHostName()} option is used for external host name of signed url
* @throws IllegalStateException if {@link SignUrlOption#signWith(ServiceAccountSigner)} was not
* used and no implementation of {@link ServiceAccountSigner} was provided to
* {@link StorageOptions}
Expand All @@ -2119,70 +2127,6 @@ public static Builder newBuilder() {
*/
URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options);

/**
* Generates a signed URL for a blob. If you have a blob that you want to allow access to for a
* fixed amount of time, you can use this method to generate a URL that is only valid within a
* certain time period. This is particularly useful if you don't want publicly accessible blobs,
* but also don't want to require users to explicitly log in. Signing a URL requires
* a service account signer. If an instance of {@link com.google.auth.ServiceAccountSigner} was
* passed to {@link StorageOptions}' builder via {@code setCredentials(Credentials)} or the
* default credentials are being used and the environment variable
* {@code GOOGLE_APPLICATION_CREDENTIALS} is set or your application is running in App Engine,
* then {@code signUrl} will use that credentials to sign the URL. If the credentials passed to
* {@link StorageOptions} do not implement {@link ServiceAccountSigner} (this is the case, for
* instance, for Google Cloud SDK credentials) then {@code signUrl} will throw an
* {@link IllegalStateException} unless an implementation of {@link ServiceAccountSigner} is
* passed using the {@link SignUrlOption#signWith(ServiceAccountSigner)} option.
*
* <p>A service account signer is looked for in the following order:
* <ol>
* <li>The signer passed with the option {@link SignUrlOption#signWith(ServiceAccountSigner)}
* <li>The credentials passed to {@link StorageOptions}
* <li>The default credentials, if no credentials were passed to {@link StorageOptions}
* </ol>
*
* <p>Example of creating a signed URL that is valid for 2 weeks, using the default credentials
* for signing the URL.
* <pre> {@code
* String bucketName = "my_unique_bucket";
* String blobName = "my_blob_name";
* URL signedUrl = storage.signUrl(BlobInfo.newBuilder(bucketName, blobName).build(), 14,
* TimeUnit.DAYS);
* }</pre>
*
* <p>Example of creating a signed URL passing the
* {@link SignUrlOption#signWith(ServiceAccountSigner)} option, that will be used for signing the
* URL.
* <pre> {@code
* String bucketName = "my_unique_bucket";
* String blobName = "my_blob_name";
* String keyPath = "/path/to/key.json";
* URL signedUrl = storage.signUrl(BlobInfo.newBuilder(bucketName, blobName).build(),
* 14, TimeUnit.DAYS, SignUrlOption.signWith(
* ServiceAccountCredentials.fromStream(new FileInputStream(keyPath))));
* }</pre>
*
* <p>Note that the {@link ServiceAccountSigner} may require additional configuration to enable
* URL signing. See the documentation for the implementation for more details.</p>
*
* @param url can be customize
* @param blobInfo the blob associated with the signed URL
* @param duration time until the signed URL expires, expressed in {@code unit}. The finest
* granularity supported is 1 second, finer granularities will be truncated
* @param unit time unit of the {@code duration} parameter
* @param options optional URL signing options
* @throws IllegalStateException if {@link SignUrlOption#signWith(ServiceAccountSigner)} was not
* used and no implementation of {@link ServiceAccountSigner} was provided to
* {@link StorageOptions}
* @throws IllegalArgumentException if {@code SignUrlOption.withMd5()} option is used and
* {@code blobInfo.md5()} is {@code null}
* @throws IllegalArgumentException if {@code SignUrlOption.withContentType()} option is used and
* {@code blobInfo.contentType()} is {@code null}
* @throws SigningException if the attempt to sign the URL failed
* @see <a href="https://cloud.google.com/storage/docs/access-control#Signed-URLs">Signed-URLs</a>
*/
URL signUrl(String url, BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options);

/**
* Gets the requested blobs. A batch request is used to perform this call.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@
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.URI;
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.gax.paging.Page;
import com.google.api.services.storage.model.BucketAccessControl;
import com.google.api.services.storage.model.ObjectAccessControl;
Expand Down Expand Up @@ -62,21 +78,6 @@
import com.google.common.io.BaseEncoding;
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.URI;
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;

final class StorageImpl extends BaseService<StorageOptions> implements Storage {

Expand Down Expand Up @@ -500,52 +501,50 @@ private BlobWriteChannel writer(BlobInfo blobInfo, BlobTargetOption... options)

@Override
public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options) {
return signUrlOptions(DEFAULT_STORAGE_HOST, blobInfo, duration, unit, options);
}

@Override
public URL signUrl(String url, BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options) {
return signUrlOptions(url, blobInfo, duration, unit, options);
}

private URL signUrlOptions(String url, BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options){
EnumMap<SignUrlOption.Option, Object> optionMap = Maps.newEnumMap(SignUrlOption.Option.class);
for (SignUrlOption option : options) {
optionMap.put(option.getOption(), option.getValue());
}
ServiceAccountSigner credentials =
(ServiceAccountSigner) optionMap.get(SignUrlOption.Option.SERVICE_ACCOUNT_CRED);
if (credentials == null) {
checkState(this.getOptions().getCredentials() instanceof ServiceAccountSigner,
"Signing key was not provided and could not be derived");
credentials = (ServiceAccountSigner) this.getOptions().getCredentials();
}

long expiration = TimeUnit.SECONDS.convert(
getOptions().getClock().millisTime() + unit.toMillis(duration), TimeUnit.MILLISECONDS);
for (SignUrlOption option : options) {
optionMap.put(option.getOption(), option.getValue());
}
ServiceAccountSigner credentials =
(ServiceAccountSigner) optionMap.get(SignUrlOption.Option.SERVICE_ACCOUNT_CRED);
if (credentials == null) {
checkState(this.getOptions().getCredentials() instanceof ServiceAccountSigner,
"Signing key was not provided and could not be derived");
credentials = (ServiceAccountSigner) this.getOptions().getCredentials();
}

StringBuilder stPath = new StringBuilder();
if (!blobInfo.getBucket().startsWith(PATH_DELIMITER)) {
stPath.append(PATH_DELIMITER);
}
stPath.append(blobInfo.getBucket());
if (!blobInfo.getBucket().endsWith(PATH_DELIMITER)) {
stPath.append(PATH_DELIMITER);
}
if (blobInfo.getName().startsWith(PATH_DELIMITER)) {
stPath.setLength(stPath.length() - 1);
}
long expiration = TimeUnit.SECONDS.convert(
getOptions().getClock().millisTime() + unit.toMillis(duration), TimeUnit.MILLISECONDS);

String escapedName = UrlEscapers.urlFragmentEscaper().escape(blobInfo.getName());
stPath.append(escapedName.replace("?", "%3F"));
StringBuilder stPath = new StringBuilder();
if (!blobInfo.getBucket().startsWith(PATH_DELIMITER)) {
stPath.append(PATH_DELIMITER);
}
stPath.append(blobInfo.getBucket());
if (!blobInfo.getBucket().endsWith(PATH_DELIMITER)) {
stPath.append(PATH_DELIMITER);
}
if (blobInfo.getName().startsWith(PATH_DELIMITER)) {
stPath.setLength(stPath.length() - 1);
}

URI path = URI.create(stPath.toString());
String escapedName = UrlEscapers.urlFragmentEscaper().escape(blobInfo.getName());
stPath.append(escapedName.replace("?", "%3F"));

URI path = URI.create(stPath.toString());

try {
try {
SignatureInfo signatureInfo = buildSignatureInfo(optionMap, blobInfo, expiration, path);
byte[] signatureBytes =
credentials.sign(signatureInfo.constructUnsignedPayload().getBytes(UTF_8));
StringBuilder stBuilder = new StringBuilder(url).append(path);
StringBuilder stBuilder = new StringBuilder();
if(optionMap.get(SignUrlOption.Option.EXT_HOST) == null){
stBuilder.append(DEFAULT_STORAGE_HOST).append(path);
}
else{
stBuilder.append(optionMap.get(SignUrlOption.Option.EXT_HOST).toString()).append(path);
}

String signature =
URLEncoder.encode(BaseEncoding.base64().encode(signatureBytes), UTF_8.name());
stBuilder.append("?GoogleAccessId=").append(credentials.getAccount());
Expand All @@ -556,7 +555,7 @@ private URL signUrlOptions(String url, BlobInfo blobInfo, long duration, TimeUni

} catch (MalformedURLException | UnsupportedEncodingException ex) {
throw new IllegalStateException(ex);
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1645,7 +1645,7 @@ public void testSignUrlWithCustomUrl()
ServiceAccountCredentials credentials =
new ServiceAccountCredentials(null, ACCOUNT, privateKey, null, null);
storage = options.toBuilder().setCredentials(credentials).build().getService();
URL url = storage.signUrl("https://custom.host.com",BLOB_INFO1, 14, TimeUnit.DAYS);
URL url = storage.signUrl(BLOB_INFO1, 14, TimeUnit.DAYS, Storage.SignUrlOption.withExtHostName("https://custom.host.com"));
String stringUrl = url.toString();
String expectedUrl =
new StringBuilder("https://custom.host.com/")
Expand Down Expand Up @@ -1730,7 +1730,7 @@ public void testSignUrlLeadingSlashWithCustomUrl()
new ServiceAccountCredentials(null, ACCOUNT, privateKey, null, null);
storage = options.toBuilder().setCredentials(credentials).build().getService();
URL url =
storage.signUrl("https://custom.host.com",BlobInfo.newBuilder(BUCKET_NAME1, blobName).build(), 14, TimeUnit.DAYS);
storage.signUrl(BlobInfo.newBuilder(BUCKET_NAME1, blobName).build(), 14, TimeUnit.DAYS, Storage.SignUrlOption.withExtHostName("https://custom.host.com"));
String escapedBlobName = UrlEscapers.urlFragmentEscaper().escape(blobName);
String stringUrl = url.toString();
String expectedUrl =
Expand Down Expand Up @@ -1824,13 +1824,13 @@ public void testSignUrlWithOptionsAndCustomUrl()
storage = options.toBuilder().setCredentials(credentials).build().getService();
URL url =
storage.signUrl(
"https://custom.host.com",
BLOB_INFO1,
14,
TimeUnit.DAYS,
Storage.SignUrlOption.httpMethod(HttpMethod.POST),
Storage.SignUrlOption.withContentType(),
Storage.SignUrlOption.withMd5());
Storage.SignUrlOption.withMd5(),
Storage.SignUrlOption.withExtHostName("https://custom.host.com"));
String stringUrl = url.toString();
String expectedUrl =
new StringBuilder("https://custom.host.com/")
Expand Down Expand Up @@ -1937,7 +1937,7 @@ public void testSignUrlForBlobWithSpecialCharsAndCustomUrl()
for (char specialChar : specialChars) {
String blobName = "/a" + specialChar + "b";
URL url =
storage.signUrl("https://custom.host.com",BlobInfo.newBuilder(BUCKET_NAME1, blobName).build(), 14, TimeUnit.DAYS);
storage.signUrl(BlobInfo.newBuilder(BUCKET_NAME1, blobName).build(), 14, TimeUnit.DAYS, Storage.SignUrlOption.withExtHostName("https://custom.host.com"));
String escapedBlobName =
UrlEscapers.urlFragmentEscaper().escape(blobName).replace("?", "%3F");
String stringUrl = url.toString();
Expand Down Expand Up @@ -2041,13 +2041,13 @@ public void testSignUrlWithExtHeadersAndCustomUrl()
extHeaders.put("x-goog-meta-owner", "myself");
URL url =
storage.signUrl(
"https://custom.host.com",
BLOB_INFO1,
14,
TimeUnit.DAYS,
Storage.SignUrlOption.httpMethod(HttpMethod.PUT),
Storage.SignUrlOption.withContentType(),
Storage.SignUrlOption.withExtHeaders(extHeaders));
Storage.SignUrlOption.withExtHeaders(extHeaders),
Storage.SignUrlOption.withExtHostName("https://custom.host.com"));
String stringUrl = url.toString();
String expectedUrl =
new StringBuilder("https://custom.host.com/")
Expand Down Expand Up @@ -2140,7 +2140,7 @@ public void testSignUrlForBlobWithSlashesAndCustomUrl()

String blobName = "/foo/bar/baz #%20other cool stuff.txt";
URL url =
storage.signUrl("https://custom.host.com", BlobInfo.newBuilder(BUCKET_NAME1, blobName).build(), 14, TimeUnit.DAYS);
storage.signUrl(BlobInfo.newBuilder(BUCKET_NAME1, blobName).build(), 14, TimeUnit.DAYS, Storage.SignUrlOption.withExtHostName("https://custom.host.com"));
String escapedBlobName = UrlEscapers.urlFragmentEscaper().escape(blobName);
String stringUrl = url.toString();
String expectedUrl =
Expand Down

0 comments on commit 5b38ded

Please sign in to comment.