You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ad...@apache.org on 2013/09/30 01:30:04 UTC
git commit: JCLOUDS-308. Add Temporary Url Support to openstack-swift
Updated Branches:
refs/heads/master cc1e2ae4d -> fd3f8cd01
JCLOUDS-308. Add Temporary Url Support to openstack-swift
Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/commit/fd3f8cd0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/tree/fd3f8cd0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/diff/fd3f8cd0
Branch: refs/heads/master
Commit: fd3f8cd01991363db91f90bb7f22133af1988061
Parents: cc1e2ae
Author: Adrian Cole <ad...@gmail.com>
Authored: Sun Sep 29 15:42:46 2013 -0700
Committer: Adrian Cole <ad...@gmail.com>
Committed: Sun Sep 29 16:27:41 2013 -0700
----------------------------------------------------------------------
.../openstack/swift/v1/TemporaryUrlSigner.java | 92 ++++++++++++++++++++
.../openstack/swift/v1/domain/Account.java | 5 ++
.../openstack/swift/v1/features/AccountApi.java | 20 +++++
.../swift/v1/TemporaryUrlSignerLiveTest.java | 91 +++++++++++++++++++
.../swift/v1/TemporaryUrlSignerMockTest.java | 75 ++++++++++++++++
.../swift/v1/features/AccountApiMockTest.java | 20 +++++
6 files changed, 303 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/fd3f8cd0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/TemporaryUrlSigner.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/TemporaryUrlSigner.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/TemporaryUrlSigner.java
new file mode 100644
index 0000000..35f2b12
--- /dev/null
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/TemporaryUrlSigner.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2013 Netflix, Inc.
+ *
+ * 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 org.jclouds.openstack.swift.v1;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Suppliers.memoizeWithExpiration;
+import static com.google.common.base.Throwables.propagate;
+import static com.google.common.io.BaseEncoding.base16;
+import static java.lang.String.format;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.jclouds.openstack.swift.v1.features.AccountApi;
+
+import com.google.common.base.Supplier;
+
+/**
+ * Use this utility to create temporary urls.
+ *
+ * @see <a
+ * href="http://docs.openstack.org/trunk/config-reference/content/object-storage-tempurl.html">Temporary
+ * URL Documentation</a>
+ */
+public class TemporaryUrlSigner {
+
+ public static TemporaryUrlSigner checkApiEvery(final AccountApi api, long seconds) {
+ Supplier<String> keySupplier = memoizeWithExpiration(new TemporaryUrlKeyFromAccount(api), seconds, SECONDS);
+ return new TemporaryUrlSigner(keySupplier);
+ }
+
+ private final Supplier<String> keySupplier;
+
+ TemporaryUrlSigner(Supplier<String> keySupplier) {
+ this.keySupplier = keySupplier;
+ }
+
+ public String sign(String method, String path, long expirationTimestampSeconds) {
+ checkNotNull(method, "method");
+ checkNotNull(path, "path");
+ checkArgument(expirationTimestampSeconds > 0, "expirationTimestamp must be a unix epoch timestamp");
+ String hmacBody = format("%s\n%s\n%s", method, expirationTimestampSeconds, path);
+ return base16().lowerCase().encode(hmacSHA1(hmacBody));
+ }
+
+ byte[] hmacSHA1(String data) {
+ try {
+ String key = keySupplier.get();
+ checkState(key != null, "%s returned a null temporaryUrlKey!", keySupplier);
+ Mac mac = Mac.getInstance("HmacSHA1");
+ mac.init(new SecretKeySpec(key.getBytes(UTF_8), "HmacSHA1"));
+ return mac.doFinal(data.getBytes(UTF_8));
+ } catch (Exception e) {
+ throw propagate(e);
+ }
+ }
+
+ static class TemporaryUrlKeyFromAccount implements Supplier<String> {
+ private final AccountApi api;
+
+ private TemporaryUrlKeyFromAccount(AccountApi api) {
+ this.api = checkNotNull(api, "accountApi");
+ }
+
+ @Override
+ public String get() {
+ return api.get().temporaryUrlKey().orNull();
+ }
+
+ @Override
+ public String toString() {
+ return format("get().temporaryUrlKey() using %s", api);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/fd3f8cd0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Account.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Account.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Account.java
index 304f329..a3857f0 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Account.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Account.java
@@ -25,6 +25,7 @@ import java.util.Map.Entry;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
/**
@@ -59,6 +60,10 @@ public class Account {
return bytesUsed;
}
+ public Optional<String> temporaryUrlKey() {
+ return Optional.fromNullable(metadata.get("temp-url-key"));
+ }
+
/**
* In current swift implementations, headers keys are lower-cased. This means
* characters such as turkish will probably not work out well.
http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/fd3f8cd0/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountApi.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountApi.java b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountApi.java
index 5e0bc6d..9736112 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountApi.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/AccountApi.java
@@ -23,6 +23,7 @@ import java.util.Map;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.HEAD;
+import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@@ -88,6 +89,25 @@ public interface AccountApi {
boolean updateMetadata(@BinderParam(BindAccountMetadataToHeaders.class) Map<String, String> metadata);
/**
+ * Replaces the {@link Account#temporaryUrlKey()}.
+ *
+ * @param metadata
+ * the Account metadata to create or update.
+ *
+ * @see <a
+ * href="http://docs.openstack.org/trunk/config-reference/content/object-storage-tempurl.html">
+ * Temporary URL Documentation</a>
+ *
+ * @return <code>true</code> if the Temporary URL Key was successfully created
+ * or updated, false if not.
+ */
+ @Named("UpdateAccountMetadata")
+ @POST
+ @Fallback(FalseOnNotFoundOr404.class)
+ @Path("/")
+ boolean updateTemporaryUrlKey(@HeaderParam("X-Account-Meta-Temp-URL-Key") String temporaryUrlKey);
+
+ /**
* Deletes Account metadata.
*
* @param metadata
http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/fd3f8cd0/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerLiveTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerLiveTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerLiveTest.java
new file mode 100644
index 0000000..f2ba78f
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerLiveTest.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.jclouds.openstack.swift.v1;
+
+import static java.lang.String.format;
+import static org.jclouds.io.Payloads.newStringPayload;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.UUID;
+
+import org.jclouds.openstack.swift.v1.domain.SwiftObject;
+import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
+import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
+import org.jclouds.util.Strings2;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+@Test(groups = "live", testName = "TemporaryUrlSignerLiveTest")
+public class TemporaryUrlSignerLiveTest extends BaseSwiftApiLiveTest {
+
+ private String name = getClass().getSimpleName();
+ private String containerName = getClass().getSimpleName() + "Container";
+
+ public void signForPublicAccess() throws Exception {
+ for (String regionId : api.configuredRegions()) {
+ SwiftObject object = api.objectApiInRegionForContainer(regionId, containerName).head(name);
+
+ long expires = System.currentTimeMillis() / 1000 + 5;
+ String signature = TemporaryUrlSigner.checkApiEvery(api.accountApiInRegion(regionId), 5) //
+ .sign("GET", object.uri().getPath(), expires);
+
+ URI signed = URI.create(format("%s?temp_url_sig=%s&temp_url_expires=%s", object.uri(), signature, expires));
+
+ InputStream publicStream = signed.toURL().openStream();
+ assertEquals(Strings2.toStringAndClose(publicStream), "swifty");
+
+ // let it expire
+ Thread.sleep(5000);
+ try {
+ signed.toURL().openStream();
+ fail("should have expired!");
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ @Override
+ @BeforeClass(groups = "live")
+ public void setup() {
+ super.setup();
+ String key = UUID.randomUUID().toString();
+ for (String regionId : api.configuredRegions()) {
+ api.accountApiInRegion(regionId).updateTemporaryUrlKey(key);
+ api.containerApiInRegion(regionId).createIfAbsent(containerName, new CreateContainerOptions());
+ api.objectApiInRegionForContainer(regionId, containerName) //
+ .replace(name, newStringPayload("swifty"), ImmutableMap.<String, String> of());
+ }
+ }
+
+ @AfterMethod
+ @AfterClass(groups = "live")
+ public void tearDown() {
+ for (String regionId : api.configuredRegions()) {
+ api.objectApiInRegionForContainer(regionId, containerName).delete(name);
+ api.containerApiInRegion(regionId).deleteIfEmpty(containerName);
+ }
+ super.tearDown();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/fd3f8cd0/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerMockTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerMockTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerMockTest.java
new file mode 100644
index 0000000..c3711a8
--- /dev/null
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TemporaryUrlSignerMockTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.jclouds.openstack.swift.v1;
+
+import static org.jclouds.openstack.swift.v1.features.AccountApiMockTest.accountResponse;
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.openstack.swift.v1.internal.BaseSwiftMockTest;
+import org.testng.annotations.Test;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+
+@Test
+public class TemporaryUrlSignerMockTest extends BaseSwiftMockTest {
+
+ @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "accountApi")
+ public void whenAccountApiIsNull() {
+ TemporaryUrlSigner.checkApiEvery(null, 10000);
+ }
+
+ public void whenAccountApiHasKey() throws Exception {
+ MockWebServer server = mockSwiftServer();
+ server.enqueue(new MockResponse().setBody(access));
+ server.enqueue(accountResponse().addHeader("X-Account-Meta-Temp-URL-Key", "mykey"));
+
+ try {
+ SwiftApi api = swiftApi(server.getUrl("/").toString());
+ String signature = TemporaryUrlSigner.checkApiEvery(api.accountApiInRegion("DFW"), 10000)
+ .sign("GET", "/v1/AUTH_account/container/object", 1323479485l);
+
+ assertEquals(signature, "d9fc2067e52b06598421664cf6610bfc8fc431f6");
+
+ assertEquals(server.getRequestCount(), 2);
+ assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
+ assertEquals(server.takeRequest().getRequestLine(),
+ "HEAD /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/ HTTP/1.1");
+ } finally {
+ server.shutdown();
+ }
+ }
+
+ @Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = ".*returned a null temporaryUrlKey!")
+ public void whenAccountApiDoesntHaveKey() throws Exception {
+ MockWebServer server = mockSwiftServer();
+ server.enqueue(new MockResponse().setBody(access));
+ server.enqueue(accountResponse());
+
+ try {
+ SwiftApi api = swiftApi(server.getUrl("/").toString());
+ TemporaryUrlSigner.checkApiEvery(api.accountApiInRegion("DFW"), 10000)
+ .sign("GET","/v1/AUTH_account/container/object", 1323479485l);
+ } finally {
+ assertEquals(server.getRequestCount(), 2);
+ assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
+ assertEquals(server.takeRequest().getRequestLine(),
+ "HEAD /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/ HTTP/1.1");
+ server.shutdown();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/fd3f8cd0/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiMockTest.java
----------------------------------------------------------------------
diff --git a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiMockTest.java b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiMockTest.java
index c2a37c3..d4f7eda 100644
--- a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiMockTest.java
+++ b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/AccountApiMockTest.java
@@ -90,6 +90,26 @@ public class AccountApiMockTest extends BaseSwiftMockTest {
}
}
+ public void updateTemporaryUrlKey() throws Exception {
+ MockWebServer server = mockSwiftServer();
+ server.enqueue(new MockResponse().setBody(access));
+ server.enqueue(accountResponse());
+
+ try {
+ SwiftApi api = swiftApi(server.getUrl("/").toString());
+ assertTrue(api.accountApiInRegion("DFW").updateTemporaryUrlKey("foobar"));
+
+ assertEquals(server.getRequestCount(), 2);
+ assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
+ RecordedRequest replaceRequest = server.takeRequest();
+ assertEquals(replaceRequest.getRequestLine(),
+ "POST /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/ HTTP/1.1");
+ assertEquals(replaceRequest.getHeader("X-Account-Meta-Temp-URL-Key"), "foobar");
+ } finally {
+ server.shutdown();
+ }
+ }
+
public void deleteMetadata() throws Exception {
MockWebServer server = mockSwiftServer();
server.enqueue(new MockResponse().setBody(access));