cloudian: wip sso integration

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2017-09-13 12:57:16 +05:30
parent 170ad669e6
commit db7697216b
6 changed files with 250 additions and 23 deletions

View File

@ -19,9 +19,12 @@ package com.cloudian.cloudstack;
import org.apache.cloudstack.framework.config.ConfigKey;
import com.cloud.domain.Domain;
import com.cloud.user.Account;
import com.cloud.utils.component.PluggableService;
public interface CloudianConnector extends PluggableService {
ConfigKey<Boolean> CloudianConnectorEnabled = new ConfigKey<>("Advanced", Boolean.class, "cloudian.connector.enabled", "false",
"If set to true, this enables the Cloudian Connector for CloudStack.", true);
@ -55,4 +58,10 @@ 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);
boolean isConnectorDisabled();
String generateSsoUrl();
boolean addGroup(final Domain domain);
boolean removeGroup(final Domain domain);
boolean addUserAccount(final Account account);
boolean removeUserAccount(final Account account);
}

View File

@ -38,9 +38,11 @@ import com.cloud.user.AccountManager;
import com.cloud.user.DomainManager;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.component.ComponentLifecycleBase;
import com.cloudian.cloudstack.api.CloudianIsEnabledCmd;
import com.cloudian.cloudstack.api.CloudianSsoLoginCmd;
public class CloudianConnectorImpl extends ComponentLifecycleBase implements CloudianConnector, Configurable {
public static final Logger LOG = Logger.getLogger(CloudianConnectorImpl.class);
private static final Logger LOG = Logger.getLogger(CloudianConnectorImpl.class);
@Inject
private AccountDao accountDao;
@ -51,6 +53,59 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
@Inject
private MessageBus messageBus;
@Override
public boolean isConnectorDisabled() {
return !CloudianConnectorEnabled.value();
}
@Override
public String generateSsoUrl() {
// add user/group in CMC if not available
// return generated login url using sso shared key
return null;
}
@Override
public boolean addGroup(final Domain domain) {
if (domain == null || isConnectorDisabled()) {
return false;
}
LOG.debug("Adding Cloudian group against domain uuid=" + domain.getUuid() + " name=" + domain.getName() + " path=" + domain.getPath());
return false;
}
@Override
public boolean removeGroup(final Domain domain) {
if (domain == null || isConnectorDisabled()) {
return false;
}
LOG.debug("Removing Cloudian group against domain uuid=" + domain.getUuid() + " name=" + domain.getName() + " path=" + domain.getPath());
return false;
}
@Override
public boolean addUserAccount(final Account account) {
if (account == null || isConnectorDisabled()) {
return false;
}
LOG.debug("Adding Cloudian user account with uuid=" + account.getUuid() + " name=" + account.getAccountName());
final Domain domain = domainDao.findById(account.getId());
return false;
}
@Override
public boolean removeUserAccount(final Account account) {
if (account == null || isConnectorDisabled()) {
return false;
}
LOG.debug("Removing Cloudian user account with uuid=" + account.getUuid() + " name=" + account.getAccountName());
final Domain domain = domainDao.findById(account.getId());
return false;
}
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
@ -59,41 +114,34 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
messageBus.subscribe(AccountManager.MESSAGE_ADD_ACCOUNT_EVENT, new MessageSubscriber() {
@Override
public void onPublishMessage(String senderAddress, String subject, Object args) {
Map<Long, Long> accountGroupMap = (Map<Long, Long>) args;
Long accountId = accountGroupMap.keySet().iterator().next();
// TODO: check and create user in CMC
final Map<Long, Long> accountGroupMap = (Map<Long, Long>) args;
final Long accountId = accountGroupMap.keySet().iterator().next();
final Account account = accountDao.findById(accountId);
LOG.info("Creating account id=" + accountId + " with uuid=" + account.getUuid());
addUserAccount(account);
}
});
messageBus.subscribe(AccountManager.MESSAGE_REMOVE_ACCOUNT_EVENT, new MessageSubscriber() {
@Override
public void onPublishMessage(String senderAddress, String subject, Object args) {
Long accountId = (Long) args;
// TODO: remove/disable user in CMC
final Account account = accountDao.findByIdIncludingRemoved(accountId);
LOG.info("Removing account id=" + accountId + " with uuid=" + account.getUuid());
final Account account = accountDao.findByIdIncludingRemoved((Long) args);
removeUserAccount(account);
}
});
messageBus.subscribe(DomainManager.MESSAGE_ADD_DOMAIN_EVENT, new MessageSubscriber() {
@Override
public void onPublishMessage(String senderAddress, String subject, Object args) {
Long domainId = (Long) args;
Domain domain = domainDao.findById(domainId);
// TODO: check and create group in CMC
LOG.info("Adding domain id=" + domainId + " with uuid=" + domain.getUuid());
final Domain domain = domainDao.findById((Long) args);
addGroup(domain);
}
});
messageBus.subscribe(DomainManager.MESSAGE_REMOVE_DOMAIN_EVENT, new MessageSubscriber() {
@Override
public void onPublishMessage(String senderAddress, String subject, Object args) {
DomainVO domain = (DomainVO) args;
// TODO: remove/disable group in CMC
LOG.info("Removing domain id=" + domain.getId() + " with uuid=" + domain.getUuid());
final DomainVO domain = (DomainVO) args;
removeGroup(domain);
}
});
@ -102,10 +150,12 @@ public class CloudianConnectorImpl extends ComponentLifecycleBase implements Clo
@Override
public List<Class<?>> getCommands() {
List<Class<?>> cmdList = new ArrayList<Class<?>>();
if (!CloudianConnectorEnabled.value()) {
final List<Class<?>> cmdList = new ArrayList<Class<?>>();
cmdList.add(CloudianIsEnabledCmd.class);
if (isConnectorDisabled()) {
return cmdList;
}
cmdList.add(CloudianSsoLoginCmd.class);
return cmdList;
}

View File

@ -0,0 +1,65 @@
// 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 com.cloudian.cloudstack.api;
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;
@APICommand(name = CloudianIsEnabledCmd.APINAME, description = "Checks if the Cloudian Connector is enabled",
responseObject = SuccessResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
since = "4.11.0",
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
public class CloudianIsEnabledCmd extends BaseCmd {
public static final String APINAME = "cloudianIsEnabled";
@Inject
private CloudianConnector connector;
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public String getCommandName() {
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
@Override
public void execute() {
final boolean isEnabled = !connector.isConnectorDisabled();
final SuccessResponse response = new SuccessResponse();
response.setSuccess(isEnabled);
response.setDisplayText(isEnabled ? "Cloudian Connector is enabled" : "Cloudian Connector is disabled");
response.setResponseName(getCommandName());
setResponseObject(response);
}
}

View File

@ -0,0 +1,62 @@
// 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 com.cloudian.cloudstack.api;
import javax.inject.Inject;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.BaseCmd;
import com.cloud.user.Account;
import com.cloudian.cloudstack.CloudianConnector;
import com.cloudian.cloudstack.response.CloudianSsoLoginResponse;
@APICommand(name = CloudianSsoLoginCmd.APINAME, description = "Generates single-sign-on login url for logged-in CloudStack user to access the Cloudian Management Console",
responseObject = CloudianSsoLoginResponse.class,
since = "4.11.0",
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
public class CloudianSsoLoginCmd extends BaseCmd {
public static final String APINAME = "cloudianSsoLogin";
@Inject
private CloudianConnector connector;
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public String getCommandName() {
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
@Override
public void execute() {
final CloudianSsoLoginResponse response = new CloudianSsoLoginResponse();
response.setSsoRedirectUrl(connector.generateSsoUrl());
response.setResponseName(getCommandName());
setResponseObject(response);
}
}

View File

@ -15,7 +15,24 @@
// specific language governing permissions and limitations
// under the License.
package com.cloudian.cloudstack.api;
package com.cloudian.cloudstack.response;
public class CloudianSsoCmd {
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 CloudianSsoLoginResponse extends BaseResponse {
@SerializedName(ApiConstants.URL)
@Param(description = "the sso redirect url")
private String ssoRedirectUrl;
public String getSsoRedirectUrl() {
return ssoRedirectUrl;
}
public void setSsoRedirectUrl(final String ssoRedirectUrl) {
this.ssoRedirectUrl = ssoRedirectUrl;
}
}

View File

@ -22,11 +22,35 @@
title: 'Cloudian Storage',
showOnNavigation: true,
preFilter: function(args) {
// FIXME: show/hide plugin based on if plugin/connector is enabled?
return true;
var pluginEnabled = false;
$.ajax({
url: createURL('cloudianIsEnabled'),
async: false,
success: function(json) {
pluginEnabled = json.cloudianisenabledresponse.success;
},
error: function(data) {
pluginEnabled = false;
}
});
return pluginEnabled;
},
show: function() {
return $('<div>').html('Cloudian Storage section');
var ssoUrl = '';
var description = 'Cloudian Storage should open in another window.';
$.ajax({
url: createURL('cloudianSsoLogin'),
async: false,
success: function(json) {
ssoUrl = json.cloudianssologinresponse.url;
// open new tab using http POST
},
error: function(data) {
description = 'Single-Sign-On failed for Cloudian Storage.';
}
});
return $('<div>').html(description);
}
});
};