cloudian: refactor code around SSO and url handling

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2017-09-26 11:11:30 +05:30
parent 7bc74bc442
commit bc0bb89e97
9 changed files with 101 additions and 54 deletions

View File

@ -52,6 +52,7 @@ import com.cloud.utils.nio.TrustAllManager;
import com.fasterxml.jackson.databind.ObjectMapper;
public class CloudianClient {
private static final Logger LOG = Logger.getLogger(CloudianClient.class);
private final HttpClient httpClient;
@ -120,7 +121,7 @@ public class CloudianClient {
//////////////// Public APIs: User /////////////////////
////////////////////////////////////////////////////////
public boolean addUser(final UserInfo user) {
public boolean addUser(final CloudianUser user) {
try {
final HttpResponse response = put("/user", user);
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
@ -130,35 +131,35 @@ public class CloudianClient {
return false;
}
public UserInfo listUser(final String userId, final String groupId) {
public CloudianUser listUser(final String userId, final String groupId) {
try {
final HttpResponse response = get(String.format("/user?userId=%s&groupId=%s", userId, groupId));
final ObjectMapper mapper = new ObjectMapper();
if (response.getEntity() == null || response.getEntity().getContent() == null) {
return null;
}
return mapper.readValue(response.getEntity().getContent(), UserInfo.class);
return mapper.readValue(response.getEntity().getContent(), CloudianUser.class);
} catch (final IOException e) {
LOG.error("Failed to list Cloudian user due to:", e);
}
return null;
}
public List<UserInfo> listUsers(final String groupId) {
public List<CloudianUser> listUsers(final String groupId) {
try {
final HttpResponse response = get(String.format("/user/list?groupId=%s&userType=all&userStatus=active", groupId));
final ObjectMapper mapper = new ObjectMapper();
if (response.getEntity() == null || response.getEntity().getContent() == null) {
return null;
return new ArrayList<>();
}
return Arrays.asList(mapper.readValue(response.getEntity().getContent(), UserInfo[].class));
return Arrays.asList(mapper.readValue(response.getEntity().getContent(), CloudianUser[].class));
} catch (final IOException e) {
LOG.error("Failed to list Cloudian users due to:", e);
}
return new ArrayList<>();
}
public boolean updateUser(final UserInfo user) {
public boolean updateUser(final CloudianUser user) {
try {
final HttpResponse response = post("/user", user);
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
@ -182,7 +183,7 @@ public class CloudianClient {
//////////////// Public APIs: Group /////////////////////
/////////////////////////////////////////////////////////
public boolean addGroup(final GroupInfo group) {
public boolean addGroup(final CloudianGroup group) {
try {
final HttpResponse response = put("/group", group);
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
@ -192,35 +193,35 @@ public class CloudianClient {
return false;
}
public GroupInfo listGroup(final String groupId) {
public CloudianGroup listGroup(final String groupId) {
try {
final HttpResponse response = get(String.format("/group?groupId=%s", groupId));
final ObjectMapper mapper = new ObjectMapper();
if (response.getEntity() == null || response.getEntity().getContent() == null) {
return null;
}
return mapper.readValue(response.getEntity().getContent(), GroupInfo.class);
return mapper.readValue(response.getEntity().getContent(), CloudianGroup.class);
} catch (final IOException e) {
LOG.error("Failed to list Cloudian group due to:", e);
}
return null;
}
public List<GroupInfo> listGroups() {
public List<CloudianGroup> listGroups() {
try {
final HttpResponse response = get("/group/list");
final ObjectMapper mapper = new ObjectMapper();
if (response.getEntity() == null || response.getEntity().getContent() == null) {
return null;
return new ArrayList<>();
}
return Arrays.asList(mapper.readValue(response.getEntity().getContent(), GroupInfo[].class));
return Arrays.asList(mapper.readValue(response.getEntity().getContent(), CloudianGroup[].class));
} catch (final IOException e) {
LOG.error("Failed to list Cloudian groups due to:", e);
}
return new ArrayList<>();
}
public boolean updateGroup(final GroupInfo group) {
public boolean updateGroup(final CloudianGroup group) {
try {
final HttpResponse response = post("/group", group);
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;

View File

@ -20,11 +20,17 @@ package com.cloudian.client;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class GroupInfo {
public class CloudianGroup {
String groupId;
String groupName;
Boolean active;
@Override
public String toString() {
return String.format("Cloudian Group [id=%s, name=%s, active=%s]", groupId, groupName, active);
}
public String getGroupId() {
return groupId;
}

View File

@ -20,7 +20,8 @@ package com.cloudian.client;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserInfo {
public class CloudianUser {
public static final String USER = "User";
String userId;
@ -32,7 +33,7 @@ public class UserInfo {
@Override
public String toString() {
return String.format("User [id=%s, group id=%s, type=%s, active=%s, name=%s]", userId, groupId, userType, active, fullName);
return String.format("Cloudian User [id=%s, group id=%s, type=%s, active=%s, name=%s]", userId, groupId, userType, active, fullName);
}
public String getUserId() {

View File

@ -59,6 +59,18 @@ public interface CloudianConnector extends PluggableService {
ConfigKey<String> CloudianSsoKey = new ConfigKey<>("Advanced", String.class, "cloudian.sso.key", "ss0sh5r3dk3y",
"The shared single sign-on key as configured in Cloudian CMC.", true);
String getAdminUrl();
String getCmcUrl();
/**
* Checks if the Cloudian Connector is disabled
* @return returns true is connector is disabled
*/
boolean isConnectorDisabled();
/**
* Generates single-sign on URL for logged in user
* @return returns the SSO URL string
*/
String generateSsoUrl();
}

View File

@ -47,8 +47,8 @@ import com.cloud.user.dao.UserDao;
import com.cloud.utils.component.ComponentLifecycleBase;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloudian.client.CloudianClient;
import com.cloudian.client.GroupInfo;
import com.cloudian.client.UserInfo;
import com.cloudian.client.CloudianGroup;
import com.cloudian.client.CloudianUser;
import com.cloudian.cloudstack.api.CloudianIsEnabledCmd;
import com.cloudian.cloudstack.api.CloudianSsoLoginCmd;
@ -71,16 +71,6 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
//////////////// Plugin Methods /////////////////////
/////////////////////////////////////////////////////
private String getAdminUrl() {
return String.format("%s://%s:%s", CloudianAdminProtocol.value(),
CloudianAdminHost.value(), CloudianAdminPort.value());
}
private String getCmcUrl() {
return String.format("%s://%s:%s/Cloudian/ssosecurelogin.htm?", CloudianCmcProtocol.value(),
CloudianCmcHost.value(), CloudianCmcPort.value());
}
private CloudianClient getClient() {
try {
return new CloudianClient(getAdminUrl(),
@ -97,7 +87,7 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
return false;
}
final CloudianClient client = getClient();
final GroupInfo existingGroup = client.listGroup(domain.getUuid());
final CloudianGroup existingGroup = client.listGroup(domain.getUuid());
if (existingGroup != null) {
if (!existingGroup.getActive() || !existingGroup.getGroupName().equals(domain.getPath())) {
LOG.debug("Updating Cloudian group for domain uuid=" + domain.getUuid() + " name=" + domain.getName() + " path=" + domain.getPath());
@ -109,7 +99,7 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
}
LOG.debug("Adding Cloudian group for domain uuid=" + domain.getUuid() + " name=" + domain.getName() + " path=" + domain.getPath());
final GroupInfo group = new GroupInfo();
final CloudianGroup group = new CloudianGroup();
group.setGroupId(domain.getUuid());
group.setGroupName(domain.getPath());
group.setActive(true);
@ -122,7 +112,7 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
}
final CloudianClient client = getClient();
LOG.debug("Removing Cloudian group for domain uuid=" + domain.getUuid() + " name=" + domain.getName() + " path=" + domain.getPath());
for (final UserInfo user: client.listUsers(domain.getUuid())) {
for (final CloudianUser user: client.listUsers(domain.getUuid())) {
if (client.removeUser(user.getUserId(), domain.getUuid())) {
LOG.error(String.format("Failed to remove Cloudian user id=%s, while removing Cloudian group id=%s", user.getUserId(), domain.getUuid()));
}
@ -137,7 +127,7 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
final User accountUser = userDao.listByAccount(account.getId()).get(0);
final String fullName = String.format("%s %s (%s)", accountUser.getFirstname(), accountUser.getLastname(), account.getAccountName());
final CloudianClient client = getClient();
final UserInfo existingUser = client.listUser(account.getUuid(), domain.getUuid());
final CloudianUser existingUser = client.listUser(account.getUuid(), domain.getUuid());
if (existingUser != null) {
if (!existingUser.getActive() || !existingUser.getFullName().equals(fullName)) {
LOG.debug("Updating Cloudian user for account with uuid=" + account.getUuid() + " name=" + account.getAccountName());
@ -150,12 +140,12 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
}
LOG.debug("Adding Cloudian user for account with uuid=" + account.getUuid() + " name=" + account.getAccountName());
final UserInfo user = new UserInfo();
final CloudianUser user = new CloudianUser();
user.setUserId(account.getUuid());
user.setGroupId(domain.getUuid());
user.setFullName(fullName);
user.setEmailAddr(accountUser.getEmail());
user.setUserType(UserInfo.USER);
user.setUserType(CloudianUser.USER);
user.setActive(true);
return client.addUser(user);
}
@ -174,6 +164,18 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
//////////////// Plugin APIs /////////////////////
//////////////////////////////////////////////////
@Override
public String getAdminUrl() {
return String.format("%s://%s:%s", CloudianAdminProtocol.value(),
CloudianAdminHost.value(), CloudianAdminPort.value());
}
@Override
public String getCmcUrl() {
return String.format("%s://%s:%s/Cloudian/", CloudianCmcProtocol.value(),
CloudianCmcHost.value(), CloudianCmcPort.value());
}
@Override
public boolean isConnectorDisabled() {
return !CloudianConnectorEnabled.value();
@ -195,12 +197,7 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
addOrUpdateUserAccount(caller, domain);
}
final String ssoParams = CloudianUtils.generateSSOUrlParams(user, group, CloudianSsoKey.value());
if (ssoParams == null) {
return null;
}
return getCmcUrl() + ssoParams;
return CloudianUtils.generateSSOUrl(getCmcUrl(), user, group, CloudianSsoKey.value());
}
///////////////////////////////////////////////////////////

View File

@ -63,7 +63,7 @@ public class CloudianUtils {
* @param ssoKey
* @return returns SSO URL parameters or null on error
*/
public static String generateSSOUrlParams(final String user, final String group, final String ssoKey) {
public static String generateSSOUrl(final String cmcUrlPath, final String user, final String group, final String ssoKey) {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("user=").append(user);
stringBuilder.append("&group=").append(group);
@ -87,6 +87,6 @@ public class CloudianUtils {
stringBuilder.append("explorer.htm");
}
return stringBuilder.toString();
return cmcUrlPath + "ssosecurelogin.htm?" + stringBuilder.toString();
}
}

View File

@ -22,13 +22,13 @@ import javax.inject.Inject;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.response.SuccessResponse;
import com.cloud.user.Account;
import com.cloudian.cloudstack.CloudianConnector;
import com.cloudian.cloudstack.response.CloudianEnabledResponse;
@APICommand(name = CloudianIsEnabledCmd.APINAME, description = "Checks if the Cloudian Connector is enabled",
responseObject = SuccessResponse.class,
responseObject = CloudianEnabledResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
since = "4.11.0",
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
@ -55,9 +55,10 @@ public class CloudianIsEnabledCmd extends BaseCmd {
@Override
public void execute() {
final boolean isEnabled = !connector.isConnectorDisabled();
final SuccessResponse response = new SuccessResponse();
response.setSuccess(isEnabled);
final CloudianEnabledResponse response = new CloudianEnabledResponse();
response.setEnabled(!connector.isConnectorDisabled());
response.setCmcUrl(connector.getCmcUrl());
response.setObjectName(APINAME.toLowerCase());
response.setResponseName(getCommandName());
setResponseObject(response);
}

View File

@ -17,5 +17,34 @@
package com.cloudian.cloudstack.response;
public class CloudianEnabledResponse {
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
public class CloudianEnabledResponse extends BaseResponse {
@SerializedName(ApiConstants.ENABLED)
@Param(description = "the Cloudian connector enabled state")
private Boolean enabled;
@SerializedName(ApiConstants.URL)
@Param(description = "the Cloudian Management Console base URL")
private String cmcUrl;
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public String getCmcUrl() {
return cmcUrl;
}
public void setCmcUrl(String cmcUrl) {
this.cmcUrl = cmcUrl;
}
}

View File

@ -7,8 +7,8 @@ import org.junit.Before;
import org.junit.Test;
import com.cloudian.client.CloudianClient;
import com.cloudian.client.GroupInfo;
import com.cloudian.client.UserInfo;
import com.cloudian.client.CloudianGroup;
import com.cloudian.client.CloudianUser;
public class CloudianClientTest {
@ -29,7 +29,7 @@ public class CloudianClientTest {
@Test
public void listUserAccount() throws Exception {
List<UserInfo> users = client.listUsers("0");
List<CloudianUser> users = client.listUsers("0");
}
@Test
@ -38,7 +38,7 @@ public class CloudianClientTest {
@Test
public void removeUserAccount() throws Exception {
for (UserInfo user : client.listUsers("2ddabedc-4733-4cdf-80b1-abbd9d027005")) {
for (CloudianUser user : client.listUsers("2ddabedc-4733-4cdf-80b1-abbd9d027005")) {
boolean result = client.removeUser(user.getUserId(), user.getGroupId());
}
}
@ -49,7 +49,7 @@ public class CloudianClientTest {
@Test
public void listGroup() throws Exception {
List<GroupInfo> groups = client.listGroups();
List<CloudianGroup> groups = client.listGroups();
}
@Test