/* (c) 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.security.onelogin;
import java.io.IOException;
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.geoserver.security.config.SecurityNamedServiceConfig;
import org.geoserver.security.filter.GeoServerLogoutFilter;
import org.geoserver.security.filter.GeoServerSecurityFilter;
import org.geoserver.security.impl.GeoServerRole;
import org.geotools.util.logging.Logging;
import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider;
import org.opensaml.xml.parse.ParserPool;
import org.springframework.context.ApplicationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.saml.SAMLEntryPoint;
import org.springframework.security.saml.SAMLLogoutFilter;
import org.springframework.security.saml.key.EmptyKeyManager;
import org.springframework.security.saml.metadata.ExtendedMetadata;
import org.springframework.security.saml.metadata.ExtendedMetadataDelegate;
import org.springframework.security.saml.metadata.MetadataGenerator;
import org.springframework.security.saml.metadata.MetadataGeneratorFilter;
import org.springframework.security.saml.metadata.MetadataManager;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutHandler;
/**
* OneLogin Authentication filter that configures SP metadata discovery filter and delegates to {@link #SAMLEntryPoint} the SAML authentication
* process
*
* @author Xandros
*/
public class OneloginAuthenticationFilter extends GeoServerPreAuthenticatedCompositeUserNameFilter
implements LogoutHandler {
static final Logger LOGGER = Logging.getLogger(OneloginAuthenticationFilter.class);
protected SAMLEntryPoint samlEntryPoint;
private static ApplicationContext context;
public OneloginAuthenticationFilter(ApplicationContext ctx) {
context = ctx;
this.samlEntryPoint = context.getBean(SAMLEntryPoint.class);
}
/**
* Configures {@link MetadataGenerator} using EntityId and ID MetadataURL from filter configuration<br/>
* Configures SAMLUserDetailsService to use {@link GeoServerRole} selected provider
*/
@Override
public void initializeFromConfig(SecurityNamedServiceConfig config) throws IOException {
super.initializeFromConfig(config);
OneloginAuthenticationFilterConfig authConfig = (OneloginAuthenticationFilterConfig) config;
try {
if (getNestedFilters().isEmpty()) {
/*
* Create metadata filter
*/
MetadataGenerator generator = new MetadataGenerator();
generator.setEntityId(authConfig.getEntityId());
generator.setIncludeDiscoveryExtension(false);
generator.setKeyManager(new EmptyKeyManager());
generator.setRequestSigned(false);
generator.setWantAssertionSigned(authConfig.getWantAssertionSigned());
ExtendedMetadata em = new ExtendedMetadata();
em.setRequireLogoutRequestSigned(false);
generator.setExtendedMetadata(em);
MetadataGeneratorFilter metadataGeneratorFilter = new MetadataGeneratorFilter(
generator);
/*
* Create metadata provider
*/
ParserPool parserPool = context.getBean(ParserPool.class);
HttpClientParams clientParams = new HttpClientParams();
clientParams.setSoTimeout(5000);
HttpClient httpClient = new HttpClient(clientParams);
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
HTTPMetadataProvider pro = new HTTPMetadataProvider(new Timer(true), httpClient,
authConfig.getMetadataURL());
pro.setParserPool(parserPool);
/*
* Use this to pass metadata string String xml =
* "<?xml version=\"1.0\"?> <EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"https://app.onelogin.com/saml/metadata/575443\"> <IDPSSODescriptor xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"> <KeyDescriptor use=\"signing\"> <ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"> <ds:X509Data> <ds:X509Certificate>MIIEHTCCAwWgAwIBAgIUIYlArFxWB1C7BX6q/mFytCIYeI8wDQYJKoZIhvcNAQEF BQAwWjELMAkGA1UEBhMCVVMxEzARBgNVBAoMCmNvZGVzdHVkaW8xFTATBgNVBAsM DE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4ODk4ODAe Fw0xNjA4MDMxMDUyNTNaFw0yMTA4MDQxMDUyNTNaMFoxCzAJBgNVBAYTAlVTMRMw EQYDVQQKDApjb2Rlc3R1ZGlvMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNV BAMMFk9uZUxvZ2luIEFjY291bnQgODg5ODgwggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQDWKHu+QvLa9BvqL6OoKKMVMBY0deHU6xqPAoatxiGlNIazoK8T PY5srjRX18W4aOb9In3zEulipGfNaQ0Avj/Jhi1UbS9lJMVNNODZ0dzfkJhIlpkG z7+totPf5P1BdUBTNk7OpguDLsb5DXKm5ZhGSDzMgGGNDNdOEZpJJ1zVjskUkmR2 frea+ZcpMkNa9CB6Jf6d6oE2BNhW94d1F4N5KB0NbmFonzLa3N5vRHstM88DbFSO UM7N9SRf+8Jnviae7fcG12woE+25G4qB1wJ1rfIu9wL7JhiXLPA7FB4L4bRglXZs Vin9sY93QyUmrv8kxNrtwLXnQopu0myfG3CXAgMBAAGjgdowgdcwDAYDVR0TAQH/ BAIwADAdBgNVHQ4EFgQUvz2fdMwH1G1W8bbcAWN238szW34wgZcGA1UdIwSBjzCB jIAUvz2fdMwH1G1W8bbcAWN238szW36hXqRcMFoxCzAJBgNVBAYTAlVTMRMwEQYD VQQKDApjb2Rlc3R1ZGlvMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMM Fk9uZUxvZ2luIEFjY291bnQgODg5ODiCFCGJQKxcVgdQuwV+qv5hcrQiGHiPMA4G A1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEANkaO/xJag1n93l+6/sbl cG/1Oi3/hI19+lp0PU26kFtkrpzfjE4QugBgnGXkeJ/MU6abk650uh+7yLqkY15G m9Lsk0XiH1k7vWWnQl12Yj0uwCY47baBLw0lMCl5vNaJcULicAM715W3d2oHptnh ftePShyHHD69Z4e+UduuClbXjSxPNB9zTOsLYRVbrX+fIFm1AK8bWcmrH/jAuj7p WP7hRJdT3jG5N7LNb4th3Ojj47NksjaPo2nOuydZvyoL2CJ/E2qJeW78V6oqXCB3 D/XVIWWdUmYMNNmXksT9MJCtZWvnV3OmdYbyZjOK2bJK7fbVgm/gwpeBSY+pquMG AA==</ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </KeyDescriptor> <SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://codestudio-dev.onelogin.com/trust/saml2/http-redirect/slo/575443\"/> <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat> <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://codestudio-dev.onelogin.com/trust/saml2/http-redirect/sso/575443\"/> <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://codestudio-dev.onelogin.com/trust/saml2/http-post/sso/575443\"/> <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:SOAP\" Location=\"https://codestudio-dev.onelogin.com/trust/saml2/soap/sso/575443\"/> <SingleLogoutService Location=\"https://codestudio-dev.onelogin.com/trust/saml2/http-redirect/slo/575443\" Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"/> </IDPSSODescriptor> <ContactPerson contactType=\"technical\"> <SurName>Support</SurName> <EmailAddress>support@onelogin.com</EmailAddress> </ContactPerson> </EntityDescriptor>"
* ; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db =
* dbf.newDocumentBuilder(); Document doc = db.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
*
* DOMMetadataProvider pro = new DOMMetadataProvider(doc.getDocumentElement()); pro.setParserPool(parserPool); pro.initialize();
*/
ExtendedMetadataDelegate emd = new ExtendedMetadataDelegate(pro, em);
/*
* Set metadata provider and add filter to chain
*/
MetadataManager metadata = context.getBean(MetadataManager.class);
metadata.addMetadataProvider(emd);
metadata.refreshMetadata();
metadataGeneratorFilter.setManager(metadata);
getNestedFilters().add(metadataGeneratorFilter);
} else {
LOGGER.log(Level.FINE, "Metadata filter already added");
}
/*
* Inject UserGroup and Role configuration into SAML user details service
*/
SAMLUserDetailsServiceImpl usd = context.getBean(SAMLUserDetailsServiceImpl.class);
usd.setConverter(this.getConverter());
usd.setRoleServiceName(this.getRoleServiceName());
usd.setRolesHeaderAttribute(this.getRolesHeaderAttribute());
usd.setRoleSource(this.getRoleSource());
usd.setSecurityManager(this.securityManager);
usd.setUserGroupServiceName(this.getUserGroupServiceName());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@Override
public AuthenticationEntryPoint getAuthenticationEntryPoint() {
return this.samlEntryPoint;
}
/**
* Injects current request into {@link SAMLUserDetailsServiceImpl} and sets {@link SAMLEntryPoint} as filter entry point
*/
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
req.setAttribute(GeoServerSecurityFilter.AUTHENTICATION_ENTRY_POINT_HEADER,
this.samlEntryPoint);
/*
* Inject current request into SAML user details service
*/
SAMLUserDetailsServiceImpl usd = context.getBean(SAMLUserDetailsServiceImpl.class);
usd.setRequest((HttpServletRequest) req);
super.doFilter(req, res, chain);
}
@Override
public boolean applicableForHtml() {
return true;
}
@Override
public boolean applicableForServices() {
return true;
}
@Override
public void logout(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) {
request.setAttribute(GeoServerLogoutFilter.LOGOUT_REDIRECT_ATTR,
SAMLLogoutFilter.FILTER_URL);
}
@Override
protected String getPreAuthenticatedPrincipalName(HttpServletRequest request) {
// TODO Auto-generated method stub
return null;
}
}