package org.springblade.common.auth.permission;

import org.springblade.common.auth.support.AuthUtil;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import javax.net.ssl.HostnameVerifier;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
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.lang3.StringUtils;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.proxy.AbstractEncryptedProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
import org.jasig.cas.client.proxy.CleanUpTimerTask;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.ReflectUtils;
import org.jasig.cas.client.util.URIBuilder;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.jasig.cas.client.validation.Cas30ProxyTicketValidator;
import org.jasig.cas.client.validation.Cas30ServiceTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;

public class CasTicketValidationFilter extends AbstractCasFilter {
    private TicketValidator ticketValidator;
    private boolean redirectAfterValidation = true;
    private boolean exceptionOnValidationFailure = false;
    private boolean useSession = true;
    private static final String[] RESERVED_INIT_PARAMS;
    private String proxyReceptorUrl;
    private Timer timer;
    private TimerTask timerTask;
    private int millisBetweenCleanUps;
    protected Class<? extends Cas20ServiceTicketValidator> defaultServiceTicketValidatorClass;
    protected Class<? extends Cas20ProxyTicketValidator> defaultProxyTicketValidatorClass;
    private ProxyGrantingTicketStorage proxyGrantingTicketStorage;

    public CasTicketValidationFilter() {
        this(Protocol.CAS2);
        this.defaultServiceTicketValidatorClass = Cas30ServiceTicketValidator.class;
        this.defaultProxyTicketValidatorClass = Cas30ProxyTicketValidator.class;
    }

    protected CasTicketValidationFilter(Protocol protocol) {
        super(protocol);
        this.proxyGrantingTicketStorage = new ProxyGrantingTicketStorageImpl();
    }

