Implemented login / logout APIs

This commit is contained in:
Andrei Savu 2012-01-31 04:11:26 +02:00
parent 19ff2eb89b
commit b6d106c7a1
10 changed files with 656 additions and 5 deletions

View File

@ -34,6 +34,7 @@ import org.jclouds.cloudstack.features.NetworkAsyncClient;
import org.jclouds.cloudstack.features.OfferingAsyncClient; import org.jclouds.cloudstack.features.OfferingAsyncClient;
import org.jclouds.cloudstack.features.SSHKeyPairAsyncClient; import org.jclouds.cloudstack.features.SSHKeyPairAsyncClient;
import org.jclouds.cloudstack.features.SecurityGroupAsyncClient; import org.jclouds.cloudstack.features.SecurityGroupAsyncClient;
import org.jclouds.cloudstack.features.SessionAsyncClient;
import org.jclouds.cloudstack.features.SnapshotAsyncClient; import org.jclouds.cloudstack.features.SnapshotAsyncClient;
import org.jclouds.cloudstack.features.TemplateAsyncClient; import org.jclouds.cloudstack.features.TemplateAsyncClient;
import org.jclouds.cloudstack.features.VMGroupAsyncClient; import org.jclouds.cloudstack.features.VMGroupAsyncClient;
@ -184,4 +185,10 @@ public interface CloudStackAsyncClient {
*/ */
@Delegate @Delegate
SnapshotAsyncClient getSnapshotClient(); SnapshotAsyncClient getSnapshotClient();
/**
* Provides asynchronous access to Sessions
*/
@Delegate
SessionAsyncClient getSessionClient();
} }

View File

@ -18,8 +18,6 @@
*/ */
package org.jclouds.cloudstack; package org.jclouds.cloudstack;
import java.util.concurrent.TimeUnit;
import org.jclouds.cloudstack.features.AccountClient; import org.jclouds.cloudstack.features.AccountClient;
import org.jclouds.cloudstack.features.AddressClient; import org.jclouds.cloudstack.features.AddressClient;
import org.jclouds.cloudstack.features.AsyncJobClient; 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.VMGroupClient;
import org.jclouds.cloudstack.features.VirtualMachineClient; import org.jclouds.cloudstack.features.VirtualMachineClient;
import org.jclouds.cloudstack.features.VolumeClient; import org.jclouds.cloudstack.features.VolumeClient;
import org.jclouds.cloudstack.features.SessionClient;
import org.jclouds.cloudstack.features.ZoneClient; import org.jclouds.cloudstack.features.ZoneClient;
import org.jclouds.concurrent.Timeout; import org.jclouds.concurrent.Timeout;
import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.Delegate;
import java.util.concurrent.TimeUnit;
/** /**
* Provides synchronous access to CloudStack. * Provides synchronous access to CloudStack.
* <p/> * <p/>
@ -187,4 +188,10 @@ public interface CloudStackClient {
*/ */
@Delegate @Delegate
SnapshotClient getSnapshotClient(); SnapshotClient getSnapshotClient();
/**
* Provides synchronous access to Sessions
*/
@Delegate
SessionClient getSessionClient();
} }

View File

@ -18,12 +18,12 @@
*/ */
package org.jclouds.cloudstack; package org.jclouds.cloudstack;
import static org.jclouds.Constants.PROPERTY_API_VERSION; import org.jclouds.PropertiesBuilder;
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
import java.util.Properties; 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 * Builds properties used in cloudstack Clients

View File

@ -96,6 +96,8 @@ import org.jclouds.cloudstack.features.SSHKeyPairAsyncClient;
import org.jclouds.cloudstack.features.SSHKeyPairClient; import org.jclouds.cloudstack.features.SSHKeyPairClient;
import org.jclouds.cloudstack.features.SecurityGroupAsyncClient; import org.jclouds.cloudstack.features.SecurityGroupAsyncClient;
import org.jclouds.cloudstack.features.SecurityGroupClient; 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.SnapshotAsyncClient;
import org.jclouds.cloudstack.features.SnapshotClient; import org.jclouds.cloudstack.features.SnapshotClient;
import org.jclouds.cloudstack.features.TemplateAsyncClient; import org.jclouds.cloudstack.features.TemplateAsyncClient;
@ -175,6 +177,7 @@ public class CloudStackRestClientModule extends RestClientModule<CloudStackClien
.put(GlobalUsageClient.class, GlobalUsageAsyncClient.class)// .put(GlobalUsageClient.class, GlobalUsageAsyncClient.class)//
.put(GlobalPodClient.class, GlobalPodAsyncClient.class)// .put(GlobalPodClient.class, GlobalPodAsyncClient.class)//
.put(GlobalVlanClient.class, GlobalVlanAsyncClient.class)// .put(GlobalVlanClient.class, GlobalVlanAsyncClient.class)//
.put(SessionClient.class, SessionAsyncClient.class)//
.build(); .build();
@Override @Override

View File

@ -0,0 +1,270 @@
/**
* 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.domain;
import com.google.gson.annotations.SerializedName;
/**
* Representation of the login API call response
*
* @author Andrei Savu
*/
public class LoginResponse implements Comparable<LoginResponse> {
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;
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 LoginResponse build() {
return new LoginResponse(userName, userId, password, domainId, timeout, accountName,
firstName, lastName, accountType, timezone, timezoneOffset, sessionKey);
}
}
// 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;
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) {
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;
}
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;
}
@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;
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);
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 + '\'' +
'}';
}
@Override
public int compareTo(LoginResponse arg0) {
return sessionKey.compareTo(arg0.getSessionKey());
}
}

