| /* |
| * 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.knox.gateway; |
| |
| import org.apache.knox.gateway.audit.api.Action; |
| import org.apache.knox.gateway.audit.api.ActionOutcome; |
| import org.apache.knox.gateway.audit.api.AuditService; |
| import org.apache.knox.gateway.audit.api.AuditServiceFactory; |
| import org.apache.knox.gateway.audit.api.Auditor; |
| import org.apache.knox.gateway.audit.api.ResourceType; |
| import org.apache.knox.gateway.audit.log4j.audit.AuditConstants; |
| import org.apache.knox.gateway.config.GatewayConfig; |
| import org.apache.knox.gateway.descriptor.GatewayDescriptor; |
| import org.apache.knox.gateway.descriptor.GatewayDescriptorFactory; |
| import org.apache.knox.gateway.filter.AbstractGatewayFilter; |
| import org.apache.knox.gateway.i18n.messages.MessagesFactory; |
| import org.apache.knox.gateway.i18n.resources.ResourcesFactory; |
| import org.apache.knox.gateway.services.ServiceType; |
| import org.apache.knox.gateway.services.GatewayServices; |
| import org.apache.knox.gateway.services.metrics.MetricsService; |
| import org.apache.knox.gateway.util.ServletRequestUtils; |
| |
| import javax.servlet.Filter; |
| import javax.servlet.FilterChain; |
| import javax.servlet.FilterConfig; |
| import javax.servlet.Servlet; |
| import javax.servlet.ServletConfig; |
| import javax.servlet.ServletContext; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletRequest; |
| import javax.servlet.ServletResponse; |
| import javax.servlet.http.HttpServletResponse; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.net.URISyntaxException; |
| import java.nio.charset.StandardCharsets; |
| import java.util.Enumeration; |
| |
| public class GatewayServlet implements Servlet, Filter { |
| public static final String GATEWAY_DESCRIPTOR_LOCATION_DEFAULT = "gateway.xml"; |
| public static final String GATEWAY_DESCRIPTOR_LOCATION_PARAM = "gatewayDescriptorLocation"; |
| |
| private static final GatewayResources res = ResourcesFactory.get( GatewayResources.class ); |
| private static final GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class ); |
| |
| private static final AuditService auditService = AuditServiceFactory.getAuditService(); |
| private static final Auditor auditor = AuditServiceFactory.getAuditService().getAuditor(AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME, |
| AuditConstants.KNOX_COMPONENT_NAME); |
| |
| private FilterConfigAdapter filterConfig; |
| private GatewayFilter filter; |
| |
| public GatewayServlet( GatewayFilter filter ) { |
| this.filterConfig = null; |
| this.filter = filter; |
| } |
| |
| public GatewayServlet() { |
| this( null ); |
| } |
| |
| public synchronized GatewayFilter getFilter() { |
| return filter; |
| } |
| |
| public synchronized void setFilter( GatewayFilter filter ) throws ServletException { |
| if( filterConfig != null ) { |
| filter.init( filterConfig ); |
| } |
| this.filter = filter; |
| if( filter != null && filterConfig != null ) { |
| filter.destroy(); |
| } |
| } |
| |
| @Override |
| public synchronized void init( ServletConfig servletConfig ) throws ServletException { |
| try { |
| if( filter == null ) { |
| filter = createFilter( servletConfig ); |
| } |
| filterConfig = new FilterConfigAdapter( servletConfig ); |
| if( filter != null ) { |
| filter.init( filterConfig ); |
| } |
| } catch( ServletException | RuntimeException e ) { |
| LOG.failedToInitializeServletInstace( e ); |
| throw e; |
| } |
| } |
| |
| @Override |
| public void init( FilterConfig filterConfig ) throws ServletException { |
| try { |
| if( filter == null ) { |
| filter = createFilter( filterConfig ); |
| } |
| if( filter != null ) { |
| filter.init( filterConfig ); |
| } |
| } catch( ServletException | RuntimeException e ) { |
| LOG.failedToInitializeServletInstace( e ); |
| throw e; |
| } |
| } |
| |
| @Override |
| public ServletConfig getServletConfig() { |
| return filterConfig.getServletConfig(); |
| } |
| |
| @Override |
| public void service( ServletRequest servletRequest, ServletResponse servletResponse ) throws ServletException, IOException { |
| try { |
| auditService.createContext(); |
| GatewayFilter f = filter; |
| if( f != null ) { |
| try { |
| f.doFilter( servletRequest, servletResponse, null ); |
| } catch( IOException | RuntimeException | ServletException e ) { |
| LOG.failedToExecuteFilter( e ); |
| throw e; |
| } |
| } else { |
| ((HttpServletResponse)servletResponse).setStatus( HttpServletResponse.SC_SERVICE_UNAVAILABLE ); |
| } |
| auditLog(servletRequest, servletResponse); |
| } finally { |
| auditService.detachContext(); |
| } |
| } |
| |
| @Override |
| public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain ) throws IOException, ServletException { |
| try { |
| auditService.createContext(); |
| GatewayFilter f = filter; |
| if( f != null ) { |
| try { |
| f.doFilter( servletRequest, servletResponse ); |
| |
| /* if response is committed in case of SSO redirect no need to apply further filters */ |
| if(!servletResponse.isCommitted()) { |
| //TODO: This should really happen naturally somehow as part of being a filter. This way will cause problems eventually. |
| chain.doFilter( servletRequest, servletResponse ); |
| } |
| |
| } catch( IOException | RuntimeException | ServletException e ) { |
| LOG.failedToExecuteFilter( e ); |
| throw e; |
| } |
| } else { |
| ((HttpServletResponse)servletResponse).setStatus( HttpServletResponse.SC_SERVICE_UNAVAILABLE ); |
| } |
| auditLog(servletRequest, servletResponse); |
| } finally { |
| auditService.detachContext(); |
| } |
| } |
| |
| private void auditLog(ServletRequest servletRequest, ServletResponse servletResponse) { |
| final int status = ((HttpServletResponse) servletResponse).getStatus(); |
| final String requestUri, actionOutcome; |
| if (HttpServletResponse.SC_SERVICE_UNAVAILABLE == status) { |
| requestUri = ServletRequestUtils.getContextPathWithQuery(servletRequest); |
| actionOutcome = ActionOutcome.UNAVAILABLE; |
| } else { |
| requestUri = (String) servletRequest.getAttribute(AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME); |
| actionOutcome = ActionOutcome.SUCCESS; |
| } |
| auditor.audit(Action.ACCESS, requestUri, ResourceType.URI, actionOutcome, res.responseStatus(status)); |
| } |
| |
| @Override |
| public String getServletInfo() { |
| return res.gatewayServletInfo(); |
| } |
| |
| @Override |
| public synchronized void destroy() { |
| if( filter != null ) { |
| filter.destroy(); |
| } |
| filter = null; |
| } |
| |
| private static GatewayFilter createFilter( InputStream stream, ServletContext servletContext ) throws ServletException { |
| try { |
| GatewayFilter filter = null; |
| if( stream != null ) { |
| try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)){ |
| GatewayDescriptor descriptor = GatewayDescriptorFactory.load("xml", reader); |
| filter = GatewayFactory.create( descriptor ); |
| } finally { |
| stream.close(); |
| } |
| } |
| GatewayConfig gatewayConfig = (GatewayConfig) servletContext.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE); |
| if (gatewayConfig.isMetricsEnabled()) { |
| GatewayServices gatewayServices = (GatewayServices) servletContext.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE); |
| MetricsService metricsService = gatewayServices.getService(ServiceType.METRICS_SERVICE); |
| if (metricsService != null) { |
| GatewayFilter instrumentedFilter = metricsService.getInstrumented(filter); |
| if (instrumentedFilter != null) { |
| filter = instrumentedFilter; |
| } |
| } |
| } |
| return filter; |
| } catch( IOException | URISyntaxException e ) { |
| throw new ServletException( e ); |
| } |
| } |
| |
| private static GatewayFilter createFilter( FilterConfig filterConfig ) throws ServletException { |
| GatewayFilter filter; |
| InputStream stream; |
| String location = filterConfig.getInitParameter( GATEWAY_DESCRIPTOR_LOCATION_PARAM ); |
| if( location != null ) { |
| stream = filterConfig.getServletContext().getResourceAsStream( location ); |
| if( stream == null ) { |
| stream = filterConfig.getServletContext().getResourceAsStream( "/WEB-INF/" + location ); |
| } |
| } else { |
| stream = filterConfig.getServletContext().getResourceAsStream( GATEWAY_DESCRIPTOR_LOCATION_DEFAULT ); |
| } |
| filter = createFilter( stream, filterConfig.getServletContext()); |
| return filter; |
| } |
| |
| private static GatewayFilter createFilter( ServletConfig servletConfig ) throws ServletException { |
| GatewayFilter filter; |
| InputStream stream; |
| String location = servletConfig.getInitParameter( GATEWAY_DESCRIPTOR_LOCATION_PARAM ); |
| if( location != null ) { |
| stream = servletConfig.getServletContext().getResourceAsStream( location ); |
| if( stream == null ) { |
| stream = servletConfig.getServletContext().getResourceAsStream( "/WEB-INF/" + location ); |
| } |
| } else { |
| stream = servletConfig.getServletContext().getResourceAsStream( GATEWAY_DESCRIPTOR_LOCATION_DEFAULT ); |
| } |
| filter = createFilter( stream, servletConfig.getServletContext()); |
| return filter; |
| } |
| |
| private static class FilterConfigAdapter implements FilterConfig { |
| |
| private ServletConfig config; |
| |
| FilterConfigAdapter( ServletConfig config ) { |
| this.config = config; |
| } |
| |
| private ServletConfig getServletConfig() { |
| return config; |
| } |
| |
| @Override |
| public String getFilterName() { |
| return config.getServletName(); |
| } |
| |
| @Override |
| public ServletContext getServletContext() { |
| return config.getServletContext(); |
| } |
| |
| @Override |
| public String getInitParameter( String name ) { |
| return config.getInitParameter( name ); |
| } |
| |
| @Override |
| public Enumeration<String> getInitParameterNames() { |
| return config.getInitParameterNames(); |
| } |
| } |
| } |