org.cloudfoundry.identity.uaa.oauth.ClientAdminEndpointsTests.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudfoundry.identity.uaa.oauth.ClientAdminEndpointsTests.java

Source

/*
 * Cloud Foundry 2012.02.03 Beta
 * Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
 *
 * This product is licensed to you under the Apache License, Version 2.0 (the "License").
 * You may not use this product except in compliance with the License.
 *
 * This product includes a number of subcomponents with
 * separate copyright notices and license terms. Your use of these
 * subcomponents is subject to the terms and conditions of the
 * subcomponent's license, as noted in the LICENSE file.
 */

package org.cloudfoundry.identity.uaa.oauth;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.cloudfoundry.identity.uaa.rest.QueryableResourceManager;
import org.cloudfoundry.identity.uaa.rest.SearchResults;
import org.cloudfoundry.identity.uaa.rest.SimpleAttributeNameMapper;
import org.cloudfoundry.identity.uaa.security.SecurityContextAccessor;
import org.cloudfoundry.identity.uaa.security.StubSecurityContextAccessor;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.BaseClientDetails;
import org.springframework.security.oauth2.provider.ClientAlreadyExistsException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientRegistrationService;
import org.springframework.security.oauth2.provider.NoSuchClientException;

/**
 * @author Dave Syer
 *
 */
public class ClientAdminEndpointsTests {

    private ClientAdminEndpoints endpoints = new ClientAdminEndpoints();

    private BaseClientDetails input = new BaseClientDetails();

    private BaseClientDetails details = new BaseClientDetails();

    @SuppressWarnings("unchecked")
    private QueryableResourceManager<ClientDetails> clientDetailsService = Mockito
            .mock(QueryableResourceManager.class);

    private SecurityContextAccessor securityContextAccessor = Mockito.mock(SecurityContextAccessor.class);

    private ClientRegistrationService clientRegistrationService = Mockito.mock(ClientRegistrationService.class);

    @Rule
    public ExpectedException expected = ExpectedException.none();

