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:
Rohit Yadav 2014-08-24 17:34:20 +02:00
parent 1b0f81ec6f
commit 06e909923a
4 changed files with 153 additions and 59 deletions

View File

@ -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>

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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;
}
}