001/*
002 * Copyright (c) 2013 - 2016 TDP Ltd All Rights Reserved.
003 * TDP Ltd grants permission, free of charge, to any person obtaining copies
004 * of this software and its associated documentation files (the "Software"),
005 * to deal in the Software without restriction, including to use, copy, adapt,
006 * publish, distribute, display, perform, sublicense, and sell copies of the
007 * Software, subject to the following condition: You must include the above
008 * copyright notice and this permission notice in all full or partial copies
009 * of the Software.
010 * 
011 * TDP LTD PROVIDES THE SOFTWARE "AS IS," WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,
012 * INCLUDING WITHOUT THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
013 * PARTICULAR PURPOSE, AND NON-INFRINGMENT. TDP LTD, THE AUTHORS OF THE SOFTWARE,
014 * AND THE OWNERS OF COPYRIGHT IN THE SOFTWARE ARE NOT LIABLE FOR ANY CLAIM, DAMAGES,
015 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING
016 * FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
017 * THE SOFTWARE.
018 */
019package cz.tdp.kshield.integration.web;
020
021import java.io.IOException;
022import java.lang.reflect.Constructor;
023
024import javax.servlet.Filter;
025import javax.servlet.FilterChain;
026import javax.servlet.FilterConfig;
027import javax.servlet.ServletException;
028import javax.servlet.ServletRequest;
029import javax.servlet.ServletResponse;
030import javax.servlet.http.HttpServletRequest;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.springframework.beans.BeanWrapper;
035import org.springframework.beans.PropertyAccessorFactory;
036
037import cz.tdp.kshield.integration.KShieldContext;
038import cz.tdp.kshield.integration.ServletAuthenticationService;
039
040
041/**
042 * <b>This is main class of web intergation package.</b><br/>
043 * <br/>
044 * This filter could be used in pure Servlet 2.5 environment and via Spring Framework. You need to position this filter before any authentication check in your application.
045 * Retrieved UserInfo instance is stored in session attribute {@link ServletAuthenticationService#KSHIELD_PRINCIPAL_TOKEN} after successful validation.
046 * You can use this data for furher validation or other authenticanion steps required in your application.<br/>
047 * <b>Example configuration in web.xml:</b>
048<pre>{@code
049
050<filter>
051  <filter-name>KShieldAuthFilter</filter-name>
052  <filter-class>cz.tdp.kshield.integration.web.KShieldAuthenticationFilter</filter-class>
053  <init-param>
054    <param-name>serviceClass</param-name>
055    <param-value>cz.tdp.kshield.integration.DefaultAuthenticationServiceImpl</param-value>
056  </init-param>
057  <init-param>
058    <param-name>serverUrl</param-name>
059    <param-value>http://127.0.0.1:8485</param-value>
060  </init-param>
061</filter>
062
063<filter-mapping>
064  <filter-name>KShieldAuthFilter</filter-name>
065  <url-pattern>*.jsp</url-pattern>
066</filter-mapping>
067
068}</pre>
069 *
070 * @see #init(FilterConfig)
071 * @see DefaultAuthenticationServiceImpl
072 * @see Filter
073 */
074public class KShieldAuthenticationFilter implements Filter
075{
076  @Override
077  public void doFilter(ServletRequest request, ServletResponse response, FilterChain filters) throws IOException, ServletException {
078    if (request instanceof HttpServletRequest) {
079      final HttpServletRequest httpReq = (HttpServletRequest)request;
080      
081      if (authenticationService.authenticate(httpReq)) {
082        log.info("Authenticated to KeyShield SSO Server");
083      }
084    }
085    
086    try {
087      filters.doFilter(request, response);
088    }
089    finally {
090      KShieldContext.closeKShieldSession();
091    }
092  }
093
094  @Override
095  public void init(FilterConfig config) throws ServletException {
096    if (authenticationService == null) {
097      // initialize sso authentication service from filter init parameters
098      final String serverUrl = config.getInitParameter("serverUrl");
099      
100      if (serverUrl != null) {
101        final String serviceClassName = config.getInitParameter("serviceClass");
102        
103        if (serviceClassName != null) {
104          try {
105            @SuppressWarnings("unchecked")
106            final Class<ServletAuthenticationService> serviceClass = (Class<ServletAuthenticationService>)Class.forName(serviceClassName);
107            final Constructor<ServletAuthenticationService> constructor = serviceClass.getConstructor(String.class);
108            
109            authenticationService = constructor.newInstance(serverUrl);
110          }
111          catch (Exception e) {
112            log.error("Cannot create authentication service", e);
113            throw new ServletException("Failed create kshield authentication service instance", e);
114          }
115        }
116        else {
117          authenticationService = new DefaultAuthenticationServiceImpl(serverUrl);
118        }
119        
120        final BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(authenticationService);
121        
122        final String apiKey = config.getInitParameter("apiKey");
123        if (apiKey != null) {
124          wrapper.setPropertyValue("apiKey", apiKey);
125        }
126        
127        final String usernameAttr = config.getInitParameter("usernameAttr");
128        if (usernameAttr != null) {
129          wrapper.setPropertyValue("usernameAttribute", usernameAttr);
130        }
131        
132        authenticationService.init();
133      }
134      else {
135        throw new ServletException("Missing serverUrl init parameter");
136      }
137    }
138  }
139
140  @Override
141  public void destroy() {
142    //do nothing
143  }
144
145  private ServletAuthenticationService authenticationService;
146  
147  public void setAuthenticationService(ServletAuthenticationService authService) {
148    this.authenticationService = authService;
149  }
150
151  private static final Log log = LogFactory.getLog(KShieldAuthenticationFilter.class);
152}