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 2014/11/27 02:19:56 UTC
jclouds git commit: JCLOUDS-523 add tempAuthCredentials to
openstack-swift
Repository: jclouds
Updated Branches:
refs/heads/master a449b24e7 -> df4323b4c
JCLOUDS-523 add tempAuthCredentials to openstack-swift
Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/df4323b4
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/df4323b4
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/df4323b4
Branch: refs/heads/master
Commit: df4323b4c69e222abc3f537c3b4e6df52441eb0f
Parents: a449b24
Author: Adrian Cole <ac...@twitter.com>
Authored: Wed Nov 26 10:13:57 2014 -0800
Committer: Adrian Cole <ad...@apache.org>
Committed: Wed Nov 26 17:19:38 2014 -0800
----------------------------------------------------------------------
.../openstack/swift/v1/SwiftApiMetadata.java | 12 +-
.../v1/config/SwiftAuthenticationModule.java | 160 +++++++++++++++++++
.../openstack/swift/v1/TempAuthMockTest.java | 97 +++++++++++
3 files changed, 261 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds/blob/df4323b4/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
index 8d77bb0..478bd81 100644
--- a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApiMetadata.java
@@ -23,13 +23,12 @@ import static org.jclouds.reflect.Reflection2.typeToken;
import java.net.URI;
import java.util.Properties;
-import org.jclouds.openstack.keystone.v2_0.config.AuthenticationApiModule;
import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
-import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.RegionModule;
import org.jclouds.openstack.swift.v1.blobstore.RegionScopedBlobStoreContext;
import org.jclouds.openstack.swift.v1.blobstore.config.SignUsingTemporaryUrls;
import org.jclouds.openstack.swift.v1.blobstore.config.SwiftBlobStoreContextModule;
+import org.jclouds.openstack.swift.v1.config.SwiftAuthenticationModule;
import org.jclouds.openstack.swift.v1.config.SwiftHttpApiModule;
import org.jclouds.openstack.swift.v1.config.SwiftTypeAdapters;
import org.jclouds.openstack.v2_0.ServiceType;
@@ -38,9 +37,6 @@ import org.jclouds.rest.internal.BaseHttpApiMetadata;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
-/**
- * Implementation of {@link ApiMetadata} for the Swift API.
- */
public class SwiftApiMetadata extends BaseHttpApiMetadata<SwiftApi> {
@Override
@@ -59,6 +55,7 @@ public class SwiftApiMetadata extends BaseHttpApiMetadata<SwiftApi> {
public static Properties defaultProperties() {
Properties properties = BaseHttpApiMetadata.defaultProperties();
properties.setProperty(SERVICE_TYPE, ServiceType.OBJECT_STORE);
+ // Can alternatively be set to "tempAuthCredentials"
properties.setProperty(CREDENTIAL_TYPE, CredentialTypes.PASSWORD_CREDENTIALS);
return properties;
}
@@ -72,13 +69,12 @@ public class SwiftApiMetadata extends BaseHttpApiMetadata<SwiftApi> {
.credentialName("${password}")
.documentation(URI.create("http://docs.openstack.org/api/openstack-object-storage/1.0/content/ch_object-storage-dev-overview.html"))
.version("1")
- .endpointName("Keystone base url ending in /v2.0/")
+ .endpointName("Keystone base url ending in /v2.0/ or TempAuth url ending in auth/v1.0/")
.defaultEndpoint("http://localhost:5000/v2.0/")
.defaultProperties(SwiftApiMetadata.defaultProperties())
.view(typeToken(RegionScopedBlobStoreContext.class))
.defaultModules(ImmutableSet.<Class<? extends Module>>builder()
- .add(AuthenticationApiModule.class)
- .add(KeystoneAuthenticationModule.class)
+ .add(SwiftAuthenticationModule.class)
.add(RegionModule.class)
.add(SwiftTypeAdapters.class)
.add(SwiftHttpApiModule.class)
http://git-wip-us.apache.org/repos/asf/jclouds/blob/df4323b4/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftAuthenticationModule.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftAuthenticationModule.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftAuthenticationModule.java
new file mode 100644
index 0000000..9b6ae40
--- /dev/null
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/config/SwiftAuthenticationModule.java
@@ -0,0 +1,160 @@
+/*
+ * 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.config;
+
+import static org.jclouds.http.HttpUtils.releasePayload;
+import static org.jclouds.http.Uris.uriBuilder;
+import static org.jclouds.openstack.v2_0.ServiceType.OBJECT_STORE;
+import static org.jclouds.openstack.v2_0.reference.AuthHeaders.AUTH_TOKEN;
+import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
+
+import java.io.Closeable;
+import java.net.URI;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.openstack.keystone.v2_0.AuthenticationApi;
+import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
+import org.jclouds.openstack.keystone.v2_0.domain.Access;
+import org.jclouds.openstack.keystone.v2_0.domain.Endpoint;
+import org.jclouds.openstack.keystone.v2_0.domain.Service;
+import org.jclouds.openstack.keystone.v2_0.domain.Token;
+import org.jclouds.openstack.keystone.v2_0.domain.User;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.InvocationContext;
+import org.jclouds.rest.annotations.ApiVersion;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.VirtualHost;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Injector;
+import com.google.inject.name.Named;
+
+/**
+ * When {@link org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties#CREDENTIAL_TYPE} is set to {@code
+ * tempAuthCredentials}, do not use Keystone. Instead, bridge TempAuth to Keystone by faking a service catalog out of
+ * the storage url. The {@link ContextBuilder#endpoint(String) endpoint} must be set to the TempAuth url, usually ending
+ * in {@code auth/v1.0/}.
+ */
+public final class SwiftAuthenticationModule extends KeystoneAuthenticationModule {
+ private static final String STORAGE_USER = "X-Storage-User";
+ private static final String STORAGE_PASS = "X-Storage-Pass";
+ private static final String STORAGE_URL = "X-Storage-Url";
+
+ @Override
+ protected void configure() {
+ super.configure();
+ bindHttpApi(binder(), AuthenticationApi.class);
+ bindHttpApi(binder(), TempAuthApi.class);
+ }
+
+ @Override protected Map<String, Function<Credentials, Access>> authenticationMethods(Injector i) {
+ return ImmutableMap.<String, Function<Credentials, Access>>builder()
+ .putAll(super.authenticationMethods(i))
+ .put("tempAuthCredentials", i.getInstance(TempAuth.class)).build();
+ }
+
+ static final class TempAuth implements Function<Credentials, Access> {
+ private final TempAuthApi delegate;
+
+ @Inject TempAuth(TempAuthApi delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override public Access apply(Credentials input) {
+ return delegate.auth(input.identity, input.credential);
+ }
+ }
+
+ @VirtualHost
+ interface TempAuthApi extends Closeable {
+
+ @Named("TempAuth")
+ @GET
+ @Consumes
+ @ResponseParser(AdaptTempAuthResponseToAccess.class)
+ Access auth(@HeaderParam(STORAGE_USER) String user, @HeaderParam(STORAGE_PASS) String key);
+ }
+
+ static final class AdaptTempAuthResponseToAccess
+ implements Function<HttpResponse, Access>, InvocationContext<AdaptTempAuthResponseToAccess> {
+
+ private final String apiVersion;
+
+ private String host;
+ private String username;
+
+ @Inject AdaptTempAuthResponseToAccess(@ApiVersion String apiVersion) {
+ this.apiVersion = apiVersion;
+ }
+
+ @Override public Access apply(HttpResponse from) {
+ releasePayload(from);
+ URI storageUrl = null;
+ String authToken = null;
+ for (Map.Entry<String, String> entry : from.getHeaders().entries()) {
+ String header = entry.getKey();
+ if (header.equalsIgnoreCase(STORAGE_URL)) {
+ storageUrl = getURI(entry.getValue());
+ } else if (header.equalsIgnoreCase(AUTH_TOKEN)) {
+ authToken = entry.getKey();
+ }
+ }
+ if (storageUrl == null || authToken == null) {
+ throw new AuthorizationException("Invalid headers in TempAuth response " + from);
+ }
+ // For portability with keystone, based on common knowledge that these tokens tend to expire in 24 hours
+ // http://docs.openstack.org/api/openstack-object-storage/1.0/content/authentication-object-dev-guide.html
+ Date expires = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(24));
+ return Access.builder()
+ .user(User.builder().id(username).name(username).build())
+ .token(Token.builder().id(authToken).expires(expires).build())
+ .service(Service.builder().name("Object Storage").type(OBJECT_STORE)
+ .endpoint(Endpoint.builder().publicURL(storageUrl).id(apiVersion).region(storageUrl.getHost()).build())
+ .build()).build();
+ }
+
+ // TODO: find the swift configuration or bug related to returning localhost
+ private URI getURI(String headerValue) {
+ if (headerValue == null)
+ return null;
+ URI toReturn = URI.create(headerValue);
+ if (!"127.0.0.1".equals(toReturn.getHost()))
+ return toReturn;
+ return uriBuilder(toReturn).host(host).build();
+ }
+
+ @Override
+ public AdaptTempAuthResponseToAccess setContext(HttpRequest request) {
+ String host = request.getEndpoint().getHost();
+ this.host = host;
+ this.username = request.getFirstHeaderOrNull(STORAGE_USER);
+ return this;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/df4323b4/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TempAuthMockTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TempAuthMockTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TempAuthMockTest.java
new file mode 100644
index 0000000..140db7d
--- /dev/null
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/TempAuthMockTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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 com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.concurrent.config.ExecutorServiceModule;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+@Test(groups = "unit", testName = "TempAuthMockTest", singleThreaded = true)
+public class TempAuthMockTest {
+
+ private MockWebServer swiftServer;
+ private MockWebServer tempAuthServer;
+
+
+ public void testGenerateJWTRequest() throws Exception {
+ tempAuthServer.enqueue(new MockResponse().setResponseCode(204)
+ .addHeader("X-Auth-Token", "token")
+ .addHeader("X-Storage-Url", "http://127.0.0.1:" + swiftServer.getPort()));
+
+ swiftServer.enqueue(new MockResponse().setBody("[{\"name\":\"test_container_1\",\"count\":2,\"bytes\":78}]"));
+
+ SwiftApi api = api("http://127.0.0.1:" + tempAuthServer.getPort());
+
+ // Region name is derived from the swift server host.
+ assertEquals(api.getConfiguredRegions(), ImmutableSet.of("127.0.0.1"));
+
+ assertTrue(api.getContainerApi("127.0.0.1").list().iterator().hasNext());
+
+ RecordedRequest auth = tempAuthServer.takeRequest();
+ assertEquals(auth.getMethod(), "GET");
+ assertEquals(auth.getHeader("X-Storage-User"), "user");
+ assertEquals(auth.getHeader("X-Storage-Pass"), "password");
+
+ // list request went to the destination specified in X-Storage-Url.
+ RecordedRequest listContainers = swiftServer.takeRequest();
+ assertEquals(listContainers.getMethod(), "GET");
+ assertEquals(listContainers.getPath(), "/");
+ assertEquals(listContainers.getHeader("Accept"), APPLICATION_JSON);
+ }
+
+ private SwiftApi api(String authUrl) throws IOException {
+ Properties overrides = new Properties();
+ overrides.setProperty(CREDENTIAL_TYPE, "tempAuthCredentials");
+ return ContextBuilder.newBuilder(new SwiftApiMetadata())
+ .credentials("user", "password")
+ .endpoint(authUrl)
+ .overrides(overrides)
+ .modules(ImmutableSet.of(new ExecutorServiceModule(sameThreadExecutor())))
+ .buildApi(SwiftApi.class);
+ }
+
+ @BeforeMethod
+ public void start() throws IOException {
+ tempAuthServer = new MockWebServer();
+ tempAuthServer.play();
+
+ swiftServer = new MockWebServer();
+ swiftServer.play();
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void stop() throws IOException {
+ tempAuthServer.shutdown();
+ swiftServer.shutdown();
+ }
+}