mirror of https://github.com/apache/cloudstack.git
saml: Have the plugin use IDP metadata from URL, get values from Config
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
1b0f81ec6f
commit
06e909923a
|
|
@ -29,7 +29,7 @@
|
|||
<property name="name" value="SAML2"/>
|
||||
</bean>
|
||||
|
||||
<bean id="SAML2Manager" class="org.apache.cloudstack.saml.SAML2AuthServiceImpl">
|
||||
<bean id="SAML2Manager" class="org.apache.cloudstack.saml.SAML2AuthManagerImpl">
|
||||
<property name="name" value="SAML2Auth"/>
|
||||
</bean>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package org.apache.cloudstack.api.command;
|
||||
|
||||
import org.apache.cloudstack.api.ApiServerService;
|
||||
import com.cloud.api.response.ApiResponseSerializer;
|
||||
import com.cloud.exception.CloudAuthenticationException;
|
||||
import com.cloud.user.Account;
|
||||
|
|
@ -27,6 +26,7 @@ import com.cloud.utils.db.EntityManager;
|
|||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.ApiServerService;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
|
|
@ -34,6 +34,7 @@ import org.apache.cloudstack.api.auth.APIAuthenticationType;
|
|||
import org.apache.cloudstack.api.auth.APIAuthenticator;
|
||||
import org.apache.cloudstack.api.response.LoginCmdResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.saml.SAML2AuthManager;
|
||||
import org.apache.cloudstack.utils.auth.SAMLUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.opensaml.DefaultBootstrap;
|
||||
|
|
@ -79,6 +80,8 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
ApiServerService _apiServer;
|
||||
@Inject
|
||||
EntityManager _entityMgr;
|
||||
@Inject
|
||||
SAML2AuthManager _samlAuthManager;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
|
|
@ -108,13 +111,20 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is an authentication api, cannot be used directly");
|
||||
}
|
||||
|
||||
public String buildAuthnRequestUrl(String consumerUrl, String identityProviderUrl) {
|
||||
String randomId = new BigInteger(130, new SecureRandom()).toString(32);
|
||||
String spId = "org.apache.cloudstack";
|
||||
public String buildAuthnRequestUrl(String idpUrl) {
|
||||
String randomSecureId = new BigInteger(130, new SecureRandom()).toString(32);
|
||||
String spId = _samlAuthManager.getServiceProviderId();
|
||||
String consumerUrl = _samlAuthManager.getSpSingleSignOnUrl();
|
||||
String identityProviderUrl = _samlAuthManager.getIdpSingleSignOnUrl();
|
||||
|
||||
if (idpUrl != null) {
|
||||
identityProviderUrl = idpUrl;
|
||||
}
|
||||
|
||||
String redirectUrl = "";
|
||||
try {
|
||||
DefaultBootstrap.bootstrap();
|
||||
AuthnRequest authnRequest = SAMLUtils.buildAuthnRequestObject(randomId, spId, identityProviderUrl, consumerUrl);
|
||||
AuthnRequest authnRequest = SAMLUtils.buildAuthnRequestObject(randomSecureId, spId, identityProviderUrl, consumerUrl);
|
||||
redirectUrl = identityProviderUrl + "?SAMLRequest=" + SAMLUtils.encodeSAMLRequest(authnRequest);
|
||||
} catch (ConfigurationException | FactoryConfigurationError | MarshallingException | IOException e) {
|
||||
s_logger.error("SAML AuthnRequest message building error: " + e.getMessage());
|
||||
|
|
@ -137,8 +147,12 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
|
|||
public String authenticate(final String command, final Map<String, Object[]> params, final HttpSession session, final String remoteAddress, final String responseType, final StringBuilder auditTrailSb, final HttpServletResponse resp) throws ServerApiException {
|
||||
try {
|
||||
if (!params.containsKey("SAMLResponse")) {
|
||||
final String[] idps = (String[])params.get("idpurl");
|
||||
String redirectUrl = buildAuthnRequestUrl("http://localhost:8080/client/api?command=samlsso", idps[0]);
|
||||
String idpUrl = null;
|
||||
final String[] idps = (String[])params.get(ApiConstants.IDP_URL);
|
||||
if (idps != null && idps.length > 0) {
|
||||
idpUrl = idps[0];
|
||||
}
|
||||
String redirectUrl = buildAuthnRequestUrl(idpUrl);
|
||||
resp.sendRedirect(redirectUrl);
|
||||
return "";
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
// 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 org.apache.cloudstack.saml;
|
||||
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.utils.component.AdapterBase;
|
||||
import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
|
||||
import org.apache.cloudstack.api.command.SAML2LoginAPIAuthenticatorCmd;
|
||||
import org.apache.cloudstack.api.command.SAML2LogoutAPIAuthenticatorCmd;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.opensaml.common.xml.SAMLConstants;
|
||||
import org.opensaml.saml2.metadata.EntityDescriptor;
|
||||
import org.opensaml.saml2.metadata.SingleLogoutService;
|
||||
import org.opensaml.saml2.metadata.SingleSignOnService;
|
||||
import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider;
|
||||
import org.opensaml.saml2.metadata.provider.MetadataProviderException;
|
||||
import org.opensaml.xml.parse.BasicParserPool;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.ejb.Local;
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Local(value = {PluggableAPIAuthenticator.class, SAML2AuthManager.class})
|
||||
public class SAML2AuthManagerImpl extends AdapterBase implements PluggableAPIAuthenticator, SAML2AuthManager {
|
||||
private static final Logger s_logger = Logger.getLogger(SAML2AuthManagerImpl.class);
|
||||
|
||||
private String serviceProviderId;
|
||||
private String spSingleSignOnUrl;
|
||||
private String spSingleLogOutUrl;
|
||||
|
||||
private String idpSingleSignOnUrl;
|
||||
private String idpSingleLogOutUrl;
|
||||
|
||||
@Inject
|
||||
ConfigurationDao _configDao;
|
||||
|
||||
protected SAML2AuthManagerImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
this.serviceProviderId = _configDao.getValue(Config.SAMLServiceProviderID.key());
|
||||
this.spSingleSignOnUrl = _configDao.getValue(Config.SAMLServiceProviderSingleSignOnURL.key());
|
||||
this.spSingleLogOutUrl = _configDao.getValue(Config.SAMLServiceProviderSingleLogOutURL.key());
|
||||
|
||||
String idpMetaDataUrl = _configDao.getValue(Config.SAMLIdentityProviderMetadataURL.key());
|
||||
|
||||
int tolerance = 30000;
|
||||
String timeout = _configDao.getValue(Config.SAMLTimeout.key());
|
||||
if (timeout != null) {
|
||||
tolerance = Integer.parseInt(timeout);
|
||||
}
|
||||
|
||||
try {
|
||||
HTTPMetadataProvider idpMetaDataProvider = new HTTPMetadataProvider(idpMetaDataUrl, tolerance);
|
||||
|
||||
idpMetaDataProvider.setRequireValidMetadata(true);
|
||||
idpMetaDataProvider.setParserPool(new BasicParserPool());
|
||||
idpMetaDataProvider.initialize();
|
||||
|
||||
EntityDescriptor idpEntityDescriptor = idpMetaDataProvider.getEntityDescriptor("Some entity id");
|
||||
for (SingleSignOnService ssos: idpEntityDescriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS).getSingleSignOnServices()) {
|
||||
if (ssos.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) {
|
||||
this.idpSingleSignOnUrl = ssos.getLocation();
|
||||
}
|
||||
}
|
||||
for (SingleLogoutService slos: idpEntityDescriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS).getSingleLogoutServices()) {
|
||||
if (slos.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) {
|
||||
this.idpSingleLogOutUrl = slos.getLocation();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (MetadataProviderException e) {
|
||||
s_logger.error("Unable to read SAML2 IDP MetaData URL, error:" + e.getMessage());
|
||||
s_logger.error("SAML2 Authentication may be unavailable");
|
||||
}
|
||||
|
||||
if (this.idpSingleLogOutUrl == null || this.idpSingleSignOnUrl == null) {
|
||||
s_logger.error("The current IDP does not support HTTP redirected authentication, SAML based authentication cannot work with this IDP");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getAuthCommands() {
|
||||
List<Class<?>> cmdList = new ArrayList<Class<?>>();
|
||||
cmdList.add(SAML2LoginAPIAuthenticatorCmd.class);
|
||||
cmdList.add(SAML2LogoutAPIAuthenticatorCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
|
||||
public String getServiceProviderId() {
|
||||
return serviceProviderId;
|
||||
}
|
||||
|
||||
public String getIdpSingleSignOnUrl() {
|
||||
return this.idpSingleSignOnUrl;
|
||||
}
|
||||
|
||||
public String getIdpSingleLogOutUrl() {
|
||||
return this.idpSingleLogOutUrl;
|
||||
}
|
||||
|
||||
public String getSpSingleSignOnUrl() {
|
||||
return spSingleSignOnUrl;
|
||||
}
|
||||
|
||||
public String getSpSingleLogOutUrl() {
|
||||
return spSingleLogOutUrl;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
// 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 org.apache.cloudstack.saml;
|
||||
|
||||
import com.cloud.utils.component.AdapterBase;
|
||||
import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
|
||||
import org.apache.cloudstack.api.command.SAML2LoginAPIAuthenticatorCmd;
|
||||
import org.apache.cloudstack.api.command.SAML2LogoutAPIAuthenticatorCmd;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.ejb.Local;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Local(value = PluggableAPIAuthenticator.class)
|
||||
public class SAML2AuthServiceImpl extends AdapterBase implements PluggableAPIAuthenticator {
|
||||
private static final Logger s_logger = Logger.getLogger(SAML2AuthServiceImpl.class);
|
||||
|
||||
protected SAML2AuthServiceImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getAuthCommands() {
|
||||
List<Class<?>> cmdList = new ArrayList<Class<?>>();
|
||||
cmdList.add(SAML2LoginAPIAuthenticatorCmd.class);
|
||||
cmdList.add(SAML2LogoutAPIAuthenticatorCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue