package org.springframework.security.config.http;

import static org.springframework.security.config.http.SecurityFilters.*;
import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.*;
import static org.springframework.security.config.Elements.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.config.Elements;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.channel.InsecureChannelProcessor;
import org.springframework.security.web.access.channel.RetryWithHttpEntryPoint;
import org.springframework.security.web.access.channel.RetryWithHttpsEntryPoint;
import org.springframework.security.web.access.channel.SecureChannelProcessor;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.access.intercept.RequestKey;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

/**
 * Stateful class which helps HttpSecurityBDP to create the configuration for the &lt;http&gt; element.
 *
 * @author Luke Taylor
 * @since 3.0
 */
class HttpConfigurationBuilder {
    private static final String ATT_CREATE_SESSION = "create-session";
    private static final String OPT_CREATE_SESSION_NEVER = "never";
    private static final String DEF_CREATE_SESSION_IF_REQUIRED = "ifRequired";
    private static final String OPT_CREATE_SESSION_ALWAYS = "always";

    private static final String ATT_SESSION_FIXATION_PROTECTION = "session-fixation-protection";
    private static final String OPT_SESSION_FIXATION_NO_PROTECTION = "none";
    private static final String OPT_SESSION_FIXATION_MIGRATE_SESSION = "migrateSession";

    private static final String ATT_INVALID_SESSION_URL = "invalid-session-url";
    private static final String ATT_SESSION_AUTH_STRATEGY_REF = "session-authentication-strategy-ref";
    private static final String ATT_SESSION_AUTH_ERROR_URL = "session-authentication-error-url";
    private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";

    private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";

    private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
    private static final String ATT_ONCE_PER_REQUEST = "once-per-request";

    private final Element httpElt;
    private final ParserContext pc;
    private final UrlMatcher matcher;
    private final Boolean convertPathsToLowerCase;
    private final boolean allowSessionCreation;
    private final List<Element> interceptUrls;

    // Use ManagedMap to allow placeholder resolution
    private ManagedMap<Object, List<BeanMetadataElement>> filterChainMap;

    private BeanDefinition cpf;
    private BeanDefinition securityContextPersistenceFilter;
    private BeanReference contextRepoRef;
    private BeanReference sessionRegistryRef;
    private BeanDefinition concurrentSessionFilter;
    private BeanReference sessionStrategyRef;
    private RootBeanDefinition sfpf;
    private BeanDefinition servApiFilter;
    private String portMapperName;
    private BeanReference fsi;

    public HttpConfigurationBuilder(Element element, ParserContext pc, UrlMatcher matcher, String portMapperName) {
        this.httpElt = element;
        this.pc = pc;
        this.portMapperName = portMapperName;
        this.matcher = matcher;
        // SEC-501 - should paths stored in request maps be converted to lower case
        // true if Ant path and using lower case
        convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
        interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
        allowSessionCreation = !OPT_CREATE_SESSION_NEVER.equals(element.getAttribute(ATT_CREATE_SESSION));
    }

    void parseInterceptUrlsForEmptyFilterChains() {
        filterChainMap = new ManagedMap<Object, List<BeanMetadataElement>>();

        for (Element urlElt : interceptUrls) {
            String path = urlElt.getAttribute(ATT_PATH_PATTERN);

            if(!StringUtils.hasText(path)) {
                pc.getReaderContext().error("path attribute cannot be empty or null", urlElt);
            }

            BeanDefinitionBuilder pathBean = BeanDefinitionBuilder.rootBeanDefinition(HttpConfigurationBuilder.class);
            pathBean.setFactoryMethod("createPath");
            pathBean.addConstructorArgValue(path);
            pathBean.addConstructorArgValue(convertPathsToLowerCase);

            String filters = urlElt.getAttribute(ATT_FILTERS);

            if (StringUtils.hasText(filters)) {
                if (!filters.equals(OPT_FILTERS_NONE)) {
                    pc.getReaderContext().error("Currently only 'none' is supported as the custom " +
                            "filters attribute", urlElt);
                }

                List<BeanMetadataElement> noFilters = Collections.emptyList();
                filterChainMap.put(pathBean.getBeanDefinition(), noFilters);
            }
        }
    }

    // Needed to account for placeholders
    static String createPath(String path, boolean lowerCase) {
        return lowerCase ? path.toLowerCase() : path;
    }

    void createSecurityContextPersistenceFilter() {
        BeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);

        String repoRef = httpElt.getAttribute(ATT_SECURITY_CONTEXT_REPOSITORY);
        String createSession = httpElt.getAttribute(ATT_CREATE_SESSION);
        String disableUrlRewriting = httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);

        if (StringUtils.hasText(repoRef)) {
            if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
                scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
            } else if (StringUtils.hasText(createSession)) {
                pc.getReaderContext().error("If using security-context-repository-ref, the only value you can set for " +
                        "'create-session' is 'always'. Other session creation logic should be handled by the " +
                        "SecurityContextRepository", httpElt);
            }
        } else {
            BeanDefinitionBuilder contextRepo = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionSecurityContextRepository.class);
            if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
                contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
                scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
            } else if (OPT_CREATE_SESSION_NEVER.equals(createSession)) {
                contextRepo.addPropertyValue("allowSessionCreation", Boolean.FALSE);
                scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
            } else {
                createSession = DEF_CREATE_SESSION_IF_REQUIRED;
                contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
                scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
            }

            if ("true".equals(disableUrlRewriting)) {
                contextRepo.addPropertyValue("disableUrlRewriting", Boolean.TRUE);
            }

            BeanDefinition repoBean = contextRepo.getBeanDefinition();
            repoRef = pc.getReaderContext().generateBeanName(repoBean);
            pc.registerBeanComponent(new BeanComponentDefinition(repoBean, repoRef));

        }

        contextRepoRef = new RuntimeBeanReference(repoRef);
        scpf.addPropertyValue("securityContextRepository", contextRepoRef);

        securityContextPersistenceFilter = scpf.getBeanDefinition();
    }

    void createSessionManagementFilters() {
        Element sessionMgmtElt = DomUtils.getChildElementByTagName(httpElt, Elements.SESSION_MANAGEMENT);
        Element sessionCtrlElt = null;

        String sessionFixationAttribute = null;
        String invalidSessionUrl = null;
        String sessionAuthStratRef = null;
        String errorUrl = null;

        if (sessionMgmtElt != null) {
            sessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
            invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);
            sessionAuthStratRef = sessionMgmtElt.getAttribute(ATT_SESSION_AUTH_STRATEGY_REF);
            errorUrl = sessionMgmtElt.getAttribute(ATT_SESSION_AUTH_ERROR_URL);
            sessionCtrlElt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS);

            if (sessionCtrlElt != null) {
                if (StringUtils.hasText(sessionAuthStratRef)) {
                    pc.getReaderContext().error(ATT_SESSION_AUTH_STRATEGY_REF + " attribute cannot be used" +
                            " in combination with <" + Elements.CONCURRENT_SESSIONS + ">", pc.extractSource(sessionCtrlElt));
                }
                createConcurrencyControlFilterAndSessionRegistry(sessionCtrlElt);
            }
        }

        if (!StringUtils.hasText(sessionFixationAttribute)) {
            sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
        } else if (StringUtils.hasText(sessionAuthStratRef)) {
            pc.getReaderContext().error(ATT_SESSION_FIXATION_PROTECTION + " attribute cannot be used" +
                    " in combination with " + ATT_SESSION_AUTH_STRATEGY_REF, pc.extractSource(sessionCtrlElt));
        }

        boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION);

        BeanDefinitionBuilder sessionStrategy;

        if (sessionCtrlElt != null) {
            assert sessionRegistryRef != null;
            sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControlStrategy.class);
            sessionStrategy.addConstructorArgValue(sessionRegistryRef);

            String maxSessions = sessionCtrlElt.getAttribute("max-sessions");

            if (StringUtils.hasText(maxSessions)) {
                sessionStrategy.addPropertyValue("maximumSessions", maxSessions);
            }

            String exceptionIfMaximumExceeded = sessionCtrlElt.getAttribute("error-if-maximum-exceeded");

            if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
                sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
            }
        } else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)
                || StringUtils.hasText(sessionAuthStratRef)) {
            sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(SessionFixationProtectionStrategy.class);
        } else {
            sfpf = null;
            return;
        }

        BeanDefinitionBuilder sessionMgmtFilter = BeanDefinitionBuilder.rootBeanDefinition(SessionManagementFilter.class);
        RootBeanDefinition failureHandler = new RootBeanDefinition(SimpleUrlAuthenticationFailureHandler.class);
        if (StringUtils.hasText(errorUrl)) {
            failureHandler.getPropertyValues().addPropertyValue("defaultFailureUrl", errorUrl);
        }
        sessionMgmtFilter.addPropertyValue("authenticationFailureHandler", failureHandler);
        sessionMgmtFilter.addConstructorArgValue(contextRepoRef);

        if (!StringUtils.hasText(sessionAuthStratRef)) {
            BeanDefinition strategyBean = sessionStrategy.getBeanDefinition();

            if (sessionFixationProtectionRequired) {
                sessionStrategy.addPropertyValue("migrateSessionAttributes",
                        Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
            }
            sessionAuthStratRef = pc.getReaderContext().generateBeanName(strategyBean);
            pc.registerBeanComponent(new BeanComponentDefinition(strategyBean, sessionAuthStratRef));
        }

        if (StringUtils.hasText(invalidSessionUrl)) {
            sessionMgmtFilter.addPropertyValue("invalidSessionUrl", invalidSessionUrl);
        }

        sessionMgmtFilter.addPropertyReference("sessionAuthenticationStrategy", sessionAuthStratRef);

        sfpf = (RootBeanDefinition) sessionMgmtFilter.getBeanDefinition();
        sessionStrategyRef = new RuntimeBeanReference(sessionAuthStratRef);
    }

    private void createConcurrencyControlFilterAndSessionRegistry(Element element) {
        final String ATT_EXPIRY_URL = "expired-url";
        final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
        final String ATT_SESSION_REGISTRY_REF = "session-registry-ref";

        CompositeComponentDefinition compositeDef =
            new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
        pc.pushContainingComponent(compositeDef);

        BeanDefinitionRegistry beanRegistry = pc.getRegistry();

        String sessionRegistryId = element.getAttribute(ATT_SESSION_REGISTRY_REF);

        if (!StringUtils.hasText(sessionRegistryId)) {
            // Register an internal SessionRegistryImpl if no external reference supplied.
            RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
            sessionRegistryId = pc.getReaderContext().registerWithGeneratedName(sessionRegistry);
            pc.registerComponent(new BeanComponentDefinition(sessionRegistry, sessionRegistryId));
        }

        String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
        if (StringUtils.hasText(registryAlias)) {
            beanRegistry.registerAlias(sessionRegistryId, registryAlias);
        }

        BeanDefinitionBuilder filterBuilder =
                BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionFilter.class);
        filterBuilder.addPropertyReference("sessionRegistry", sessionRegistryId);

        Object source = pc.extractSource(element);
        filterBuilder.getRawBeanDefinition().setSource(source);
        filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

        String expiryUrl = element.getAttribute(ATT_EXPIRY_URL);

        if (StringUtils.hasText(expiryUrl)) {
            WebConfigUtils.validateHttpRedirect(expiryUrl, pc, source);
            filterBuilder.addPropertyValue("expiredUrl", expiryUrl);
        }

        pc.popAndRegisterContainingComponent();

        concurrentSessionFilter = filterBuilder.getBeanDefinition();
        sessionRegistryRef = new RuntimeBeanReference(sessionRegistryId);
    }

    // Adds the servlet-api integration filter if required
    void createServletApiFilter() {
        final String ATT_SERVLET_API_PROVISION = "servlet-api-provision";
        final String DEF_SERVLET_API_PROVISION = "true";

        String provideServletApi = httpElt.getAttribute(ATT_SERVLET_API_PROVISION);
        if (!StringUtils.hasText(provideServletApi)) {
            provideServletApi = DEF_SERVLET_API_PROVISION;
        }

        if ("true".equals(provideServletApi)) {
            servApiFilter = new RootBeanDefinition(SecurityContextHolderAwareRequestFilter.class);
        }
    }

    void createChannelProcessingFilter() {
        ManagedMap<BeanDefinition,BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();

        if (channelRequestMap.isEmpty()) {
            return;
        }

        RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);
        BeanDefinitionBuilder metadataSourceBldr = BeanDefinitionBuilder.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);
        metadataSourceBldr.addConstructorArgValue(matcher);
        metadataSourceBldr.addConstructorArgValue(channelRequestMap);
        metadataSourceBldr.addPropertyValue("stripQueryStringFromUrls", matcher instanceof AntUrlPathMatcher);

        channelFilter.getPropertyValues().addPropertyValue("securityMetadataSource", metadataSourceBldr.getBeanDefinition());
        RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
        ManagedList<RootBeanDefinition> channelProcessors = new ManagedList<RootBeanDefinition>(3);
        RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
        RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
        RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
        RuntimeBeanReference portMapper = new RuntimeBeanReference(portMapperName);
        retryWithHttp.getPropertyValues().addPropertyValue("portMapper", portMapper);
        retryWithHttps.getPropertyValues().addPropertyValue("portMapper", portMapper);
        secureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttps);
        RootBeanDefinition inSecureChannelProcessor = new RootBeanDefinition(InsecureChannelProcessor.class);
        inSecureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttp);
        channelProcessors.add(secureChannelProcessor);
        channelProcessors.add(inSecureChannelProcessor);
        channelDecisionManager.getPropertyValues().addPropertyValue("channelProcessors", channelProcessors);

        String id = pc.getReaderContext().registerWithGeneratedName(channelDecisionManager);
        channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager", new RuntimeBeanReference(id));
        cpf = channelFilter;
    }

    /**
     * Parses the intercept-url elements to obtain the map used by channel security.
     * This will be empty unless the <tt>requires-channel</tt> attribute has been used on a URL path.
     */
    private ManagedMap<BeanDefinition,BeanDefinition> parseInterceptUrlsForChannelSecurity() {

        ManagedMap<BeanDefinition, BeanDefinition> channelRequestMap = new ManagedMap<BeanDefinition, BeanDefinition>();

        for (Element urlElt : interceptUrls) {
            String path = urlElt.getAttribute(ATT_PATH_PATTERN);

            if(!StringUtils.hasText(path)) {
                pc.getReaderContext().error("path attribute cannot be empty or null", urlElt);
            }

            if (convertPathsToLowerCase) {
                path = path.toLowerCase();
            }

            String requiredChannel = urlElt.getAttribute(ATT_REQUIRES_CHANNEL);

            if (StringUtils.hasText(requiredChannel)) {
                String filters = urlElt.getAttribute(ATT_FILTERS);
                if(OPT_FILTERS_NONE.equals(filters)) {
                    pc.getReaderContext().error(
                            "Ambiguous configuration. Cannot contain " + INTERCEPT_URL+"@" + ATT_FILTERS +
                            "=\"" + OPT_FILTERS_NONE + "\" and " + INTERCEPT_URL + "@" + ATT_REQUIRES_CHANNEL,
                            pc.extractSource(urlElt));
                }
                BeanDefinition requestKey = new RootBeanDefinition(RequestKey.class);
                requestKey.getConstructorArgumentValues().addGenericArgumentValue(path);

                String method = urlElt.getAttribute(ATT_HTTP_METHOD);
                if(StringUtils.hasText(method)) {
                    requestKey.getConstructorArgumentValues().addGenericArgumentValue(method);
                }

                RootBeanDefinition channelAttributes = new RootBeanDefinition(ChannelAttributeFactory.class);
                channelAttributes.getConstructorArgumentValues().addGenericArgumentValue(requiredChannel);
                channelAttributes.setFactoryMethodName("createChannelAttributes");

                channelRequestMap.put(requestKey, channelAttributes);
            }
        }

        return channelRequestMap;
    }

    void createFilterSecurityInterceptor(BeanReference authManager) {
        boolean useExpressions = FilterInvocationSecurityMetadataSourceParser.isUseExpressions(httpElt);
        BeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser.createSecurityMetadataSource(interceptUrls, httpElt, pc);

        RootBeanDefinition accessDecisionMgr;
        ManagedList<BeanDefinition> voters =  new ManagedList<BeanDefinition>(2);

        if (useExpressions) {
            voters.add(new RootBeanDefinition(WebExpressionVoter.class));
        } else {
            voters.add(new RootBeanDefinition(RoleVoter.class));
            voters.add(new RootBeanDefinition(AuthenticatedVoter.class));
        }
        accessDecisionMgr = new RootBeanDefinition(AffirmativeBased.class);
        accessDecisionMgr.getPropertyValues().addPropertyValue("decisionVoters", voters);
        accessDecisionMgr.setSource(pc.extractSource(httpElt));

        // Set up the access manager reference for http
        String accessManagerId = httpElt.getAttribute(ATT_ACCESS_MGR);

        if (!StringUtils.hasText(accessManagerId)) {
            accessManagerId = pc.getReaderContext().generateBeanName(accessDecisionMgr);
            pc.registerBeanComponent(new BeanComponentDefinition(accessDecisionMgr, accessManagerId));
        }

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);

        builder.addPropertyReference("accessDecisionManager", accessManagerId);
        builder.addPropertyValue("authenticationManager", authManager);

        if ("false".equals(httpElt.getAttribute(ATT_ONCE_PER_REQUEST))) {
            builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
        }

        builder.addPropertyValue("securityMetadataSource", securityMds);
        BeanDefinition fsiBean = builder.getBeanDefinition();
        String fsiId = pc.getReaderContext().generateBeanName(fsiBean);
        pc.registerBeanComponent(new BeanComponentDefinition(fsiBean,fsiId));

        // Create and register a DefaultWebInvocationPrivilegeEvaluator for use with taglibs etc.
        BeanDefinition wipe = new RootBeanDefinition(DefaultWebInvocationPrivilegeEvaluator.class);
        wipe.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(fsiId));

        pc.registerBeanComponent(new BeanComponentDefinition(wipe, pc.getReaderContext().generateBeanName(wipe)));

        this.fsi = new RuntimeBeanReference(fsiId);
    }

    BeanReference getSessionStrategy() {
        return sessionStrategyRef;
    }


    boolean isAllowSessionCreation() {
        return allowSessionCreation;
    }

    public ManagedMap<Object, List<BeanMetadataElement>> getFilterChainMap() {
        return filterChainMap;
    }

    List<OrderDecorator> getFilters() {
        List<OrderDecorator> filters = new ArrayList<OrderDecorator>();

        if (cpf != null) {
            filters.add(new OrderDecorator(cpf, CHANNEL_FILTER));
        }

        if (concurrentSessionFilter != null) {
            filters.add(new OrderDecorator(concurrentSessionFilter, CONCURRENT_SESSION_FILTER));
        }

        filters.add(new OrderDecorator(securityContextPersistenceFilter, SECURITY_CONTEXT_FILTER));

        if (servApiFilter != null) {
            filters.add(new OrderDecorator(servApiFilter, SERVLET_API_SUPPORT_FILTER));
        }

        if (sfpf != null) {
            filters.add(new OrderDecorator(sfpf, SESSION_MANAGEMENT_FILTER));
        }

        filters.add(new OrderDecorator(fsi, FILTER_SECURITY_INTERCEPTOR));

        return filters;
    }
}