    protected TicketValidator getTicketValidator(FilterConfig filterConfig) {
        boolean allowAnyProxy = this.getBoolean(ConfigurationKeys.ACCEPT_ANY_PROXY);
        String allowedProxyChains = this.getString(ConfigurationKeys.ALLOWED_PROXY_CHAINS);
        String casServerUrlPrefix = this.getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX);
        Class<? extends Cas20ServiceTicketValidator> ticketValidatorClass = this
                .getClass(ConfigurationKeys.TICKET_VALIDATOR_CLASS);
        Object validator;
        if (!allowAnyProxy && !CommonUtils.isNotBlank(allowedProxyChains)) {
            validator = (Cas20ServiceTicketValidator) this
                    .createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix,
                            this.defaultServiceTicketValidatorClass);
        } else {
            Cas20ProxyTicketValidator v = (Cas20ProxyTicketValidator) this
                    .createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix,
                            this.defaultProxyTicketValidatorClass);
            v.setAcceptAnyProxy(allowAnyProxy);
            v.setAllowedProxyChains(CommonUtils.createProxyList(allowedProxyChains));
            validator = v;
        }

        ((Cas20ServiceTicketValidator) validator)
                .setProxyCallbackUrl(this.getString(ConfigurationKeys.PROXY_CALLBACK_URL));
        ((Cas20ServiceTicketValidator) validator).setProxyGrantingTicketStorage(this.proxyGrantingTicketStorage);
        HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(this.getHostnameVerifier(),
                this.getSSLConfig());
        ((Cas20ServiceTicketValidator) validator).setURLConnectionFactory(factory);
        ((Cas20ServiceTicketValidator) validator).setProxyRetriever(
                new Cas20ProxyRetriever(casServerUrlPrefix, this.getString(ConfigurationKeys.ENCODING), factory));
        ((Cas20ServiceTicketValidator) validator).setRenew(this.getBoolean(ConfigurationKeys.RENEW));
        ((Cas20ServiceTicketValidator) validator).setEncoding(this.getString(ConfigurationKeys.ENCODING));
        Map<String, String> additionalParameters = new HashMap();
        List<String> params = Arrays.asList(RESERVED_INIT_PARAMS);
        Enumeration e = filterConfig.getInitParameterNames();

        while (e.hasMoreElements()) {
            String s = (String) e.nextElement();
            if (!params.contains(s)) {
                additionalParameters.put(s, filterConfig.getInitParameter(s));
            }
        }

        ((Cas20ServiceTicketValidator) validator).setCustomParameters(additionalParameters);
        return (TicketValidator) validator;
    }

    private <T> T createNewTicketValidator(Class<? extends Cas20ServiceTicketValidator> ticketValidatorClass,
            String casServerUrlPrefix, Class<T> clazz) {
        return ReflectUtils.newInstance(clazz, new Object[] { casServerUrlPrefix });
    }

    protected Properties getSSLConfig() {
        Properties properties = new Properties();
        String fileName = this.getString(ConfigurationKeys.SSL_CONFIG_FILE);
        if (fileName != null) {
            FileInputStream fis = null;

            try {
                fis = new FileInputStream(fileName);
                properties.load(fis);
                this.logger.trace("Loaded {} entries from {}", properties.size(), fileName);
            } catch (IOException var8) {
                this.logger.error(var8.getMessage(), var8);
            } finally {
                CommonUtils.closeQuietly(fis);
            }
        }

        return properties;
    }

    protected HostnameVerifier getHostnameVerifier() {
        Class<? extends HostnameVerifier> className = this.getClass(ConfigurationKeys.HOSTNAME_VERIFIER);
        String config = this.getString(ConfigurationKeys.HOSTNAME_VERIFIER_CONFIG);
        if (className != null) {
            return config != null ?
                    (HostnameVerifier) ReflectUtils.newInstance(className, new Object[] { config }) :
                    (HostnameVerifier) ReflectUtils.newInstance(className, new Object[0]);
        } else {
            return null;
        }
    }

    @Override
    protected void initInternal(FilterConfig filterConfig) throws ServletException {
        this.setProxyReceptorUrl(this.getString(ConfigurationKeys.PROXY_RECEPTOR_URL));
        Class<? extends ProxyGrantingTicketStorage> proxyGrantingTicketStorageClass = this
                .getClass(ConfigurationKeys.PROXY_GRANTING_TICKET_STORAGE_CLASS);
        if (proxyGrantingTicketStorageClass != null) {
            this.proxyGrantingTicketStorage = (ProxyGrantingTicketStorage) ReflectUtils
                    .newInstance(proxyGrantingTicketStorageClass, new Object[0]);
            if (this.proxyGrantingTicketStorage instanceof AbstractEncryptedProxyGrantingTicketStorageImpl) {
                AbstractEncryptedProxyGrantingTicketStorageImpl p = (AbstractEncryptedProxyGrantingTicketStorageImpl) this.proxyGrantingTicketStorage;
                String cipherAlgorithm = this.getString(ConfigurationKeys.CIPHER_ALGORITHM);
                String secretKey = this.getString(ConfigurationKeys.SECRET_KEY);
                p.setCipherAlgorithm(cipherAlgorithm);

                try {
                    if (secretKey != null) {
                        p.setSecretKey(secretKey);
                    }
                } catch (Exception var7) {
                    throw new RuntimeException(var7);
                }
            }
        }

        this.millisBetweenCleanUps = this.getInt(ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS);

        this.setExceptionOnValidationFailure(this.getBoolean(ConfigurationKeys.EXCEPTION_ON_VALIDATION_FAILURE));
        this.setRedirectAfterValidation(this.getBoolean(ConfigurationKeys.REDIRECT_AFTER_VALIDATION));
        this.setUseSession(this.getBoolean(ConfigurationKeys.USE_SESSION));
        if (!this.useSession && this.redirectAfterValidation) {
            this.logger
                    .warn("redirectAfterValidation parameter may not be true when useSession parameter is false. Resetting it to false in order to prevent infinite redirects.");
            this.setRedirectAfterValidation(false);
        }

        this.setTicketValidator(this.getTicketValidator(filterConfig));
        super.initInternal(filterConfig);
    }

    @Override
    public void init() {
        super.init();
        CommonUtils.assertNotNull(this.ticketValidator, "ticketValidator cannot be null.");

        CommonUtils.assertNotNull(this.proxyGrantingTicketStorage, "proxyGrantingTicketStorage cannot be null.");
        if (this.timer == null) {
            this.timer = new Timer(true);
        }

        if (this.timerTask == null) {
            this.timerTask = new CleanUpTimerTask(this.proxyGrantingTicketStorage);
        }

        this.timer.schedule(this.timerTask, (long) this.millisBetweenCleanUps, (long) this.millisBetweenCleanUps);
    }

    protected boolean preFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String requestUri = request.getRequestURI();
        if (!CommonUtils.isEmpty(this.proxyReceptorUrl) && requestUri.endsWith(this.proxyReceptorUrl)) {
            try {
                CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
                return false;
            } catch (RuntimeException var8) {
                this.logger.error(var8.getMessage(), var8);
                throw var8;
            }
        } else {
            return true;
        }
    }

    protected void onSuccessfulValidation(HttpServletRequest request, HttpServletResponse response,
            Assertion assertion) {
    }

    protected void onFailedValidation(HttpServletRequest request, HttpServletResponse response) {
    }

    @Override
    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        if (this.preFilter(servletRequest, servletResponse, filterChain)) {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            String ticket = this.retrieveTicketFromRequest(request);
            if (CommonUtils.isNotBlank(ticket)) {
                this.logger.debug("Attempting to validate ticket: {}", ticket);

                try {
                    String service = AuthUtil.constructServiceUrl(request, response, getString(ConfigurationKeys.SERVICE),
                            getString(ConfigurationKeys.SERVER_NAME), getProtocol().getServiceParameterName(),
                            getProtocol().getArtifactParameterName(), getBoolean(ConfigurationKeys.ENCODE_SERVICE_URL));
                    Assertion assertion = this.ticketValidator.validate(ticket, service);
                    this.logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName());
                    request.setAttribute("_const_cas_assertion_", assertion);
                    if (this.useSession) {
                        request.getSession().setAttribute("_const_cas_assertion_", assertion);
                    }

                    this.onSuccessfulValidation(request, response, assertion);
                    if (this.redirectAfterValidation) {
                        this.logger.debug("Redirecting after successful ticket validation.");
                        response.sendRedirect(service);
                        return;
                    }
                } catch (TicketValidationException var8) {
                    this.logger.debug(var8.getMessage(), var8);
                    this.onFailedValidation(request, response);
                    if (this.exceptionOnValidationFailure) {
                        throw new ServletException(var8);
                    }

                    response.sendError(403, var8.getMessage());
                    return;
                }
            }

            filterChain.doFilter(request, response);
        }
    }

    public final void setTicketValidator(TicketValidator ticketValidator) {
        this.ticketValidator = ticketValidator;
    }

    public final void setRedirectAfterValidation(boolean redirectAfterValidation) {
        this.redirectAfterValidation = redirectAfterValidation;
    }

    public final void setExceptionOnValidationFailure(boolean exceptionOnValidationFailure) {
        this.exceptionOnValidationFailure = exceptionOnValidationFailure;
    }

    public final void setUseSession(boolean useSession) {
        this.useSession = useSession;
    }

    @Override
    public void destroy() {
        super.destroy();
        this.timer.cancel();
    }

    public final void setProxyReceptorUrl(String proxyReceptorUrl) {
        this.proxyReceptorUrl = proxyReceptorUrl;
    }

    public void setProxyGrantingTicketStorage(ProxyGrantingTicketStorage storage) {
        this.proxyGrantingTicketStorage = storage;
    }

    public void setTimer(Timer timer) {
        this.timer = timer;
    }

    public void setTimerTask(TimerTask timerTask) {
        this.timerTask = timerTask;
    }

    public void setMillisBetweenCleanUps(int millisBetweenCleanUps) {
        this.millisBetweenCleanUps = millisBetweenCleanUps;
    }

    static {
        RESERVED_INIT_PARAMS = new String[] { ConfigurationKeys.ARTIFACT_PARAMETER_NAME.getName(),
                ConfigurationKeys.SERVER_NAME.getName(), ConfigurationKeys.SERVICE.getName(),
                ConfigurationKeys.RENEW.getName(), ConfigurationKeys.LOGOUT_PARAMETER_NAME.getName(),
                ConfigurationKeys.ARTIFACT_PARAMETER_OVER_POST.getName(),
                ConfigurationKeys.EAGERLY_CREATE_SESSIONS.getName(), ConfigurationKeys.ENCODE_SERVICE_URL.getName(),
                ConfigurationKeys.SSL_CONFIG_FILE.getName(), ConfigurationKeys.ROLE_ATTRIBUTE.getName(),
                ConfigurationKeys.IGNORE_CASE.getName(), ConfigurationKeys.CAS_SERVER_LOGIN_URL.getName(),
                ConfigurationKeys.GATEWAY.getName(), ConfigurationKeys.AUTHENTICATION_REDIRECT_STRATEGY_CLASS.getName(),
                ConfigurationKeys.GATEWAY_STORAGE_CLASS.getName(), ConfigurationKeys.CAS_SERVER_URL_PREFIX.getName(),
                ConfigurationKeys.ENCODING.getName(), ConfigurationKeys.TOLERANCE.getName(),
                ConfigurationKeys.IGNORE_PATTERN.getName(), ConfigurationKeys.IGNORE_URL_PATTERN_TYPE.getName(),
                ConfigurationKeys.HOSTNAME_VERIFIER.getName(), ConfigurationKeys.HOSTNAME_VERIFIER_CONFIG.getName(),
                ConfigurationKeys.EXCEPTION_ON_VALIDATION_FAILURE.getName(),
                ConfigurationKeys.REDIRECT_AFTER_VALIDATION.getName(), ConfigurationKeys.USE_SESSION.getName(),
                ConfigurationKeys.SECRET_KEY.getName(), ConfigurationKeys.CIPHER_ALGORITHM.getName(),
                ConfigurationKeys.PROXY_RECEPTOR_URL.getName(),
                ConfigurationKeys.PROXY_GRANTING_TICKET_STORAGE_CLASS.getName(),
                ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS.getName(), ConfigurationKeys.ACCEPT_ANY_PROXY.getName(),
                ConfigurationKeys.ALLOWED_PROXY_CHAINS.getName(), ConfigurationKeys.TICKET_VALIDATOR_CLASS.getName(),
                ConfigurationKeys.PROXY_CALLBACK_URL.getName(),
                ConfigurationKeys.RELAY_STATE_PARAMETER_NAME.getName() };
    }
}
