package com.avaya.zephyr.services.sample_services.Authorization;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import com.avaya.collaboration.authorization.AuthorizationHelperException;
import com.avaya.collaboration.authorization.resource.AuthorizationResourceHelper;
import com.avaya.collaboration.authorization.spi.ResourceHelperProvider;
import com.avaya.collaboration.util.logger.Logger;
import com.avaya.zephyr.common.testutils.deencapsulation.Deencapsulation;
import com.avaya.zephyr.services.sample_services.Authorization.util.ServiceAttributeStore;

@RunWith(MockitoJUnitRunner.class)
public class BearerTokenValidationFilterTest
{

    BearerTokenValidationFilter classToTest;
    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String BEARER = "Bearer";
    private static final String SECURITY_POLICY_HEADER = "X-Av-HTTP-Security-Policy";
    private static final String BEARER_TOKEN = "bearerToken";

    @Mock
    HttpServletRequest mockHttpServletRequest;

    @Mock
    HttpServletResponse mockHttpServletResponse;

    @Mock
    FilterChain mockFilterChain;

    @Mock
    FilterConfig mockFilterConfig;

    @Mock
    Logger mockLogger;

    @Mock
    ResourceHelperProvider mockResourceHelperDelegate;

    @Before
    public void before()
    {
        classToTest = new BearerTokenValidationFilter(mockLogger);
        Deencapsulation.setField(AuthorizationResourceHelper.class, "resourceHelperDelegate",
                mockResourceHelperDelegate);
    }

    @Test
    public void doFilter_ValidBearerToken_SuccessfulRequest() throws Exception
    {
        when(mockHttpServletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(BEARER + " token");
        when(mockHttpServletRequest.getHeader(SECURITY_POLICY_HEADER)).thenReturn("no");
        when(mockResourceHelperDelegate.isValid("token")).thenReturn(true);

        classToTest.doFilter(mockHttpServletRequest, mockHttpServletResponse, mockFilterChain);

        verify(mockHttpServletRequest).setAttribute(BEARER_TOKEN, "token");
        verify(mockFilterChain).doFilter(mockHttpServletRequest, mockHttpServletResponse);
    }

    @Test
    public void doFilter_InvalidBearerToken_401() throws Exception
    {
        when(mockHttpServletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(BEARER + " invalid_token");
        when(mockHttpServletRequest.getHeader(SECURITY_POLICY_HEADER)).thenReturn("no");
        when(mockResourceHelperDelegate.isValid("invalid_token")).thenReturn(false);

        classToTest.doFilter(mockHttpServletRequest, mockHttpServletResponse, mockFilterChain);

        verify(mockHttpServletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid bearer token.");
        verify(mockHttpServletRequest).getHeader(AUTHORIZATION_HEADER);
        verify(mockHttpServletRequest).getHeader(SECURITY_POLICY_HEADER);
        verifyNoMoreInteractions(mockHttpServletRequest);
        verifyZeroInteractions(mockFilterChain);
    }

    @Test
    public void doFilter_AuthorizationHelperException_500() throws Exception
    {
        when(mockHttpServletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(BEARER + " token");
        when(mockHttpServletRequest.getHeader(SECURITY_POLICY_HEADER)).thenReturn("no");
        when(mockResourceHelperDelegate.isValid("token"))
                .thenThrow(new AuthorizationHelperException("AuthorizationHelperException"));
        classToTest.doFilter(mockHttpServletRequest, mockHttpServletResponse, mockFilterChain);
        verifyZeroInteractions(mockFilterChain);
        verify(mockHttpServletResponse).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                "Error while validating access token");
    }

    @Test
    public void doFilter_NoBearerTokenValidSecurityHeader_SuccessfulRequest() throws Exception
    {
        when(mockHttpServletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(null);
        when(mockHttpServletRequest.getHeader(SECURITY_POLICY_HEADER)).thenReturn("passed");
        classToTest.doFilter(mockHttpServletRequest, mockHttpServletResponse, mockFilterChain);
        verify(mockFilterChain).doFilter(mockHttpServletRequest, mockHttpServletResponse);
    }

    @Test
    public void doFilter_NoBearerTokenTokenlessAccessTrue_SuccessfulRequest() throws Exception
    {
        ServiceAttributeStore.getInstance().setEnableTokenlessAccess(true);
        when(mockHttpServletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(null);
        when(mockHttpServletRequest.getHeader(SECURITY_POLICY_HEADER)).thenReturn("no");

        classToTest.doFilter(mockHttpServletRequest, mockHttpServletResponse, mockFilterChain);

        verify(mockFilterChain).doFilter(mockHttpServletRequest, mockHttpServletResponse);
    }

    @Test
    public void doFilter_InvalidRequest_401() throws Exception
    {
        when(mockHttpServletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(null);
        when(mockHttpServletRequest.getHeader(SECURITY_POLICY_HEADER)).thenReturn("no");
        classToTest.doFilter(mockHttpServletRequest, mockHttpServletResponse, mockFilterChain);
        verifyZeroInteractions(mockFilterChain);
        verify(mockHttpServletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED,
                "Missing bearer token, security policy, " +
                        "or enableTokenlessAccess attribute in null or false");
    }
}