    @Before
    public void setUp() throws Exception {
        endpoints.setClientDetailsService(clientDetailsService);
        endpoints.setClientRegistrationService(clientRegistrationService);
        endpoints.setSecurityContextAccessor(securityContextAccessor);

        Map<String, String> attributeNameMap = new HashMap<String, String>();
        attributeNameMap.put("client_id", "clientId");
        attributeNameMap.put("resource_ids", "resourceIds");
        attributeNameMap.put("authorized_grant_types", "authorizedGrantTypes");
        attributeNameMap.put("redirect_uri", "registeredRedirectUri");
        attributeNameMap.put("access_token_validity", "accessTokenValiditySeconds");
        attributeNameMap.put("refresh_token_validity", "refreshTokenValiditySeconds");
        endpoints.setAttributeNameMapper(new SimpleAttributeNameMapper(attributeNameMap));

        input.setClientId("foo");
        input.setClientSecret("secret");
        input.setAuthorizedGrantTypes(Arrays.asList("authorization_code"));
        details = new BaseClientDetails(input);
        details.setResourceIds(Arrays.asList("none"));
        // refresh token is added automatically by endpoint validation
        details.setAuthorizedGrantTypes(Arrays.asList("authorization_code", "refresh_token"));
        details.setScope(Arrays.asList("uaa.none"));
        details.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("uaa.none"));
        endpoints.afterPropertiesSet();
    }

    @Test
    public void testStatistics() throws Exception {
        assertEquals(0, endpoints.getClientDeletes());
        assertEquals(0, endpoints.getClientSecretChanges());
        assertEquals(0, endpoints.getClientUpdates());
        assertEquals(0, endpoints.getErrorCounts().size());
        assertEquals(0, endpoints.getTotalClients());
    }

    @Test
    public void testCreateClientDetails() throws Exception {
        ClientDetails result = endpoints.createClientDetails(input);
        assertNull(result.getClientSecret());
        Mockito.verify(clientRegistrationService).addClientDetails(details);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void testCreateClientDetailsWithReservedId() throws Exception {
        input.setClientId("uaa");
        ClientDetails result = endpoints.createClientDetails(input);
        assertNull(result.getClientSecret());
        Mockito.verify(clientRegistrationService).addClientDetails(details);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void testCreateClientDetailsWithNoGrantType() throws Exception {
        input.setAuthorizedGrantTypes(Collections.<String>emptySet());
        ClientDetails result = endpoints.createClientDetails(input);
        assertNull(result.getClientSecret());
        Mockito.verify(clientRegistrationService).addClientDetails(details);
    }

    @Test
    public void testCreateClientDetailsWithClientCredentials() throws Exception {
        input.setAuthorizedGrantTypes(Arrays.asList("client_credentials"));
        details.setAuthorizedGrantTypes(input.getAuthorizedGrantTypes());
        ClientDetails result = endpoints.createClientDetails(input);
        assertNull(result.getClientSecret());
        Mockito.verify(clientRegistrationService).addClientDetails(details);
    }

    @Test
    public void testCreateClientDetailsWithAdditionalInformation() throws Exception {
        input.setAdditionalInformation(Collections.singletonMap("foo", "bar"));
        details.setAdditionalInformation(input.getAdditionalInformation());
        ClientDetails result = endpoints.createClientDetails(input);
        assertNull(result.getClientSecret());
        Mockito.verify(clientRegistrationService).addClientDetails(details);
    }

    @Test
    public void testResourceServerCreation() throws Exception {
        details.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("uaa.resource"));
        details.setScope(Arrays.asList(details.getClientId() + ".some"));
        details.setAuthorizedGrantTypes(Arrays.asList("client_credentials"));
        endpoints.createClientDetails(details);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void testCreateClientDetailsWithPasswordGrant() throws Exception {
        input.setAuthorizedGrantTypes(Arrays.asList("password"));
        ClientDetails result = endpoints.createClientDetails(input);
        assertNull(result.getClientSecret());
        Mockito.verify(clientRegistrationService).addClientDetails(details);
    }

    @Test
    public void testFindClientDetails() throws Exception {
        Mockito.when(clientDetailsService.query("filter", "sortBy", true))
                .thenReturn(Arrays.<ClientDetails>asList(details));
        SearchResults<?> result = endpoints.listClientDetails("client_id", "filter", "sortBy", "ascending", 1, 100);
        assertEquals(1, result.getResources().size());
        Mockito.verify(clientDetailsService).query("filter", "sortBy", true);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void testUpdateClientDetailsWithNullCallerAndInvalidScope() throws Exception {
        Mockito.when(clientDetailsService.retrieve(input.getClientId())).thenReturn(new BaseClientDetails(input));
        input.setScope(Arrays.asList("read"));
        ClientDetails result = endpoints.updateClientDetails(input, input.getClientId());
        assertNull(result.getClientSecret());
        details.setScope(Arrays.asList("read"));
        Mockito.verify(clientRegistrationService).updateClientDetails(details);
    }

    @Test
    public void testGetClientDetails() throws Exception {
        Mockito.when(clientDetailsService.retrieve(input.getClientId())).thenReturn(input);
        input.setScope(Arrays.asList(input.getClientId() + ".read"));
        input.setAdditionalInformation(Collections.singletonMap("foo", "bar"));
        ClientDetails result = endpoints.getClientDetails(input.getClientId());
        assertNull(result.getClientSecret());
        assertEquals(input.getAdditionalInformation(), result.getAdditionalInformation());
    }

    @Test
    public void testUpdateClientDetails() throws Exception {
        Mockito.when(clientDetailsService.retrieve(input.getClientId())).thenReturn(new BaseClientDetails(input));
        input.setScope(Arrays.asList(input.getClientId() + ".read"));
        ClientDetails result = endpoints.updateClientDetails(input, input.getClientId());
        assertNull(result.getClientSecret());
        details.setScope(Arrays.asList(input.getClientId() + ".read"));
        Mockito.verify(clientRegistrationService).updateClientDetails(details);
    }

    @Test
    public void testUpdateClientDetailsWithAdditionalInformation() throws Exception {
        Mockito.when(clientDetailsService.retrieve(input.getClientId())).thenReturn(new BaseClientDetails(input));
        input.setScope(Arrays.asList(input.getClientId() + ".read"));
        input.setAdditionalInformation(Collections.singletonMap("foo", "bar"));
        ClientDetails result = endpoints.updateClientDetails(input, input.getClientId());
        assertNull(result.getClientSecret());
        details.setScope(input.getScope());
        details.setAdditionalInformation(input.getAdditionalInformation());
        Mockito.verify(clientRegistrationService).updateClientDetails(details);
    }

    @Test
    public void testUpdateClientDetailsRemoveAdditionalInformation() throws Exception {
        input.setAdditionalInformation(Collections.singletonMap("foo", "bar"));
        Mockito.when(clientDetailsService.retrieve(input.getClientId())).thenReturn(new BaseClientDetails(input));
        input.setAdditionalInformation(Collections.<String, Object>emptyMap());
        ClientDetails result = endpoints.updateClientDetails(input, input.getClientId());
        assertNull(result.getClientSecret());
        Mockito.verify(clientRegistrationService).updateClientDetails(details);
    }

    @Test
    public void testPartialUpdateClientDetails() throws Exception {
        BaseClientDetails updated = new BaseClientDetails(details);
        input = new BaseClientDetails();
        input.setClientId("foo");
        Mockito.when(clientDetailsService.retrieve(input.getClientId())).thenReturn(details);
        input.setScope(Arrays.asList("foo.write"));
        updated.setScope(input.getScope());
        updated.setClientSecret(null);
        ClientDetails result = endpoints.updateClientDetails(input, input.getClientId());
        assertNull(result.getClientSecret());
        Mockito.verify(clientRegistrationService).updateClientDetails(updated);
    }

    @Test
    public void testChangeSecret() throws Exception {

        when(clientDetailsService.retrieve(details.getClientId())).thenReturn(details);

        SecurityContextAccessor sca = mock(SecurityContextAccessor.class);
        when(sca.getClientId()).thenReturn(details.getClientId());
        when(sca.isClient()).thenReturn(true);
        endpoints.setSecurityContextAccessor(sca);

        SecretChangeRequest change = new SecretChangeRequest();
        change.setOldSecret(details.getClientSecret());
        change.setSecret("newpassword");
        endpoints.changeSecret(details.getClientId(), change);
        Mockito.verify(clientRegistrationService).updateClientSecret(details.getClientId(), "newpassword");

    }

    @Test
    public void testChangeSecretDeniedForUser() throws Exception {

        when(clientDetailsService.retrieve(details.getClientId())).thenReturn(details);

        SecurityContextAccessor sca = mock(SecurityContextAccessor.class);
        when(sca.getClientId()).thenReturn(details.getClientId());
        when(sca.isClient()).thenReturn(false);
        endpoints.setSecurityContextAccessor(sca);

        SecretChangeRequest change = new SecretChangeRequest();
        change.setOldSecret(details.getClientSecret());
        change.setSecret("newpassword");
        expected.expect(IllegalStateException.class);
        expected.expectMessage("Only a client");
        endpoints.changeSecret(details.getClientId(), change);

    }

    @Test
    public void testChangeSecretDeniedForNonAdmin() throws Exception {

        when(clientDetailsService.retrieve(details.getClientId())).thenReturn(details);

        SecurityContextAccessor sca = mock(SecurityContextAccessor.class);
        when(sca.getClientId()).thenReturn("bar");
        when(sca.isClient()).thenReturn(true);
        when(sca.isAdmin()).thenReturn(false);
        endpoints.setSecurityContextAccessor(sca);

        SecretChangeRequest change = new SecretChangeRequest();
        change.setSecret("newpassword");
        expected.expect(IllegalStateException.class);
        expected.expectMessage("Not permitted to change");
        endpoints.changeSecret(details.getClientId(), change);

    }

    @Test
    public void testChangeSecretDeniedWhenOldSecretNotProvided() throws Exception {

        when(clientDetailsService.retrieve(details.getClientId())).thenReturn(details);

        SecurityContextAccessor sca = mock(SecurityContextAccessor.class);
        when(sca.getClientId()).thenReturn(details.getClientId());
        when(sca.isClient()).thenReturn(true);
        when(sca.isAdmin()).thenReturn(false);
        endpoints.setSecurityContextAccessor(sca);

        SecretChangeRequest change = new SecretChangeRequest();
        change.setSecret("newpassword");
        expected.expect(IllegalStateException.class);
        expected.expectMessage("Previous secret is required");
        endpoints.changeSecret(details.getClientId(), change);

    }

    @Test
    public void testChangeSecretDeniedWhenOldSecretNotProvidedEvenFormAdmin() throws Exception {

        when(clientDetailsService.retrieve(details.getClientId())).thenReturn(details);

        SecurityContextAccessor sca = mock(SecurityContextAccessor.class);
        when(sca.getClientId()).thenReturn(details.getClientId());
        when(sca.isClient()).thenReturn(true);
        when(sca.isAdmin()).thenReturn(true);
        endpoints.setSecurityContextAccessor(sca);

        SecretChangeRequest change = new SecretChangeRequest();
        change.setSecret("newpassword");
        expected.expect(IllegalStateException.class);
        expected.expectMessage("Previous secret is required");
        endpoints.changeSecret(details.getClientId(), change);

    }

    @Test
    public void testChangeSecretByAdmin() throws Exception {

        when(clientDetailsService.retrieve(details.getClientId())).thenReturn(details);

        SecurityContextAccessor sca = mock(SecurityContextAccessor.class);
        when(sca.getClientId()).thenReturn("admin");
        when(sca.isClient()).thenReturn(true);
        when(sca.isAdmin()).thenReturn(true);
        endpoints.setSecurityContextAccessor(sca);

        SecretChangeRequest change = new SecretChangeRequest();
        change.setOldSecret(details.getClientSecret());
        change.setSecret("newpassword");
        endpoints.changeSecret(details.getClientId(), change);
        Mockito.verify(clientRegistrationService).updateClientSecret(details.getClientId(), "newpassword");

    }

    @Test
    public void testRemoveClientDetailsAdminCaller() throws Exception {
        Mockito.when(securityContextAccessor.isAdmin()).thenReturn(true);
        Mockito.when(clientDetailsService.retrieve("foo")).thenReturn(details);
        ClientDetails result = endpoints.removeClientDetails("foo");
        assertNull(result.getClientSecret());
        Mockito.verify(clientRegistrationService).removeClientDetails("foo");
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void testScopeIsRestrictedByCaller() throws Exception {
        BaseClientDetails caller = new BaseClientDetails("caller", null, "none", "client_credentials,implicit",
                "uaa.none");
        when(clientDetailsService.retrieve("caller")).thenReturn(caller);
        endpoints.setSecurityContextAccessor(new StubSecurityContextAccessor() {
            @Override
            public String getClientId() {
                return "caller";
            }
        });
        details.setScope(Arrays.asList("some"));
        endpoints.createClientDetails(details);
    }

    @Test
    public void testValidScopeIsNotRestrictedByCaller() throws Exception {
        BaseClientDetails caller = new BaseClientDetails("caller", null, "none", "client_credentials,implicit",
                "uaa.none");
        when(clientDetailsService.retrieve("caller")).thenReturn(caller);
        endpoints.setSecurityContextAccessor(new StubSecurityContextAccessor() {
            @Override
            public String getClientId() {
                return "caller";
            }
        });
        details.setScope(Arrays.asList("none"));
        endpoints.createClientDetails(details);
    }

    @Test
    public void testClientPrefixScopeIsNotRestrictedByClient() throws Exception {
        BaseClientDetails caller = new BaseClientDetails("caller", null, "none", "client_credentials,implicit",
                "uaa.none");
        when(clientDetailsService.retrieve("caller")).thenReturn(caller);
        endpoints.setSecurityContextAccessor(new StubSecurityContextAccessor() {
            @Override
            public String getClientId() {
                return "caller";
            }
        });
        details.setScope(Arrays.asList(details.getClientId() + ".read"));
        endpoints.createClientDetails(details);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void testAuthorityIsRestrictedByCaller() throws Exception {
        BaseClientDetails caller = new BaseClientDetails("caller", null, "none", "client_credentials,implicit",
                "uaa.none");
        when(clientDetailsService.retrieve("caller")).thenReturn(caller);
        endpoints.setSecurityContextAccessor(new StubSecurityContextAccessor() {
            @Override
            public String getClientId() {
                return "caller";
            }
        });
        details.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("uaa.some"));
        endpoints.createClientDetails(details);
    }

    @Test
    public void testAuthorityAllowedByCaller() throws Exception {
        BaseClientDetails caller = new BaseClientDetails("caller", null, "uaa.none", "client_credentials,implicit",
                "uaa.none");
        when(clientDetailsService.retrieve("caller")).thenReturn(caller);
        endpoints.setSecurityContextAccessor(new StubSecurityContextAccessor() {
            @Override
            public String getClientId() {
                return "caller";
            }
        });
        details.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("uaa.none"));
        endpoints.createClientDetails(details);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void cannotExpandScope() throws Exception {
        BaseClientDetails caller = new BaseClientDetails();
        caller.setScope(Arrays.asList("none"));
        when(clientDetailsService.retrieve("caller")).thenReturn(caller);
        details.setAuthorizedGrantTypes(Arrays.asList("implicit"));
        details.setClientSecret("hello");
        endpoints.createClientDetails(details);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void implicitClientWithNonEmptySecretIsRejected() throws Exception {
        details.setAuthorizedGrantTypes(Arrays.asList("implicit"));
        details.setClientSecret("hello");
        endpoints.createClientDetails(details);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void implicitAndAuthorizationCodeClientIsRejected() throws Exception {
        details.setAuthorizedGrantTypes(Arrays.asList("implicit", "authorization_code"));
        details.setClientSecret("hello");
        endpoints.createClientDetails(details);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void implicitAndAuthorizationCodeClientIsRejectedWithNullPassword() throws Exception {
        details.setAuthorizedGrantTypes(Arrays.asList("implicit", "authorization_code"));
        details.setClientSecret(null);
        endpoints.createClientDetails(details);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void implicitAndAuthorizationCodeClientIsRejectedForAdmin() throws Exception {
        endpoints.setSecurityContextAccessor(new StubSecurityContextAccessor() {
            @Override
            public boolean isAdmin() {
                return true;
            }
        });
        details.setAuthorizedGrantTypes(Arrays.asList("implicit", "authorization_code"));
        details.setClientSecret("hello");
        endpoints.createClientDetails(details);
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void nonImplicitClientWithEmptySecretIsRejected() throws Exception {
        details.setAuthorizedGrantTypes(Arrays.asList("authorization_code"));
        details.setClientSecret("");
        endpoints.createClientDetails(details);
    }

    @Test
    public void updateNonImplicitClientWithEmptySecretIsOk() throws Exception {
        Mockito.when(securityContextAccessor.isAdmin()).thenReturn(true);
        details.setAuthorizedGrantTypes(Arrays.asList("authorization_code"));
        details.setClientSecret(null);
        endpoints.updateClientDetails(details, details.getClientId());
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void updateNonImplicitClientAndMakeItImplicit() throws Exception {
        assertFalse(details.getAuthorizedGrantTypes().contains("implicit"));
        details.setAuthorizedGrantTypes(Arrays.asList("authorization_code", "implicit"));
        details.setClientSecret(null);
        endpoints.updateClientDetails(details, details.getClientId());
    }

    @Test(expected = InvalidClientDetailsException.class)
    public void invalidGrantTypeIsRejected() throws Exception {
        details.setAuthorizedGrantTypes(Arrays.asList("not_a_grant_type"));
        endpoints.createClientDetails(details);
    }

    @Test
    public void testHandleNoSuchClient() throws Exception {
        ResponseEntity<Void> result = endpoints
                .handleNoSuchClient(new NoSuchClientException("No such client: foo"));
        assertEquals(HttpStatus.NOT_FOUND, result.getStatusCode());
    }

    @Test
    public void testHandleClientAlreadyExists() throws Exception {
        ResponseEntity<InvalidClientDetailsException> result = endpoints
                .handleClientAlreadyExists(new ClientAlreadyExistsException("No such client: foo"));
        assertEquals(HttpStatus.CONFLICT, result.getStatusCode());
    }

    @Test
    public void testErrorHandler() throws Exception {
        ResponseEntity<InvalidClientDetailsException> result = endpoints
                .handleInvalidClientDetails(new InvalidClientDetailsException("No such client: foo"));
        assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode());
        assertEquals(1, endpoints.getErrorCounts().size());
    }
}