saml: Implement logic to check response against X509 keys

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2014-08-24 20:48:25 +02:00
parent 47ccce85a1
commit 7687b7311a
3 changed files with 90 additions and 22 deletions

View File

@ -24,11 +24,13 @@ import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.auth.APIAuthenticationType;
import org.apache.cloudstack.api.auth.APIAuthenticator;
import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
import org.apache.cloudstack.api.response.LogoutCmdResponse;
import org.apache.log4j.Logger;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Map;
@APICommand(name = "samlslo", description = "SAML Global Log Out API", responseObject = LogoutCmdResponse.class, entityType = {})
@ -70,4 +72,8 @@ public class SAML2LogoutAPIAuthenticatorCmd extends BaseCmd implements APIAuthen
public APIAuthenticationType getAPIType() {
return APIAuthenticationType.LOGOUT_API;
}
@Override
public void setAuthenticators(List<PluggableAPIAuthenticator> authenticators) {
}
}

View File

@ -17,11 +17,20 @@
package org.apache.cloudstack.saml;
public interface SAML2AuthManager {
public String getServiceProviderId();
public String getSpSingleSignOnUrl();
public String getSpSingleLogOutUrl();
import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
import java.security.cert.X509Certificate;
public interface SAML2AuthManager extends PluggableAPIAuthenticator {
public String getServiceProviderId();
public String getIdentityProviderId();
public X509Certificate getIdpSigningKey();
public X509Certificate getIdpEncryptionKey();
public String getSpSingleSignOnUrl();
public String getIdpSingleSignOnUrl();
public String getSpSingleLogOutUrl();
public String getIdpSingleLogOutUrl();
}

View File

@ -23,32 +23,48 @@ 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.DefaultBootstrap;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml2.metadata.IDPSSODescriptor;
import org.opensaml.saml2.metadata.KeyDescriptor;
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.ConfigurationException;
import org.opensaml.xml.parse.BasicParserPool;
import org.opensaml.xml.security.credential.UsageType;
import org.opensaml.xml.security.keyinfo.KeyInfoHelper;
import org.springframework.stereotype.Component;
import javax.ejb.Local;
import javax.inject.Inject;
import javax.xml.stream.FactoryConfigurationError;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
@Component
@Local(value = {PluggableAPIAuthenticator.class, SAML2AuthManager.class})
public class SAML2AuthManagerImpl extends AdapterBase implements PluggableAPIAuthenticator, SAML2AuthManager {
@Local(value = {SAML2AuthManager.class, PluggableAPIAuthenticator.class})
public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManager {
private static final Logger s_logger = Logger.getLogger(SAML2AuthManagerImpl.class);
private String serviceProviderId;
private String spSingleSignOnUrl;
private String spSingleLogOutUrl;
private String identityProviderId;
private X509Certificate idpSigningKey;
private X509Certificate idpEncryptionKey;
private String spSingleSignOnUrl;
private String idpSingleSignOnUrl;
private String spSingleLogOutUrl;
private String idpSingleLogOutUrl;
private HTTPMetadataProvider idpMetaDataProvider;
@Inject
ConfigurationDao _configDao;
@ -59,6 +75,8 @@ public class SAML2AuthManagerImpl extends AdapterBase implements PluggableAPIAut
@Override
public boolean start() {
this.serviceProviderId = _configDao.getValue(Config.SAMLServiceProviderID.key());
this.identityProviderId = _configDao.getValue(Config.SAMLIdentityProviderID.key());
this.spSingleSignOnUrl = _configDao.getValue(Config.SAMLServiceProviderSingleSignOnURL.key());
this.spSingleLogOutUrl = _configDao.getValue(Config.SAMLServiceProviderSingleLogOutURL.key());
@ -71,31 +89,54 @@ public class SAML2AuthManagerImpl extends AdapterBase implements PluggableAPIAut
}
try {
HTTPMetadataProvider idpMetaDataProvider = new HTTPMetadataProvider(idpMetaDataUrl, tolerance);
DefaultBootstrap.bootstrap();
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();
}
}
EntityDescriptor idpEntityDescriptor = idpMetaDataProvider.getEntityDescriptor(this.identityProviderId);
IDPSSODescriptor idpssoDescriptor = idpEntityDescriptor.getIDPSSODescriptor(SAMLConstants.SAML20P_NS);
if (idpssoDescriptor != null) {
for (SingleSignOnService ssos: idpssoDescriptor.getSingleSignOnServices()) {
if (ssos.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) {
this.idpSingleSignOnUrl = ssos.getLocation();
}
}
for (SingleLogoutService slos: idpssoDescriptor.getSingleLogoutServices()) {
if (slos.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) {
this.idpSingleLogOutUrl = slos.getLocation();
}
}
for (KeyDescriptor kd: idpssoDescriptor.getKeyDescriptors()) {
if (kd.getUse() == UsageType.SIGNING) {
try {
this.idpSigningKey = KeyInfoHelper.getCertificates(kd.getKeyInfo()).get(0);
} catch (CertificateException ignored) {
}
}
if (kd.getUse() == UsageType.ENCRYPTION) {
try {
this.idpEncryptionKey = KeyInfoHelper.getCertificates(kd.getKeyInfo()).get(0);
} catch (CertificateException ignored) {
}
}
}
} else {
s_logger.warn("Provided IDP XML Metadata does not contain IDPSSODescriptor, SAML authentication may not work");
}
} catch (MetadataProviderException e) {
s_logger.error("Unable to read SAML2 IDP MetaData URL, error:" + e.getMessage());
s_logger.error("SAML2 Authentication may be unavailable");
} catch (ConfigurationException | FactoryConfigurationError e) {
s_logger.error("OpenSAML bootstrapping failed: error: " + e.getMessage());
}
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");
s_logger.error("SAML based authentication won't work");
}
return true;
@ -128,4 +169,16 @@ public class SAML2AuthManagerImpl extends AdapterBase implements PluggableAPIAut
public String getSpSingleLogOutUrl() {
return spSingleLogOutUrl;
}
public String getIdentityProviderId() {
return identityProviderId;
}
public X509Certificate getIdpSigningKey() {
return idpSigningKey;
}
public X509Certificate getIdpEncryptionKey() {
return idpEncryptionKey;
}
}