/****************************************************************************
 * Copyright Avaya Inc., All Rights Reserved.
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF Avaya Inc.
 * The copyright notice above does not evidence any actual or intended publication of such source code.
 * Some third-party source code components may have been modified from their original versions by Avaya Inc.
 * The modifications are Copyright Avaya Inc., All Rights Reserved.
 * Avaya Confidential & Restricted. May not be distributed further without written permission of
 * the Avaya owner.
 ****************************************************************************/
package com.avaya.zephyr.services.sample_services.Authorization;

import java.io.IOException;

import javax.servlet.Filter;
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 com.avaya.collaboration.authorization.AuthorizationHelperException;
import com.avaya.collaboration.authorization.resource.AuthorizationResourceHelper;
import com.avaya.collaboration.util.logger.Logger;
import com.avaya.zephyr.services.sample_services.Authorization.util.ServiceAttributeStore;

public final class BearerTokenValidationFilter implements Filter
{
    private static final String BEARER_TOKEN = "bearerToken";
    private static final String SECURITY_POLICY_HEADER = "X-Av-HTTP-Security-Policy";
    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String BEARER = "Bearer";
    private static final String SECURITY_POLICY_PASSED = "passed";
    private final Logger logger;

    public BearerTokenValidationFilter()
    {
        this(Logger.getLogger(BearerTokenValidationFilter.class));
    }

    BearerTokenValidationFilter(final Logger logger)
    {
        this.logger = logger;
    }

    /*
     * 1) If the Authorization header contains Bearer token, it will validate the token. Successful token validation will allow the request
     * to reach the resource. If token validation fails, it returns 401 unauthorized. 2)Else, It will check for "X-Av-HTTP-Security-Policy"
     * header. If this header value is "passed", it will allow the request to go through. 3)Else, It will check for
     * "com.avaya.authorization.enableTokenlessAccess" attribute value. If this value is set to true, it will allow the request to go
     * through.
     */
    @Override
    public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
            final FilterChain filterChain) throws IOException, ServletException
    {
        final HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        final String authorizationHeader = httpServletRequest.getHeader(AUTHORIZATION_HEADER);
        final String securityHeader = httpServletRequest.getHeader(SECURITY_POLICY_HEADER);
        if (logger.isFinestEnabled())
        {
            logger.finest("BearerTokenValidationFilter doFilter : Before filtering request");
        }
        try
        {
            if (authorizationHeader != null && authorizationHeader.startsWith(BEARER))
            {
                final String accessToken = authorizationHeader.substring(BEARER.length()).trim();
                if (AuthorizationResourceHelper.isValid(accessToken))
                {
                    httpServletRequest.setAttribute(BEARER_TOKEN, accessToken);
                    filterChain.doFilter(servletRequest, servletResponse);
                }
                else
                {
                    logger.error("BearerTokenValidationFilter doFilter : Invalid bearer token.");
                    ((HttpServletResponse) servletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED,
                            "Invalid bearer token.");
                }
            }
            else if (!StringUtils.isBlank(securityHeader) && securityHeader.equals(SECURITY_POLICY_PASSED))
            {
                filterChain.doFilter(servletRequest, servletResponse);
            }
            else if (ServiceAttributeStore.getInstance().isEnableTokenlessAccess())
            {
                filterChain.doFilter(servletRequest, servletResponse);
            }
            else
            {
                logger.error("BearerTokenValidationFilter doFilter : " +
                        "Missing bearer token, security policy, or " +
                        "enableTokenlessAccess attribute in null or false");
                ((HttpServletResponse) servletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED,
                        "Missing bearer token, security policy, " +
                                "or enableTokenlessAccess attribute in null or false");
            }
        }
        catch (final AuthorizationHelperException e)
        {
            logger.error("BearerTokenValidationFilter doFilter : Error while validating access token", e);
            ((HttpServletResponse) servletResponse).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    "Error while validating access token");
        }
    }

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException
    {
        logger.finer("BearerTokenValidationFilter: init");
    }

    @Override
    public void destroy()
    {
        logger.finer("BearerTokenValidationFilter: destroy");
    }
}
