com.netflix.genie.web.security.oauth2.pingfederate.PingFederateRemoteTokenServicesUnitTests.java Source code

Java tutorial

Introduction

Here is the source code for com.netflix.genie.web.security.oauth2.pingfederate.PingFederateRemoteTokenServicesUnitTests.java

Source

/*
 *
 *  Copyright 2016 Netflix, Inc.
 *
 *     Licensed 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 com.netflix.genie.web.security.oauth2.pingfederate;

import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.core.Options;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.netflix.genie.test.categories.UnitTest;
import com.netflix.spectator.api.Counter;
import com.netflix.spectator.api.Id;
import com.netflix.spectator.api.Registry;
import com.netflix.spectator.api.Timer;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.Mockito;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;

import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Unit tests for PingFederateRemoteTokenServices.
 *
 * @author tgianos
 * @since 3.0.0
 */
@Category(UnitTest.class)
public class PingFederateRemoteTokenServicesUnitTests {

    private static final String CLIENT_ID = UUID.randomUUID().toString();
    private static final String CLIENT_SECRET = UUID.randomUUID().toString();
    private static final String CHECK_TOKEN_ENDPOINT_URL = UUID.randomUUID().toString();

    /**
     * Create a mock server.
     */
    @Rule
    @SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
    public WireMockRule wireMockRule = new WireMockRule(Options.DYNAMIC_PORT);

    private ResourceServerProperties resourceServerProperties;
    private Registry registry;
    private Id validationError;
    private Timer authenticationTimer;

    /**
     * Setup for the tests.
     */
    @Before
    public void setup() {
        this.resourceServerProperties = new ResourceServerProperties(CLIENT_ID, CLIENT_SECRET);
        this.resourceServerProperties.setTokenInfoUri(CHECK_TOKEN_ENDPOINT_URL);
        this.registry = Mockito.mock(Registry.class);
        this.validationError = Mockito.mock(Id.class);
        this.authenticationTimer = Mockito.mock(Timer.class);
        final Timer pingFederateAPITimer = Mockito.mock(Timer.class);
        Mockito.when(this.registry.createId(Mockito.anyString())).thenReturn(this.validationError);
        Mockito.when(this.registry.timer(PingFederateRemoteTokenServices.AUTHENTICATION_TIMER_NAME))
                .thenReturn(this.authenticationTimer);
        Mockito.when(this.registry.timer(PingFederateRemoteTokenServices.API_TIMER_NAME))
                .thenReturn(pingFederateAPITimer);
    }

    /**
     * Make sure we can't construct without a client id.
     */
    @Test(expected = IllegalStateException.class)
    public void cantConstructWithoutClientId() {
        final ResourceServerProperties properties = new ResourceServerProperties(null, null);
        final AccessTokenConverter converter = new DefaultAccessTokenConverter();
        new PingFederateRemoteTokenServices(properties, converter, this.registry);
    }

    /**
     * Make sure we can't construct without a client secret.
     */
    @Test(expected = IllegalStateException.class)
    public void cantConstructWithoutClientSecret() {
        final ResourceServerProperties properties = new ResourceServerProperties("AnID", null);
        final AccessTokenConverter converter = new DefaultAccessTokenConverter();
        new PingFederateRemoteTokenServices(properties, converter, this.registry);
    }

    /**
     * Make sure we can't construct without a check token url.
     */
    @Test(expected = IllegalStateException.class)
    public void cantConstructWithoutCheckTokenURL() {
        this.resourceServerProperties.setTokenInfoUri(null);
        final AccessTokenConverter converter = new DefaultAccessTokenConverter();
        new PingFederateRemoteTokenServices(this.resourceServerProperties, converter, this.registry);
    }

