diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java index baf433b5e1..0a983f7e41 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackAsyncClient.java @@ -34,6 +34,7 @@ import org.jclouds.cloudstack.features.NetworkAsyncClient; import org.jclouds.cloudstack.features.OfferingAsyncClient; import org.jclouds.cloudstack.features.SSHKeyPairAsyncClient; import org.jclouds.cloudstack.features.SecurityGroupAsyncClient; +import org.jclouds.cloudstack.features.SessionAsyncClient; import org.jclouds.cloudstack.features.SnapshotAsyncClient; import org.jclouds.cloudstack.features.TemplateAsyncClient; import org.jclouds.cloudstack.features.VMGroupAsyncClient; @@ -184,4 +185,10 @@ public interface CloudStackAsyncClient { */ @Delegate SnapshotAsyncClient getSnapshotClient(); + + /** + * Provides asynchronous access to Sessions + */ + @Delegate + SessionAsyncClient getSessionClient(); } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java index 82741a32aa..fc153e06a3 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackClient.java @@ -18,8 +18,6 @@ */ package org.jclouds.cloudstack; -import java.util.concurrent.TimeUnit; - import org.jclouds.cloudstack.features.AccountClient; import org.jclouds.cloudstack.features.AddressClient; import org.jclouds.cloudstack.features.AsyncJobClient; @@ -41,10 +39,13 @@ import org.jclouds.cloudstack.features.TemplateClient; import org.jclouds.cloudstack.features.VMGroupClient; import org.jclouds.cloudstack.features.VirtualMachineClient; import org.jclouds.cloudstack.features.VolumeClient; +import org.jclouds.cloudstack.features.SessionClient; import org.jclouds.cloudstack.features.ZoneClient; import org.jclouds.concurrent.Timeout; import org.jclouds.rest.annotations.Delegate; +import java.util.concurrent.TimeUnit; + /** * Provides synchronous access to CloudStack. *

@@ -187,4 +188,10 @@ public interface CloudStackClient { */ @Delegate SnapshotClient getSnapshotClient(); + + /** + * Provides synchronous access to Sessions + */ + @Delegate + SessionClient getSessionClient(); } diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackPropertiesBuilder.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackPropertiesBuilder.java index 0238bc0bac..61dafad385 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackPropertiesBuilder.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackPropertiesBuilder.java @@ -18,12 +18,12 @@ */ package org.jclouds.cloudstack; -import static org.jclouds.Constants.PROPERTY_API_VERSION; -import static org.jclouds.Constants.PROPERTY_ENDPOINT; +import org.jclouds.PropertiesBuilder; import java.util.Properties; -import org.jclouds.PropertiesBuilder; +import static org.jclouds.Constants.PROPERTY_API_VERSION; +import static org.jclouds.Constants.PROPERTY_ENDPOINT; /** * Builds properties used in cloudstack Clients diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java index 68023513f3..999f7025b7 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/config/CloudStackRestClientModule.java @@ -96,6 +96,8 @@ import org.jclouds.cloudstack.features.SSHKeyPairAsyncClient; import org.jclouds.cloudstack.features.SSHKeyPairClient; import org.jclouds.cloudstack.features.SecurityGroupAsyncClient; import org.jclouds.cloudstack.features.SecurityGroupClient; +import org.jclouds.cloudstack.features.SessionAsyncClient; +import org.jclouds.cloudstack.features.SessionClient; import org.jclouds.cloudstack.features.SnapshotAsyncClient; import org.jclouds.cloudstack.features.SnapshotClient; import org.jclouds.cloudstack.features.TemplateAsyncClient; @@ -175,6 +177,7 @@ public class CloudStackRestClientModule extends RestClientModule { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String userName; + private long userId; + private String password; + private long domainId; + private long timeout; + private String accountName; + private String firstName; + private String lastName; + private Account.Type accountType; + private String timezone; + private String timezoneOffset; + private String sessionKey; + private String jSessionId; + + public Builder copyOf(LoginResponse r) { + this.userName = r.userName; + this.userId = r.userId; + this.password = r.password; + this.domainId = r.domainId; + this.timeout = r.timeout; + this.accountName = r.accountName; + this.firstName = r.firstName; + this.lastName = r.lastName; + this.accountType = r.accountType; + this.timezone = r.timezone; + this.timezoneOffset = r.timezoneOffset; + this.sessionKey = r.sessionKey; + this.jSessionId = r.jSessionId; + return this; + } + + public Builder userName(String userName) { + this.userName = userName; + return this; + } + + public Builder userId(long userId) { + this.userId = userId; + return this; + } + + public Builder password(String password) { + this.password = password; + return this; + } + + public Builder domainId(long domainId) { + this.domainId = domainId; + return this; + } + + public Builder timeout(long timeout) { + this.timeout = timeout; + return this; + } + + public Builder accountName(String accountName) { + this.accountName = accountName; + return this; + } + + public Builder firstName(String firstName) { + this.firstName = firstName; + return this; + } + + public Builder lastName(String lastName) { + this.lastName = lastName; + return this; + } + + public Builder accountType(Account.Type accountType) { + this.accountType = accountType; + return this; + } + + public Builder timezone(String timezone) { + this.timezone = timezone; + return this; + } + + public Builder timezoneOffset(String timezoneOffset) { + this.timezoneOffset = timezoneOffset; + return this; + } + + public Builder sessionKey(String sessionKey) { + this.sessionKey = sessionKey; + return this; + } + + public Builder jSessionId(String jSessionId) { + this.jSessionId = jSessionId; + return this; + } + + public LoginResponse build() { + return new LoginResponse(userName, userId, password, domainId, timeout, accountName, + firstName, lastName, accountType, timezone, timezoneOffset, sessionKey, jSessionId); + } + } + + // for deserialization + LoginResponse() { } + + @SerializedName("username") + private String userName; + @SerializedName("userid") + private long userId; + private String password; + @SerializedName("domainid") + private long domainId; + private long timeout; + @SerializedName("account") + private String accountName; + @SerializedName("firstname") + private String firstName; + @SerializedName("lastname") + private String lastName; + @SerializedName("type") + private Account.Type accountType; + private String timezone; + @SerializedName("timezoneoffset") + private String timezoneOffset; + @SerializedName("sessionkey") + private String sessionKey; + private String jSessionId; + + public LoginResponse(String userName, long userId, String password, long domainId, long timeout, + String accountName, String firstName, String lastName, Account.Type accountType, + String timezone, String timezoneOffset, String sessionKey, String jSessionId) { + this.userName = userName; + this.userId = userId; + this.password = password; + this.domainId = domainId; + this.timeout = timeout; + this.accountName = accountName; + this.firstName = firstName; + this.lastName = lastName; + this.accountType = accountType; + this.timezone = timezone; + this.timezoneOffset = timezoneOffset; + this.sessionKey = sessionKey; + this.jSessionId = jSessionId; + } + + public String getUserName() { + return userName; + } + + public long getUserId() { + return userId; + } + + public String getPassword() { + return password; + } + + public long getDomainId() { + return domainId; + } + + public long getTimeout() { + return timeout; + } + + public String getAccountName() { + return accountName; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public Account.Type getAccountType() { + return accountType; + } + + public String getTimezone() { + return timezone; + } + + public String getTimezoneOffset() { + return timezoneOffset; + } + + public String getSessionKey() { + return sessionKey; + } + + public String getJSessionId() { + return jSessionId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + LoginResponse loginResponse = (LoginResponse) o; + + if (domainId != loginResponse.domainId) return false; + if (timeout != loginResponse.timeout) return false; + if (userId != loginResponse.userId) return false; + if (accountName != null ? !accountName.equals(loginResponse.accountName) : loginResponse.accountName != null) return false; + if (accountType != loginResponse.accountType) return false; + if (firstName != null ? !firstName.equals(loginResponse.firstName) : loginResponse.firstName != null) return false; + if (lastName != null ? !lastName.equals(loginResponse.lastName) : loginResponse.lastName != null) return false; + if (password != null ? !password.equals(loginResponse.password) : loginResponse.password != null) return false; + if (sessionKey != null ? !sessionKey.equals(loginResponse.sessionKey) : loginResponse.sessionKey != null) return false; + if (timezone != null ? !timezone.equals(loginResponse.timezone) : loginResponse.timezone != null) return false; + if (timezoneOffset != null ? !timezoneOffset.equals(loginResponse.timezoneOffset) : loginResponse.timezoneOffset != null) + return false; + if (userName != null ? !userName.equals(loginResponse.userName) : loginResponse.userName != null) return false; + if (jSessionId != null ? !jSessionId.equals(loginResponse.jSessionId) : loginResponse.jSessionId != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = userName != null ? userName.hashCode() : 0; + result = 31 * result + (int) (userId ^ (userId >>> 32)); + result = 31 * result + (password != null ? password.hashCode() : 0); + result = 31 * result + (int) (domainId ^ (domainId >>> 32)); + result = 31 * result + (int) (timeout ^ (timeout >>> 32)); + result = 31 * result + (accountName != null ? accountName.hashCode() : 0); + result = 31 * result + (firstName != null ? firstName.hashCode() : 0); + result = 31 * result + (lastName != null ? lastName.hashCode() : 0); + result = 31 * result + (accountType != null ? accountType.hashCode() : 0); + result = 31 * result + (timezone != null ? timezone.hashCode() : 0); + result = 31 * result + (timezoneOffset != null ? timezoneOffset.hashCode() : 0); + result = 31 * result + (sessionKey != null ? sessionKey.hashCode() : 0); + result = 31 * result + (jSessionId != null ? jSessionId.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "LoginResponse{" + + "userName='" + userName + '\'' + + ", userId=" + userId + + ", password='" + password + '\'' + + ", domainId=" + domainId + + ", timeout=" + timeout + + ", accountName='" + accountName + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", accountType=" + accountType + + ", timezone='" + timezone + '\'' + + ", timezoneOffset='" + timezoneOffset + '\'' + + ", sessionKey='" + sessionKey + '\'' + + ", jSessionId='" + jSessionId + '\'' + + '}'; + } + + @Override + public int compareTo(LoginResponse arg0) { + return sessionKey.compareTo(arg0.getSessionKey()); + } + +} diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SessionAsyncClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SessionAsyncClient.java new file mode 100644 index 0000000000..452e6a3a76 --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SessionAsyncClient.java @@ -0,0 +1,78 @@ +/** + * 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 com.google.common.util.concurrent.ListenableFuture; +import org.jclouds.cloudstack.domain.Account; +import org.jclouds.cloudstack.domain.LoginResponse; +import org.jclouds.cloudstack.functions.ParseLoginResponseFromHttpResponse; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.QueryParams; +import org.jclouds.rest.annotations.ResponseParser; +import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; + +import javax.ws.rs.Consumes; +import javax.ws.rs.CookieParam; +import javax.ws.rs.GET; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +/** + * Provides asynchronous access to Cloudstack Sessions + *

+ * + * @see org.jclouds.cloudstack.features.SessionClient + * @see + * @author Andrei Savu + */ +@QueryParams(keys = "response", values = "json") +public interface SessionAsyncClient { + + /** + * @see SessionClient#loginUserInDomainWithHashOfPassword + */ + @GET + @QueryParams(keys = "command", values = "login") + @ResponseParser(ParseLoginResponseFromHttpResponse.class) + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture loginUserInDomainWithHashOfPassword(@QueryParam("username") String userName, + @QueryParam("domain") String domain, @QueryParam("password") String hashedPassword); + + /** + * @see SessionClient#getAccountByNameUsingSession + */ + @GET + @QueryParams(keys = "comand", values = "listAccounts") + @SelectJson("account") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getAccountByNameUsingSession(@QueryParam("name") String accountName, + @QueryParam("sessionkey") String sessionKey, @CookieParam("JSESSIONID") String jSessionId); + + /** + * @see SessionClient#logoutUser + */ + @GET + @QueryParams(keys = "command", values = "logout") + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) + ListenableFuture logoutUser(@QueryParam("sessionkey") String sessionKey); +} diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SessionClient.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SessionClient.java new file mode 100644 index 0000000000..f21239873e --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/features/SessionClient.java @@ -0,0 +1,77 @@ +/** + * 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.LoginResponse; +import org.jclouds.concurrent.Timeout; + +import java.util.concurrent.TimeUnit; + +/** + * Provides synchronous access to CloudStack Sessions + *

+ * + * @see + * @author Andrei Savu + */ +@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS) +public interface SessionClient { + + /** + * Logs a user into Cloudstack. A successful login attempt will generate a JSESSIONID + * cookie value that can be passed in subsequent Query command calls until the "logout" + * command has been issued or the session has expired. + * + * + * + * @param userName + * user account name + * @param domain + * domain name, if empty defaults to ROOT + * @param hashedPassword + * hashed password (by default MD5) + * @return + * login response with session key or null + */ + LoginResponse loginUserInDomainWithHashOfPassword(String userName, String domain, String hashedPassword); + + /** + * Retrieve an account by name using the session key for login + * + * @param accountName + * account name + * @param sessionKey + * a valid session key + * @param jSessionId + * the JSESSIONID cookie returned by the server + * @return + * account instance or null + */ + Account getAccountByNameUsingSession(String accountName, String sessionKey, String jSessionId); + + /** + * Logs out the user by invalidating the session key + * + * @param sessionKey + * user session key + */ + void logoutUser(String sessionKey); + +} diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseLoginResponseFromHttpResponse.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseLoginResponseFromHttpResponse.java new file mode 100644 index 0000000000..557a2764ad --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/functions/ParseLoginResponseFromHttpResponse.java @@ -0,0 +1,41 @@ +package org.jclouds.cloudstack.functions; + +import com.google.common.base.Function; +import com.google.common.base.Splitter; +import com.google.inject.Inject; +import com.google.inject.TypeLiteral; +import org.jclouds.cloudstack.domain.LoginResponse; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.ParseFirstJsonValueNamed; +import org.jclouds.json.internal.GsonWrapper; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.get; +import static com.google.common.collect.Iterables.getOnlyElement; + +/** + * @author Andrei Savu + */ +public class ParseLoginResponseFromHttpResponse implements Function { + + private ParseFirstJsonValueNamed parser; + + @Inject + ParseLoginResponseFromHttpResponse(GsonWrapper gson) { + this.parser = new ParseFirstJsonValueNamed(checkNotNull(gson, "gsonWrapper"), + new TypeLiteral(){}, "loginresponse"); + } + + @Override + public LoginResponse apply(HttpResponse response) { + checkNotNull(response, "response"); + + LoginResponse login = parser.apply(response); + checkNotNull(login, "loginResponse"); + + String jSessionId = get(Splitter.on("=").split(get(Splitter.on(";").trimResults().split( + getOnlyElement(response.getHeaders().get("Set-Cookie"))), 0)), 1); + + return LoginResponse.builder().copyOf(login).jSessionId(jSessionId).build(); + } +} diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/util/ApiKeyPairs.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/util/ApiKeyPairs.java new file mode 100644 index 0000000000..1846d22e80 --- /dev/null +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/util/ApiKeyPairs.java @@ -0,0 +1,46 @@ +/** + * 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.util; + +import org.jclouds.cloudstack.domain.ApiKeyPair; + +import java.net.URI; + +/** + * @author Andrei Savu + */ +public class ApiKeyPairs { + + /** + * Retrieve the API key pair for a given CloudStack user + * + * @param endpoint + * CloudStack API endpoint (e.g. http://72.52.126.25/client/api/) + * @param userName + * User account name + * @param password + * User password + * @param domain + * Domain name. If empty defaults to ROOT + * @return + */ + public static ApiKeyPair getApiKeyPairForUser(URI endpoint, String userName, String password, String domain) { + return null; + } +} 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..1b44bf5edf --- /dev/null +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/SessionClientLiveTest.java @@ -0,0 +1,79 @@ +/** + * 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 com.google.common.base.Predicate; +import org.jclouds.cloudstack.domain.Account; +import org.jclouds.cloudstack.domain.LoginResponse; +import org.jclouds.cloudstack.domain.User; +import org.testng.annotations.Test; + +import static com.google.common.collect.Iterables.find; +import static org.jclouds.crypto.CryptoStreams.md5Hex; +import static org.testng.Assert.assertNotNull; +import static org.testng.AssertJUnit.assertEquals; + +/** + * Tests behavior of {@code SessionClient} + * + * @author Andrei Savu + */ +@Test(groups = "live", singleThreaded = true, testName = "SessionClientLiveTest") +public class SessionClientLiveTest extends BaseCloudStackClientLiveTest { + + private final String USER = "jcloud"; + private final String PLAIN_TEXT_PASSWORD = "jcl0ud"; + private final String DOMAIN = "Partners/jCloud"; + + private LoginResponse login; + + @Test(enabled = true) + public void testLoginWithHashOfPassword() throws Exception { + login = client.getSessionClient() + .loginUserInDomainWithHashOfPassword(USER, DOMAIN, md5Hex(PLAIN_TEXT_PASSWORD)); + + assertNotNull(login); + assertNotNull(login.getSessionKey()); + assertNotNull(login.getJSessionId()); + } + + @Test(dependsOnMethods = "testLoginWithHashOfPassword") + public void testRetrieveUserInfoWithSessionKey() throws Exception { + Account account = client.getSessionClient().getAccountByNameUsingSession( + login.getAccountName(), login.getSessionKey(), login.getJSessionId()); + + assertNotNull(account); + assertEquals(account.getName(), login.getAccountName()); + + User currentUser = find(account.getUsers(), new Predicate() { + @Override + public boolean apply(User user) { + return user.getId() == login.getUserId(); + } + }); + assertNotNull(currentUser); + assertEquals(currentUser.getName(), login.getUserName()); + assertEquals(currentUser.getDomainId(), login.getDomainId()); + } + + @Test(dependsOnMethods = "testRetrieveUserInfoWithSessionKey") + public void testLogout() throws Exception { + client.getSessionClient().logoutUser(login.getSessionKey()); + } +} diff --git a/core/src/main/java/org/jclouds/crypto/CryptoStreams.java b/core/src/main/java/org/jclouds/crypto/CryptoStreams.java index ad3ae8e280..6c810cae10 100644 --- a/core/src/main/java/org/jclouds/crypto/CryptoStreams.java +++ b/core/src/main/java/org/jclouds/crypto/CryptoStreams.java @@ -18,8 +18,17 @@ */ package org.jclouds.crypto; -import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.annotations.Beta; +import com.google.common.base.Charsets; +import com.google.common.base.Throwables; +import com.google.common.io.ByteProcessor; +import com.google.common.io.ByteStreams; +import com.google.common.io.InputSupplier; +import org.jclouds.encryption.internal.Base64; +import org.jclouds.io.InputSuppliers; +import javax.crypto.Mac; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -28,17 +37,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; -import javax.crypto.Mac; - -import org.jclouds.encryption.internal.Base64; -import org.jclouds.io.InputSuppliers; - -import com.google.common.annotations.Beta; -import com.google.common.base.Charsets; -import com.google.common.base.Throwables; -import com.google.common.io.ByteProcessor; -import com.google.common.io.ByteStreams; -import com.google.common.io.InputSupplier; +import static com.google.common.base.Preconditions.checkNotNull; /** * functions related to but not in {@link com.google.common.io.ByteStreams} @@ -89,6 +88,18 @@ public class CryptoStreams { return hex(md5(supplier)); } + /** + * @see #md5Hex + */ + public static String md5Hex(final String in) throws IOException { + return md5Hex(new InputSupplier() { + @Override + public InputStream getInput() throws IOException { + return new ByteArrayInputStream(in.getBytes()); + } + }); + } + /** * @see #md5 * @see #base64