From db7697216bd5a0de86def960616937182ab4e493 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 13 Sep 2017 12:57:16 +0530 Subject: [PATCH] cloudian: wip sso integration Signed-off-by: Rohit Yadav --- .../cloudstack/CloudianConnector.java | 9 ++ .../cloudstack/CloudianConnectorImpl.java | 88 +++++++++++++++---- .../cloudstack/api/CloudianIsEnabledCmd.java | 65 ++++++++++++++ .../cloudstack/api/CloudianSsoLoginCmd.java | 62 +++++++++++++ .../CloudianSsoLoginResponse.java} | 21 ++++- ui/plugins/cloudian/cloudian.js | 28 +++++- 6 files changed, 250 insertions(+), 23 deletions(-) create mode 100644 plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianIsEnabledCmd.java create mode 100644 plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianSsoLoginCmd.java rename plugins/connectors/cloudian/src/com/cloudian/cloudstack/{api/CloudianSsoCmd.java => response/CloudianSsoLoginResponse.java} (57%) diff --git a/plugins/connectors/cloudian/src/com/cloudian/cloudstack/CloudianConnector.java b/plugins/connectors/cloudian/src/com/cloudian/cloudstack/CloudianConnector.java index 5ccef1efab9..02d8b4a21b2 100644 --- a/plugins/connectors/cloudian/src/com/cloudian/cloudstack/CloudianConnector.java +++ b/plugins/connectors/cloudian/src/com/cloudian/cloudstack/CloudianConnector.java @@ -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 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 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); } diff --git a/plugins/connectors/cloudian/src/com/cloudian/cloudstack/CloudianConnectorImpl.java b/plugins/connectors/cloudian/src/com/cloudian/cloudstack/CloudianConnectorImpl.java index b86eeba4f75..59e056cfca1 100644 --- a/plugins/connectors/cloudian/src/com/cloudian/cloudstack/CloudianConnectorImpl.java +++ b/plugins/connectors/cloudian/src/com/cloudian/cloudstack/CloudianConnectorImpl.java @@ -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 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 accountGroupMap = (Map) args; - Long accountId = accountGroupMap.keySet().iterator().next(); - // TODO: check and create user in CMC + final Map accountGroupMap = (Map) 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> getCommands() { - List> cmdList = new ArrayList>(); - if (!CloudianConnectorEnabled.value()) { + final List> cmdList = new ArrayList>(); + cmdList.add(CloudianIsEnabledCmd.class); + if (isConnectorDisabled()) { return cmdList; } + cmdList.add(CloudianSsoLoginCmd.class); return cmdList; } diff --git a/plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianIsEnabledCmd.java b/plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianIsEnabledCmd.java new file mode 100644 index 00000000000..0f2b63731dd --- /dev/null +++ b/plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianIsEnabledCmd.java @@ -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); + } +} diff --git a/plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianSsoLoginCmd.java b/plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianSsoLoginCmd.java new file mode 100644 index 00000000000..d1c9f56d9d7 --- /dev/null +++ b/plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianSsoLoginCmd.java @@ -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); + } +} diff --git a/plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianSsoCmd.java b/plugins/connectors/cloudian/src/com/cloudian/cloudstack/response/CloudianSsoLoginResponse.java similarity index 57% rename from plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianSsoCmd.java rename to plugins/connectors/cloudian/src/com/cloudian/cloudstack/response/CloudianSsoLoginResponse.java index 21b33093be5..03f220dffef 100644 --- a/plugins/connectors/cloudian/src/com/cloudian/cloudstack/api/CloudianSsoCmd.java +++ b/plugins/connectors/cloudian/src/com/cloudian/cloudstack/response/CloudianSsoLoginResponse.java @@ -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; + } } diff --git a/ui/plugins/cloudian/cloudian.js b/ui/plugins/cloudian/cloudian.js index 4163b5ba922..6f6e294de71 100644 --- a/ui/plugins/cloudian/cloudian.js +++ b/ui/plugins/cloudian/cloudian.js @@ -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 $('
').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 $('
').html(description); } }); };