    /**
     * Make sure we can validate a token.
     */
    @Test
    public void canLoadAuthentication() {
        final AccessTokenConverter converter = Mockito.mock(AccessTokenConverter.class);
        final RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
        final PingFederateRemoteTokenServices services = new PingFederateRemoteTokenServices(
                this.resourceServerProperties, converter, this.registry);
        services.setRestTemplate(restTemplate);
        final String accessToken = UUID.randomUUID().toString();

        final String clientId = UUID.randomUUID().toString();
        final String scope1 = UUID.randomUUID().toString();
        final String scope2 = UUID.randomUUID().toString();

        final Map<String, Object> map = Maps.newHashMap();
        map.put(PingFederateRemoteTokenServices.CLIENT_ID_KEY, clientId);
        map.put(PingFederateRemoteTokenServices.SCOPE_KEY, scope1 + " " + scope2);

        @SuppressWarnings("unchecked")
        final ResponseEntity<Map> response = Mockito.mock(ResponseEntity.class);

        Mockito.when(restTemplate.exchange(Mockito.eq(CHECK_TOKEN_ENDPOINT_URL), Mockito.eq(HttpMethod.POST),
                Mockito.any(HttpEntity.class), Mockito.eq(Map.class))).thenReturn(response);

        Mockito.when(response.getBody()).thenReturn(map);

        final SimpleGrantedAuthority scope1Authority = new SimpleGrantedAuthority(scope1);
        final SimpleGrantedAuthority scope2Authority = new SimpleGrantedAuthority(scope2);
        final Set<GrantedAuthority> authorities = Sets.newHashSet(scope1Authority, scope2Authority);

        final Authentication authentication = new UsernamePasswordAuthenticationToken(clientId, "NA", authorities);

        final OAuth2Authentication oauth2Authentication = new OAuth2Authentication(
                Mockito.mock(OAuth2Request.class), authentication);

        Mockito.when(converter.extractAuthentication(Mockito.eq(map))).thenReturn(oauth2Authentication);

        final OAuth2Authentication result = services.loadAuthentication(accessToken);
        Assert.assertThat(result, Matchers.is(oauth2Authentication));
        Assert.assertThat(result.getPrincipal(), Matchers.is(clientId));
        Assert.assertThat(result.getAuthorities().size(), Matchers.is(2));
        Assert.assertTrue(result.getAuthorities().contains(scope1Authority));
        Assert.assertTrue(result.getAuthorities().contains(scope2Authority));
        Mockito.verify(this.authenticationTimer, Mockito.times(1)).record(Mockito.anyLong(),
                Mockito.eq(TimeUnit.NANOSECONDS));
    }

    /**
     * Make sure invalid response from server causes authentication to fail.
     */
    @Test(expected = InvalidTokenException.class)
    public void cantLoadAuthenticationOnError() {
        final AccessTokenConverter converter = Mockito.mock(AccessTokenConverter.class);
        final RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
        final PingFederateRemoteTokenServices services = new PingFederateRemoteTokenServices(
                this.resourceServerProperties, converter, this.registry);
        services.setRestTemplate(restTemplate);
        final String accessToken = UUID.randomUUID().toString();

        final Map<String, Object> map = Maps.newHashMap();
        map.put(PingFederateRemoteTokenServices.ERROR_KEY, UUID.randomUUID().toString());

        @SuppressWarnings("unchecked")
        final ResponseEntity<Map> response = Mockito.mock(ResponseEntity.class);

        Mockito.when(restTemplate.exchange(Mockito.eq(CHECK_TOKEN_ENDPOINT_URL), Mockito.eq(HttpMethod.POST),
                Mockito.any(HttpEntity.class), Mockito.eq(Map.class))).thenReturn(response);

        Mockito.when(response.getBody()).thenReturn(map);

        // Should throw exception based on error key being present
        services.loadAuthentication(accessToken);
    }

    /**
     * Make sure rest call failures are handled.
     */
    @Test
    public void cantLoadAuthenticationOnRestError() {
        final String path = UUID.randomUUID().toString();
        final String uri = "http://localhost:" + this.wireMockRule.port() + "/" + path;
        final int status = HttpStatus.NOT_FOUND.value();
        WireMock.post(WireMock.urlEqualTo(uri)).willReturn(WireMock.aResponse().withStatus(status));
        final AccessTokenConverter converter = Mockito.mock(AccessTokenConverter.class);
        this.resourceServerProperties = new ResourceServerProperties(CLIENT_ID, CLIENT_SECRET);
        // Some resource no one should ever have
        this.resourceServerProperties.setTokenInfoUri(uri);
        final PingFederateRemoteTokenServices services = new PingFederateRemoteTokenServices(
                this.resourceServerProperties, converter, this.registry);
        final String accessToken = UUID.randomUUID().toString();
        final Counter restErrorCounter = Mockito.mock(Counter.class);
        final Id finalValidationId = Mockito.mock(Id.class);
        Mockito.when(this.validationError.withTag(Mockito.anyString(), Mockito.eq(Integer.toString(status))))
                .thenReturn(finalValidationId);
        Mockito.when(this.registry.counter(finalValidationId)).thenReturn(restErrorCounter);

        // Should throw exception based on error key being present
        try {
            services.loadAuthentication(accessToken);
            Assert.fail();
        } catch (final HttpClientErrorException | HttpServerErrorException e) {
            Mockito.verify(restErrorCounter, Mockito.times(1)).increment();
        }
    }

    /**
     * This method isn't implemented for Ping Federate currently. Make sure this fails in case we ever implement it
     * and need to update the tests.
     */
    @Test(expected = UnsupportedOperationException.class)
    public void cantReadAccessToken() {
        final AccessTokenConverter converter = new DefaultAccessTokenConverter();
        final PingFederateRemoteTokenServices services = new PingFederateRemoteTokenServices(
                this.resourceServerProperties, converter, this.registry);
        services.readAccessToken(UUID.randomUUID().toString());
    }
}