View File

@ -0,0 +1,93 @@
/**
* 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.HashToMD5;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.ParamParser;
import org.jclouds.rest.annotations.QueryParams;
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.HeaderParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
/**
* Provides asynchronous access to Cloudstack Sessions
* <p/>
*
* @see org.jclouds.cloudstack.features.SessionClient
* @see <a href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html" />
* @author Andrei Savu
*/
@QueryParams(keys = "response", values = "json")
public interface SessionAsyncClient {
/**
* @see SessionClient#loginWithHashedPassword
*/
@GET
@QueryParams(keys = "command", values = "login")
@SelectJson("loginresponse")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<LoginResponse> loginWithHashedPassword(@QueryParam("username") String userName,
@QueryParam("password") String hashedPassword, @QueryParam("domain") String domainOrEmpty);
/**
* @see SessionClient#loginWithPlainTextPassword
*/
@GET
@QueryParams(keys = "command", values = "login")
@SelectJson("loginresponse")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<LoginResponse> loginWithPlainTextPassword(@QueryParam("username") String userName,
@QueryParam("password") @ParamParser(HashToMD5.class) String plainTextPassword,
@QueryParam("domain") String domainOrEmpty);
/**
* @see SessionClient#getAccountByName
*/
@GET
@QueryParams(keys = "comand", values = "listAccounts")
@SelectJson("account")
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
@HeaderParam(HttpHeaders.COOKIE)
ListenableFuture<Account> getAccountByName(@QueryParam("name") String accountName,
@CookieParam("sessionKey") @QueryParam("sessionkey") String sessionKey);
/**
* @see SessionClient#logout
*/
@GET
@QueryParams(keys = "command", values = "logout")
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
ListenableFuture<Void> logout(@QueryParam("sessionkey") String sessionKey);
}

View File

@ -0,0 +1,89 @@
/**
* 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
* <p/>
*
* @see <a href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html" />
* @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 hashedPassword
* hashed password (by default MD5)
* @param domainOrEmpty
* domain name, if empty defaults to ROOT
* @return
* login response with session key or null
*/
LoginResponse loginWithHashedPassword(String userName, String hashedPassword, String domainOrEmpty);
/**
* Logs a user into Cloudstack. A successful login attempt will generate a session key
* 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 plainTextPassword
* plain text password
* @param domainOrEmpty
* domain name, if empty defaults to ROOT
* @return
* login response with session key or null
*/
LoginResponse loginWithPlainTextPassword(String userName, String plainTextPassword, String domainOrEmpty);
/**
* Retrieve an account by name using the session key for login
*
* @param accountName
* account name
* @param sessionKey
* a valid session key
* @return
* account instance or null
*/
Account getAccountByName(String accountName, String sessionKey);
/**
* Logs out the user by invalidating the session key
*
* @param sessionKey
* user session key
*/
Void logout(String sessionKey);
}

View File

@ -0,0 +1,49 @@
/**
* 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.functions;
import com.google.common.base.Function;
import com.google.common.io.InputSupplier;
import org.jclouds.crypto.CryptoStreams;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Hash a string to MD5 (hex representation)
*
* @author Andrei Savu
*/
public class HashToMD5 implements Function<Object, String> {
@Override
public String apply(final Object input) {
try {
return CryptoStreams.md5Hex(new InputSupplier<InputStream>() {
@Override
public InputStream getInput() throws IOException {
return new ByteArrayInputStream(String.class.cast(input).getBytes());
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -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 CloudStackUtil {
/**
* 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;
}
}

View File

@ -0,0 +1,87 @@
/**
* 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.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 PASSWORD_MD5_HASH = "30e14b3727225d833aad2206acea1275";
private final String DOMAIN = "/Partners/jCloud";
private LoginResponse loginResponse;
@Test(enabled = true)
public void testLoginWithPlainTextPassword() throws Exception {
LoginResponse response = client.getSessionClient()
.loginWithPlainTextPassword(USER, PLAIN_TEXT_PASSWORD, DOMAIN);
assertNotNull(response);
assertNotNull(response.getSessionKey());
}
@Test(enabled = true)
public void testLoginWithHashedPassword() throws Exception {
loginResponse = client.getSessionClient()
.loginWithHashedPassword(USER, PASSWORD_MD5_HASH, DOMAIN);
assertNotNull(loginResponse);
assertNotNull(loginResponse.getSessionKey());
}
@Test(dependsOnMethods = "testLoginWithHashedPassword")
public void testRetrieveUserInfoWithSessionKey() throws Exception {
Account account = client.getSessionClient()
.getAccountByName(loginResponse.getAccountName(), loginResponse.getSessionKey());
assertNotNull(account);
assertEquals(account.getName(), loginResponse.getAccountName());
User currentUser = find(account.getUsers(), new Predicate<User>() {
@Override
public boolean apply(User user) {
return user.getId() == loginResponse.getUserId();
}
});
assertNotNull(currentUser);
assertEquals(currentUser.getName(), loginResponse.getUserName());
assertEquals(currentUser.getDomainId(), loginResponse.getDomainId());
}
@Test(dependsOnMethods = "testRetrieveUserInfoWithSessionKey")
public void testLogout() throws Exception {
client.getSessionClient().logout(loginResponse.getSessionKey());
}
}