diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/LoginWithPasswordCredentials.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/LoginWithPasswordCredentials.java index 1521c557ce..5909404008 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/LoginWithPasswordCredentials.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/LoginWithPasswordCredentials.java @@ -18,17 +18,15 @@ */ package org.jclouds.cloudstack.functions; -import java.io.File; - -import javax.inject.Inject; -import javax.inject.Singleton; - +import com.google.common.base.Function; import org.jclouds.cloudstack.domain.LoginResponse; import org.jclouds.cloudstack.features.SessionClient; import org.jclouds.crypto.CryptoStreams; import org.jclouds.domain.Credentials; -import com.google.common.base.Function; +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.File; @Singleton public class LoginWithPasswordCredentials implements Function { @@ -42,7 +40,8 @@ public class LoginWithPasswordCredentials implements Functionof(), setupProperties(endpoint, username, password, domain)); + + CloudStackClient client = CloudStackClient.class.cast(context.getProviderSpecificContext().getApi()); + Set listOfAccounts = client.getAccountClient().listAccounts(); + + domain = (domain.equals("") || domain.equals("/")) ? "ROOT" : domain; + for (Account account : listOfAccounts) { + for (User user : account.getUsers()) { + if (user.getName().equals(username) && user.getDomain().equals(domain)) { + return ApiKeyPair.builder().apiKey(user.getApiKey()) + .secretKey(user.getSecretKey()).build(); + } + } + } + throw new NoSuchElementException("Unable to find API keypair for user " + username); + + } finally { + if (context != null) + context.close(); + } + } + + private static Properties setupRestProperties() { + return RestContextFactory.getPropertiesFromResource("/rest.properties"); + } + + private static Properties setupProperties(URI endpoint, String username, String password, String domain) { + Properties overrides = new Properties(); + + overrides.put(Constants.PROPERTY_TRUST_ALL_CERTS, "true"); + overrides.put(Constants.PROPERTY_RELAX_HOSTNAME, "true"); + + overrides.put("jclouds.cloudstack.credential-type", "passwordCredentials"); + + overrides.put(PROVIDER + ".endpoint", checkNotNull(endpoint, "endpoint").toASCIIString()); + overrides.put(PROVIDER + ".identity", + String.format("%s/%s", checkNotNull(domain, "domain"), checkNotNull(username, "username"))); + overrides.put(PROVIDER + ".credential", checkNotNull(password, "password")); + + return overrides; } } diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/PasswordAuthenticationExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/PasswordAuthenticationExpectTest.java index 6f36a95d81..47837c3d0f 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/PasswordAuthenticationExpectTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/PasswordAuthenticationExpectTest.java @@ -35,7 +35,7 @@ import com.google.common.net.HttpHeaders; /** * - * @see KeystoneProperties#CREDENTIAL_TYPE + * @see CloudStackProperties#CREDENTIAL_TYPE * @author Adrian Cole */ @Test(groups = "unit", testName = "PasswordAuthenticationExpectTest") diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackRestClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackRestClientExpectTest.java index e8ffd55631..be8fccfbaa 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackRestClientExpectTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackRestClientExpectTest.java @@ -18,12 +18,10 @@ */ package org.jclouds.cloudstack.features; -import static org.jclouds.crypto.CryptoStreams.md5Hex; - -import java.net.URI; -import java.net.URLEncoder; -import java.util.Properties; - +import com.google.common.base.Function; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; import org.jclouds.cloudstack.CloudStackContext; import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.http.HttpRequest; @@ -31,10 +29,11 @@ import org.jclouds.http.HttpResponse; import org.jclouds.logging.config.NullLoggingModule; import org.jclouds.rest.BaseRestClientExpectTest; -import com.google.common.base.Function; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.inject.Module; +import java.net.URI; +import java.net.URLEncoder; +import java.util.Properties; + +import static org.jclouds.crypto.CryptoStreams.md5Hex; /** * Base class for writing CloudStack Rest Client Expect tests @@ -60,7 +59,7 @@ public abstract class BaseCloudStackRestClientExpectTest extends BaseRestClie .method("GET") .endpoint( URI.create("http://localhost:8080/client/api?response=json&command=login&" + - "username=identity&password=" + md5Hex("credential")+ "&domain=ROOT")) + "username=identity&password=" + md5Hex("credential")+ "&domain=")) .headers( ImmutableMultimap.builder() .put("Accept", "application/json") diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountClientLiveTest.java index d4c066ad6f..78b35d5ba6 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountClientLiveTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalAccountClientLiveTest.java @@ -20,6 +20,7 @@ package org.jclouds.cloudstack.features; import org.jclouds.cloudstack.CloudStackGlobalClient; import org.jclouds.cloudstack.domain.Account; +import org.jclouds.crypto.CryptoStreams; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; @@ -36,7 +37,7 @@ public class GlobalAccountClientLiveTest extends BaseCloudStackClientLiveTest { public static Account createTestAccount(CloudStackGlobalClient client, String prefix) { return client.getAccountClient().createAccount( prefix + "-account", Account.Type.USER, "dummy@example.com", - "First", "Last", "hashed-password"); + "First", "Last", CryptoStreams.md5Hex("password")); } @Test diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserClientLiveTest.java index d0427889b4..ba5e9cac7f 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserClientLiveTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/GlobalUserClientLiveTest.java @@ -27,6 +27,7 @@ import org.jclouds.cloudstack.domain.ApiKeyPair; import org.jclouds.cloudstack.domain.User; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; +import org.jclouds.crypto.CryptoStreams; import org.jclouds.logging.log4j.config.Log4JLoggingModule; import org.jclouds.sshj.config.SshjSshClientModule; import org.testng.annotations.Test; @@ -48,7 +49,7 @@ public class GlobalUserClientLiveTest extends BaseCloudStackClientLiveTest { public static User createTestUser(CloudStackGlobalClient client, Account account, String prefix) { return client.getUserClient().createUser(prefix + "-user", - account.getName(), "dummy2@example.com", "md5-password", "First", "Last"); + account.getName(), "dummy2@example.com", CryptoStreams.md5Hex("password"), "First", "Last"); } @Test diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientExpectTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientExpectTest.java index c58f3ce0f4..887832b03c 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientExpectTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientExpectTest.java @@ -18,13 +18,7 @@ */ package org.jclouds.cloudstack.features; -import static org.jclouds.crypto.CryptoStreams.md5Hex; -import static org.testng.Assert.assertEquals; - -import java.io.IOException; -import java.net.URI; -import java.net.URLEncoder; - +import com.google.common.collect.ImmutableMultimap; import org.jclouds.cloudstack.CloudStackContext; import org.jclouds.cloudstack.domain.Account; import org.jclouds.cloudstack.domain.LoginResponse; @@ -32,11 +26,16 @@ import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableMultimap; +import java.io.IOException; +import java.net.URI; +import java.net.URLEncoder; + +import static org.jclouds.crypto.CryptoStreams.md5Hex; +import static org.testng.Assert.assertEquals; /** * Tests behavior of {@code SessionClient} - * + * * @author Andrei Savu */ @Test(groups = "live", singleThreaded = true, testName = "SessionClientExpectTest") @@ -44,42 +43,54 @@ public class SessionClientExpectTest extends BaseCloudStackRestClientExpectTest< @SuppressWarnings("deprecation") public void testLoginWhenResponseIs2xxIncludesJSessionId() throws IOException { - String domain = "Partners/jCloud"; - String user = "jcloud"; - String password = "jcl0ud"; - String md5password = md5Hex(password); + String domain = "Partners/jCloud"; + String user = "jcloud"; + String password = "jcl0ud"; + String md5password = md5Hex(password); HttpRequest request = HttpRequest.builder() .method("GET") .endpoint( URI.create("http://localhost:8080/client/api?response=json&command=login&" + - "username="+user+"&password=" + md5password+ "&domain=" + URLEncoder.encode(domain))) + "username=" + user + "&password=" + md5password + "&domain=" + URLEncoder.encode(domain))) .headers( ImmutableMultimap.builder() .put("Accept", "application/json") .build()) .build(); - String jSessionId = "90DD65D13AEAA590ECCA312D150B9F6D"; + String jSessionId = "90DD65D13AEAA590ECCA312D150B9F6D"; SessionClient client = requestSendsResponse(request, HttpResponse.builder() .statusCode(200) - .headers( - ImmutableMultimap.builder() - .put("Set-Cookie", "JSESSIONID="+jSessionId+"; Path=/client") - .build()) + .headers( + ImmutableMultimap.builder() + .put("Set-Cookie", "JSESSIONID=" + jSessionId + "; Path=/client") + .build()) .payload(payloadFromResource("/loginresponse.json")) .build()); assertEquals(client.loginUserInDomainWithHashOfPassword(user, domain, md5password).toString(), - - LoginResponse.builder().timeout(1800).lastName("Kiran").registered(false).username("jcloud").firstName("Vijay") - .domainId(11).accountType(Account.Type.DOMAIN_ADMIN).userId(19).sessionKey( - "uYT4/MNiglgAKiZRQkvV8QP8gn0=").jSessionId(jSessionId).accountName("jcloud").build().toString()); + LoginResponse.builder().timeout(1800).lastName("Kiran").registered(false).username("jcloud").firstName("Vijay") + .domainId(11).accountType(Account.Type.DOMAIN_ADMIN).userId(19).sessionKey( + "uYT4/MNiglgAKiZRQkvV8QP8gn0=").jSessionId(jSessionId).accountName("jcloud").build().toString()); } - - //TODO: logout. + public void testLogout() throws IOException { + HttpRequest request = HttpRequest.builder() + .method("GET") + .endpoint( + URI.create("http://localhost:8080/client/api?response=json&command=logout&sessionkey=dummy-session-key")) + .build(); + + SessionClient client = requestSendsResponse(request, + HttpResponse.builder() + .statusCode(200) + .payload(payloadFromResource("/logoutresponse.json")) + .build()); + + client.logoutUser("dummy-session-key"); + } @Override protected SessionClient clientFrom(CloudStackContext context) { diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientLiveTest.java new file mode 100644 index 0000000000..e546ee98e7 --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientLiveTest.java @@ -0,0 +1,91 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.cloudstack.features; + +import org.jclouds.cloudstack.domain.Account; +import org.jclouds.cloudstack.domain.ApiKeyPair; +import org.jclouds.cloudstack.domain.LoginResponse; +import org.jclouds.cloudstack.domain.User; +import org.jclouds.cloudstack.util.ApiKeyPairs; +import org.jclouds.crypto.CryptoStreams; +import org.jclouds.rest.AuthorizationException; +import org.testng.annotations.Test; + +import java.net.URI; + +import static org.jclouds.cloudstack.features.GlobalAccountClientLiveTest.createTestAccount; +import static org.jclouds.cloudstack.features.GlobalUserClientLiveTest.createTestUser; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +/** + * Tests behavior of {@code SessionClient} + * + * @author Andrei Savu + */ +@Test(groups = "live", singleThreaded = true, testName = "SessionClientLiveTest") +public class SessionClientLiveTest extends BaseCloudStackClientLiveTest { + + @Test + public void testCreateContextUsingUserAndPasswordAuthentication() { + assert globalAdminEnabled; + + Account testAccount = null; + User testUser = null; + String prefix = this.prefix + "-session"; + try { + testAccount = createTestAccount(globalAdminClient, prefix); + testUser = createTestUser(globalAdminClient, testAccount, prefix); + + String expectedUsername = prefix + "-user"; + assertEquals(testUser.getName(), expectedUsername); + + checkLoginAsTheNewUser(expectedUsername); + + ApiKeyPair expected = globalAdminClient.getUserClient().registerUserKeys(testUser.getId()); + ApiKeyPair actual = ApiKeyPairs.loginToEndpointAsUsernameInDomainWithPasswordAndReturnApiKeyPair( + URI.create(endpoint), prefix + "-user", "password", ""); + + assertEquals(actual, expected); + + } finally { + if (testUser != null) + globalAdminClient.getUserClient().deleteUser(testUser.getId()); + if (testAccount != null) + globalAdminClient.getAccountClient().deleteAccount(testAccount.getId()); + } + } + + @Test(expectedExceptions = AuthorizationException.class) + public void testTryToGetApiKeypairWithWrongCredentials() { + ApiKeyPairs.loginToEndpointAsUsernameInDomainWithPasswordAndReturnApiKeyPair( + URI.create(endpoint), "dummy-missing-user", "with-a-wrong-password", ""); + } + + private void checkLoginAsTheNewUser(String expectedUsername) { + LoginResponse response = globalAdminClient.getSessionClient() + .loginUserInDomainWithHashOfPassword(expectedUsername, "", CryptoStreams.md5Hex("password")); + + assertNotNull(response); + assertNotNull(response.getSessionKey()); + assertNotNull(response.getJSessionId()); + + client.getSessionClient().logoutUser(response.getSessionKey()); + } +} diff --git a/apis/cloudstack/src/test/resources/logoutresponse.json b/apis/cloudstack/src/test/resources/logoutresponse.json new file mode 100644 index 0000000000..f88dddd3e7 --- /dev/null +++ b/apis/cloudstack/src/test/resources/logoutresponse.json @@ -0,0 +1 @@ +{ "logoutresponse" : { "description" : "success" } } \ No newline at end of file