org.apigw.authserver.svc.impl.TokenServicesImplTest.java Source code

Java tutorial

Introduction

Here is the source code for org.apigw.authserver.svc.impl.TokenServicesImplTest.java

Source

/**
 *   Copyright 2013 Stockholm County Council
 *
 *   This file is part of APIGW
 *
 *   APIGW is free software; you can redistribute it and/or modify
 *   it under the terms of version 2.1 of the GNU Lesser General Public
 *   License as published by the Free Software Foundation.
 *
 *   APIGW is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with APIGW; if not, write to the
 *   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 *   Boston, MA 02111-1307  USA
 *
 */
package org.apigw.authserver.svc.impl;

import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import static org.joda.time.DateTime.now;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.Assert;
import org.apigw.authserver.svc.ResidentServices;
import org.apigw.authserver.svc.TokenServices;
import org.apigw.authserver.svc.repository.CertifiedClientPermissionRepository;
import org.apigw.authserver.svc.repository.AuthorizationGrantRepository;
import org.apigw.authserver.svc.repository.CertifiedClientRepository;
import org.apigw.authserver.svc.repository.PermissionRepository;
import org.apigw.authserver.types.domain.CertifiedClientPermission;
import org.apigw.authserver.types.domain.AuthorizationGrant;
import org.apigw.authserver.types.domain.CertifiedClient;
import org.apigw.authserver.types.domain.Permission;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.DefaultAuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;

/**
 * This test class contains code derived from the TestRandomValueTokenServices
 * class in Spring Security OAuth 2.0 licensed under Apache License 2.0
 * (http://www.apache.org/licenses/LICENSE-2.0).
 * TestRandomValueTokenServices class in Spring Security OAuth 2.0
 * was originally written by Ryan Heaton and Dave Syer and the original
 * code is changed by Christian Hilmersson in this class.
 * See Spring Security OAuth at Github for more information:
 * https://github.com/SpringSource/spring-security-oauth/
 *
 * @author Christian Hilmersson
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(defaultRollback = true)
@ContextConfiguration(locations = "classpath:apigw-resources-test-config.xml")
public class TokenServicesImplTest {

    @Autowired
    private TokenServices services;

    @Autowired
    private AuthorizationGrantRepository authorizationGrantRepository;

    @Autowired
    private CertifiedClientPermissionRepository ccPermissionRepository;

    @Autowired
    private PermissionRepository permRepository;

    @Autowired
    private CertifiedClientRepository certifiedClientRepository;

    final DateTimeFormatter yyyyMMddFormatter = DateTimeFormat.forPattern("yyyyMMdd");

    private static final String CLIENT = "client";
    private static final String READ_SCOPE = "SCOPE_READ_SCHEDULE";
    private static final String WRITE_SCOPE = "SCOPE_WRITE_SCHEDULE";
    //    private static final String CITIZEN = "630928-3221";
    //    private static final String LEGAL_GUARDIAN = "188803099368";

    @After
    public void removeStore() {
        authorizationGrantRepository.deleteAll();
        ccPermissionRepository.deleteAll();
        permRepository.deleteAll();
        certifiedClientRepository.deleteAll();
    }

    @Before
    public void createStore() throws Exception {

        CertifiedClient client = new CertifiedClient();
        client.setClientId(CLIENT);
        client.setName("name");
        client.setX509CertificateIssuerDN("issuerDN");
        client.setX509CertificateSubjectDN("subjectDN");
        certifiedClientRepository.save(client);

        Permission perm1 = new Permission();
        perm1.setName(READ_SCOPE);
        perm1.setAccessTokenValiditySeconds(30 * 24 * 3600);
        permRepository.save(perm1);

        Permission perm2 = new Permission();
        perm2.setName(WRITE_SCOPE);
        perm2.setAccessTokenValiditySeconds(3600);
        permRepository.save(perm2);

        CertifiedClientPermission asRole1 = new CertifiedClientPermission();
        asRole1.setCertifiedClient(client);
        asRole1.setPermission(perm1);
        ccPermissionRepository.save(asRole1);

        CertifiedClientPermission asRole2 = new CertifiedClientPermission();
        asRole2.setCertifiedClient(client);
        asRole2.setPermission(perm2);
        ccPermissionRepository.save(asRole2);

        services.setSupportRefreshToken(false);
    }

    private Date createDate(int year, int month, int day, int hour, int minute) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(year, month - 1, day, hour, minute);
        return calendar.getTime();
    }

    private AuthorizationGrant buildAuthorizationGrantWithLegalGuardian() {
        final AuthorizationGrant authorizationGrant = buildAuthorizationGrant();
        authorizationGrant.setLegalGuardianResidentIdentificationNumber("111111-1111");

        return authorizationGrant;
    }

    private AuthorizationGrant buildAuthorizationGrant() {
        return buildAuthorizationGrant("121212-1212");
    }

    private AuthorizationGrant buildAuthorizationGrant(String residentIdentificationNumber) {
        Permission scopeReadSchedule = new Permission();
        scopeReadSchedule.setName(READ_SCOPE);
        Permission scopeWriteSchedule = new Permission();
        scopeWriteSchedule.setName(WRITE_SCOPE);

        CertifiedClientPermission ccPermissionRead = new CertifiedClientPermission();
        ccPermissionRead.setPermission(scopeReadSchedule);
        CertifiedClientPermission ccPermissionWrite = new CertifiedClientPermission();
        ccPermissionWrite.setPermission(scopeWriteSchedule);
        AuthorizationGrant authorizationGrant = new AuthorizationGrant();
        authorizationGrant.setGrantedPermissions(Arrays.asList(ccPermissionRead, ccPermissionWrite));
        authorizationGrant.setAccessToken("ABC");
        authorizationGrant.setAccessTokenExpires(createDate(2012, 5, 2, 16, 30));
        authorizationGrant.setClientId(CLIENT);
        authorizationGrant.setGrantExpires(createDate(2012, 6, 2, 15, 30));
        authorizationGrant.setRefreshToken("XYZ");
        authorizationGrant.setResidentIdentificationNumber(residentIdentificationNumber); //"121212-1212"
        authorizationGrant.setVersion(0);
        return authorizationGrant;
    }

    private AuthorizationGrant buildAuthorizationGrantReversedScopes() {
        Permission scopeReadSchedule = new Permission();
        scopeReadSchedule.setName(READ_SCOPE);
        Permission scopeWriteSchedule = new Permission();
        scopeWriteSchedule.setName(WRITE_SCOPE);

        CertifiedClientPermission ccPermissionRead = new CertifiedClientPermission();
        ccPermissionRead.setPermission(scopeReadSchedule);
        CertifiedClientPermission ccPermissionWrite = new CertifiedClientPermission();
        ccPermissionWrite.setPermission(scopeWriteSchedule);
        AuthorizationGrant authorizationGrant = new AuthorizationGrant();
        authorizationGrant.setGrantedPermissions(Arrays.asList(ccPermissionWrite, ccPermissionRead));
        authorizationGrant.setAccessToken("ABC");
        authorizationGrant.setAccessTokenExpires(createDate(2012, 5, 2, 16, 30));
        authorizationGrant.setClientId(CLIENT);
        authorizationGrant.setGrantExpires(createDate(2012, 6, 2, 15, 30));
        authorizationGrant.setRefreshToken("XYZ");
        authorizationGrant.setResidentIdentificationNumber("121212-1212");
        authorizationGrant.setVersion(0);
        return authorizationGrant;
    }

    /**
     * Check so that different order of the input scopes gives the same output.
     */
    @Test
    @DirtiesContext
    public void testBuildScopeSetFromAuthorizationGrantScopeOrder() {
        AuthorizationGrant authorizationGrant = buildAuthorizationGrant();

        TokenServicesImpl tokenServices = new TokenServicesImpl();
        Set<String> scope = tokenServices.buildScopeFromAuthorizationGrant(authorizationGrant);
        Assert.assertNotNull(scope);
        Assert.assertEquals(2, scope.size());
        Set<String> expectedScopes = new HashSet<String>(Arrays.asList(READ_SCOPE, WRITE_SCOPE));
        List<String> listOne = new ArrayList<String>();
        for (String actualScope : scope) {
            Assert.assertTrue(expectedScopes.remove(actualScope));
            listOne.add(actualScope);
        }
        // Create scope from reversed auth grant with list of scopes
        authorizationGrant = buildAuthorizationGrantReversedScopes();
        scope = tokenServices.buildScopeFromAuthorizationGrant(authorizationGrant);
        int i = 0;
        for (String actualScope : scope) {
            Assert.assertEquals(listOne.get(i), actualScope);
            i++;
        }
    }

    @Test
    @DirtiesContext
    public void testBuildScopeSetFromAuthorizationGrant() {
        AuthorizationGrant authorizationGrant = buildAuthorizationGrant();

        TokenServicesImpl tokenServices = new TokenServicesImpl();
        Set<String> scope = tokenServices.buildScopeFromAuthorizationGrant(authorizationGrant);
        Assert.assertNotNull(scope);
        Assert.assertEquals(2, scope.size());
        Set<String> expectedScopes = new HashSet<String>(Arrays.asList(READ_SCOPE, WRITE_SCOPE));
        for (String actualScope : scope) {
            Assert.assertTrue(expectedScopes.remove(actualScope));
        }
    }

    @Test
    @DirtiesContext
    public void testBuildAuthorizationGrantExpiresCitizenIs12Today() {

        long validitySeconds = 60 * 60 * 12;
        DateTime dateOfBirth = now().minusYears(12);
        String residentIdentificationNumber = yyyyMMddFormatter.print(dateOfBirth) + "-9876";
        services.setAccessTokenValiditySeconds(validitySeconds);

        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false, residentIdentificationNumber));
        OAuth2AccessToken accessToken = services.createAccessToken(authentication);

        Date expirationDate = accessToken.getExpiration(); // 12 hours from now
        DateTime expectedDate = now().plus(validitySeconds * 1000L);

        // check the dates are within one second of each other - assumption is that the unit test runs in less than one second
        assertTrue("expirationDate:" + expirationDate + " != expectedDate:" + expectedDate,
                Math.abs(expirationDate.getTime() - expectedDate.getMillis()) < 1000);
        assertTrue("expires too soon:" + accessToken.getExpiresIn() + " - validitySeconds:" + validitySeconds,
                accessToken.getExpiresIn() <= validitySeconds);
        assertTrue("expires too late:" + accessToken.getExpiresIn() + " - validitySeconds:" + validitySeconds,
                accessToken.getExpiresIn() >= validitySeconds - 1);
    }

    @Test
    @DirtiesContext
    public void testBuildAuthorizationGrantExpiresCitizenIs18Today() {

        long validitySeconds = 60 * 60 * 12; // 12 hours
        DateTime dateOfBirth = now().minusYears(18);
        String residentIdentificationNumber = yyyyMMddFormatter.print(dateOfBirth) + "-9876";
        services.setAccessTokenValiditySeconds(validitySeconds);

        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false, residentIdentificationNumber));
        OAuth2AccessToken accessToken = services.createAccessToken(authentication);

        Date expirationDate = accessToken.getExpiration(); // 12 hours from now
        DateTime expectedDate = now().plus(validitySeconds * 1000L);

        // check the dates are within one second of each other - assumption is that the unit test runs in less than one second
        assertTrue("expirationDate:" + expirationDate + " != expectedDate:" + expectedDate,
                Math.abs(expirationDate.getTime() - expectedDate.getMillis()) < 1000);
        assertTrue("expires too soon:" + accessToken.getExpiresIn() + " - validitySeconds:" + validitySeconds,
                accessToken.getExpiresIn() <= validitySeconds);
        assertTrue("expires too late:" + accessToken.getExpiresIn() + " - validitySeconds:" + validitySeconds,
                accessToken.getExpiresIn() >= validitySeconds - 1);
    }

    @Test
    @DirtiesContext
    public void testBuildAuthorizationGrantExpiresCitizenIs13Today() {

        long validitySeconds = 60 * 60 * 12; // 12 hours
        DateTime dateOfBirth = now().minusYears(13);
        String residentIdentificationNumber = yyyyMMddFormatter.print(dateOfBirth) + "-9876";
        services.setAccessTokenValiditySeconds(validitySeconds);

        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false, residentIdentificationNumber));
        OAuth2AccessToken accessToken = services.createAccessToken(authentication);

        assertTrue("token should be expired", accessToken.isExpired());
    }

    @Test
    @DirtiesContext
    public void testBuildAuthorizationGrantExpiresCitizenIs13Tomorrow() {

        long validitySeconds = 60 * 60 * 48; // 48 hours (use more than one day of expiration so we can make correct assumption about expected expiration)
        DateTime dateOfBirth = now().minusYears(13).plusDays(1);
        String residentIdentificationNumber = yyyyMMddFormatter.print(dateOfBirth) + "-9876";
        services.setAccessTokenValiditySeconds(validitySeconds);

        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false, residentIdentificationNumber));
        OAuth2AccessToken accessToken = services.createAccessToken(authentication);

        Date expirationDate = accessToken.getExpiration();
        DateTime expectedDate = now().withTimeAtStartOfDay().plusDays(1); // midnight
        // check the dates are within one second of each other - assumption is that the unit test runs in less than one second
        assertTrue("expirationDate:" + expirationDate + " != expectedDate:" + expectedDate,
                Math.abs(expirationDate.getTime() - expectedDate.getMillis()) < 1000);
        assertTrue(
                "expires too soon:" + accessToken.getExpiresIn() + " - seconds to midnight:"
                        + expectedDate.minus(now().getMillis()).getMillis() / 1000L,
                accessToken.getExpiresIn() <= expectedDate.minus(now().getMillis()).getMillis() / 1000L);
        assertTrue(
                "expires too late:" + accessToken.getExpiresIn() + " - seconds to midnight:"
                        + expectedDate.minus(now().getMillis()).getMillis() / 1000L,
                accessToken.getExpiresIn() >= expectedDate.minus(now().getMillis()).getMillis() / 1000L - 1);
    }

    @Test
    @DirtiesContext
    public void testBuildAccessTokenFromAuthorizationGrant() {
        AuthorizationGrant authorizationGrant = buildAuthorizationGrant();

        TokenServicesImpl tokenServices = new TokenServicesImpl();
        tokenServices.setSupportRefreshToken(true);
        OAuth2AccessToken accessToken = tokenServices.buildAccessTokenFromAuthorizationGrant(authorizationGrant,
                true);
        Assert.assertNotNull(accessToken);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");
        Assert.assertEquals("201205021630", sdf.format(accessToken.getExpiration()));
        Assert.assertEquals("XYZ", accessToken.getRefreshToken().getValue());
        Set<String> scope = accessToken.getScope();
        Assert.assertEquals(2, scope.size());
        Set<String> expectedScopes = new HashSet<String>(Arrays.asList(READ_SCOPE, WRITE_SCOPE));
        for (String actualScope : scope) {
            Assert.assertTrue(expectedScopes.remove(actualScope));
        }
        Assert.assertEquals(OAuth2AccessToken.BEARER_TYPE, accessToken.getTokenType());
        Assert.assertEquals("ABC", accessToken.getValue());
    }

    @Test
    @DirtiesContext
    public void testBuildAccessTokenFromAuthorizationGrantWithNoRefreshToken() {
        AuthorizationGrant authorizationGrant = buildAuthorizationGrant();

        TokenServicesImpl tokenServices = new TokenServicesImpl();
        tokenServices.setSupportRefreshToken(false);
        OAuth2AccessToken accessToken = tokenServices.buildAccessTokenFromAuthorizationGrant(authorizationGrant,
                true);
        Assert.assertNotNull(accessToken);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");
        Assert.assertEquals("201205021630", sdf.format(accessToken.getExpiration()));
        Assert.assertNull(accessToken.getRefreshToken());
    }

    private AuthorizationRequest createAuthorizationRequest(String client, Set<String> scope) {
        DefaultAuthorizationRequest authorizationRequest = new DefaultAuthorizationRequest(client, scope);
        authorizationRequest.setApproved(true);
        return authorizationRequest;
    }

    @Test
    @DirtiesContext
    public void testDoubleStoreToken() throws InterruptedException {
        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false));
        OAuth2AccessToken token1 = services.createAccessToken(authentication);
        Thread.sleep(1500);
        OAuth2AccessToken token2 = services.createAccessToken(authentication);
        // Make sure we don't get the same access token twice
        Assert.assertFalse(token1.getValue().equals(token2.getValue()));
        Assert.assertFalse(token1.getExpiration().equals(token2.getExpiration()));
    }

    @Test
    @DirtiesContext
    @Ignore
    public void testConcurrentTokenUpdate() throws InterruptedException {
        //        ExpiringOAuth2RefreshToken expectedExpiringRefreshToken = new ExpiringOAuth2RefreshToken("testToken", new Date(
        //                System.currentTimeMillis() + 100000));
        final OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false));

        final OAuth2AccessToken[] accessTokens = new OAuth2AccessToken[10];
        for (int i = 0; i < 10; i++) {
            accessTokens[i] = services.createAccessToken(authentication);
        }

        int numberOfConcurrent = 1000;
        List<Thread> executors = new ArrayList<Thread>();
        for (int i = 0; i < numberOfConcurrent; i++) {
            Thread concurrentUpdater = new Thread() {

                //                private OAuth2Authentication innerAuthentication = authentication;
                //                private TransactionalAuthServerTokenServicesDelegatorImpl innerServices = services;

                @Override
                public void run() {
                    System.out.println("To run createAccessToken");
                    try {
                        if (Math.random() > 0.8) {
                            services.createAccessToken(authentication);
                        } else {
                            OAuth2AccessToken tk = accessTokens[(int) Math.floor(Math.random() * 10)];
                            AuthorizationRequest request = createAuthorizationRequest(CLIENT, tk.getScope());
                            services.refreshAccessToken(tk.getValue(), request);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        //                        Thread.currentThread().interrupt();
                        //                        Assert.fail("Got exception: " + e.getMessage());
                        throw new RuntimeException("failed");

                    }

                    System.out.println("Ran createAccessToken");
                }
            };
            executors.add(concurrentUpdater);
        }
        for (Thread executor : executors) {
            executor.start();
        }
        for (Thread executor : executors) {
            executor.join();
        }
        Assert.assertEquals(1, authorizationGrantRepository.count());
    }

    @Test
    @DirtiesContext
    public void testRefreshedTokenHasScopes() throws Exception {
        services.setSupportRefreshToken(true);

        OAuth2Authentication expectedAuthentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(expectedAuthentication);

        AuthorizationRequest request = createAuthorizationRequest(CLIENT, Collections.<String>emptySet());
        OAuth2AccessToken refreshedAccessToken = services
                .refreshAccessToken(accessToken.getRefreshToken().getValue(), request);

        assertFalse(accessToken.getValue().equals(refreshedAccessToken.getValue()));
        assertEquals("[" + READ_SCOPE + "]", refreshedAccessToken.getScope().toString());
    }

    @Test
    @DirtiesContext
    public void testRefreshedTokenWithNarrowedScope() throws Exception {
        services.setSupportRefreshToken(true);

        OAuth2Authentication expectedAuthentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(READ_SCOPE, WRITE_SCOPE))),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(expectedAuthentication);
        assertEquals("[" + READ_SCOPE + ", " + WRITE_SCOPE + "]", accessToken.getScope().toString());

        AuthorizationRequest request = createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE));
        OAuth2AccessToken refreshedAccessToken = services
                .refreshAccessToken(accessToken.getRefreshToken().getValue(), request);

        assertEquals("[" + READ_SCOPE + "]", refreshedAccessToken.getScope().toString());
    }

    @Test(expected = InvalidScopeException.class)
    @DirtiesContext
    public void testRefreshedTokenWithAnotherScope() throws Exception {
        services.setSupportRefreshToken(true);

        OAuth2Authentication expectedAuthentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(READ_SCOPE))),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(expectedAuthentication);
        assertEquals("[" + READ_SCOPE + "]", accessToken.getScope().toString());

        AuthorizationRequest request = createAuthorizationRequest(CLIENT, Collections.singleton(WRITE_SCOPE));
        services.refreshAccessToken(accessToken.getRefreshToken().getValue(), request);
    }

    @Test(expected = InvalidScopeException.class)
    @DirtiesContext
    public void testRefreshedTokenOnExistingClientAndScope() throws Exception {
        services.setSupportRefreshToken(true);

        OAuth2Authentication firstAuthentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(READ_SCOPE))),
                new TestAuthentication(false));
        OAuth2AccessToken firstAccessToken = services.createAccessToken(firstAuthentication);
        assertEquals("[" + READ_SCOPE + "]", firstAccessToken.getScope().toString());

        OAuth2Authentication secondAuthentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(READ_SCOPE, WRITE_SCOPE))),
                new TestAuthentication(false));
        OAuth2AccessToken secondAccessToken = services.createAccessToken(secondAuthentication);
        assertEquals("[" + READ_SCOPE + ", " + WRITE_SCOPE + "]", secondAccessToken.getScope().toString());

        assertEquals(2, authorizationGrantRepository.count());

        for (AuthorizationGrant auth : authorizationGrantRepository.findAll()) {
            System.out.println(auth.getAuthenticationKey());
        }

        AuthorizationRequest request = createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE));
        services.refreshAccessToken(secondAccessToken.getRefreshToken().getValue(), request);
    }

    @Test(expected = InvalidGrantException.class)
    @DirtiesContext
    public void testRefreshTokenWithNoSupport() throws Exception {
        AuthorizationRequest request = createAuthorizationRequest(CLIENT, Collections.singleton(WRITE_SCOPE));
        services.refreshAccessToken("token", request);
    }

    @Test
    @DirtiesContext
    public void testUnlimitedTokenExpiry() throws Exception {
        services.setAccessTokenValiditySeconds(0);
        OAuth2Authentication expectedAuthentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(expectedAuthentication);

        assertEquals("[" + READ_SCOPE + "]", accessToken.getScope().toString());
        assertEquals(0, accessToken.getExpiresIn());
        assertEquals(null, accessToken.getExpiration());
    }

    @Test
    @DirtiesContext
    public void testDefaultTokenExpiry() throws Exception {
        services.setAccessTokenValiditySeconds(100);
        OAuth2Authentication expectedAuthentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(expectedAuthentication);
        assertTrue(100 >= accessToken.getExpiresIn());
    }

    @Test
    @DirtiesContext
    public void testRoleSpecificTokenExpiry() throws Exception {
        services.setAccessTokenValiditySeconds(100 * 24 * 3600);
        OAuth2Authentication expectedAuthentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(expectedAuthentication);
        assertTrue(30 * 24 * 3600 >= accessToken.getExpiresIn());
    }

    @Test
    @DirtiesContext
    public void testLeastRoleSpecificTokenExpiry() throws Exception {
        services.setAccessTokenValiditySeconds(100 * 24 * 3600);
        OAuth2Authentication expectedAuthentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(READ_SCOPE, WRITE_SCOPE))),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(expectedAuthentication);
        assertTrue(3600 >= accessToken.getExpiresIn());
    }

    @Test
    @DirtiesContext
    public void testOneAccessTokenPerAuthentication() throws Exception {
        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                new TestAuthentication(false));
        OAuth2AccessToken first = services.createAccessToken(authentication);

        assertEquals(1, authorizationGrantRepository.count());
        OAuth2AccessToken second = services.createAccessToken(authentication);
        assertFalse(first.equals(second));
        assertEquals(1, authorizationGrantRepository.count());
    }

    @Test
    @DirtiesContext
    public void testOneAccessTokenPerUniqueAuthentication() throws Exception {
        OAuth2AccessToken token1 = services.createAccessToken(
                new OAuth2Authentication(createAuthorizationRequest(CLIENT, Collections.singleton(READ_SCOPE)),
                        new TestAuthentication(false)));
        assertEquals(1, authorizationGrantRepository.count());
        OAuth2AccessToken token2 = services.createAccessToken(
                new OAuth2Authentication(createAuthorizationRequest(CLIENT, Collections.singleton(WRITE_SCOPE)),
                        new TestAuthentication(false)));
        assertEquals(2, authorizationGrantRepository.count());
        assertTrue(token1 != token2);
    }

    @Test
    @DirtiesContext
    public void testCreateAccessTokenWithDifferentOrderedScopes() {
        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(READ_SCOPE, WRITE_SCOPE))),
                new TestAuthentication(false));
        String accessToken = services.createAccessToken(authentication).getValue();
        assertNotNull(authorizationGrantRepository.findByAccessToken(accessToken));
        authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(WRITE_SCOPE, READ_SCOPE))),
                new TestAuthentication(false));
        services.createAccessToken(authentication);
        // Now the first access token should have been overwritten with a new one since
        // they have the same scope, only in different order
        Assert.assertNull(authorizationGrantRepository.findByAccessToken(accessToken));
    }

    @Test
    @DirtiesContext
    public void testLoadAuthentication() throws Exception {

        AuthorizationGrant auth = buildAuthorizationGrant();
        for (CertifiedClientPermission ccPermission : auth.getGrantedPermissions()) {
            ccPermission.setCertifiedClient(certifiedClientRepository.findOne(0L));
            ccPermission.setPermission(permRepository.findOne(0L));
            ccPermissionRepository.save(ccPermission);
        }
        auth.setAccessTokenExpires(new Date(System.currentTimeMillis() + 10000));

        authorizationGrantRepository.save(auth);

        assertEquals(1, authorizationGrantRepository.count());

        OAuth2Authentication storedAuthentication = services.loadAuthentication(auth.getAccessToken());
        assertNotNull(storedAuthentication);

    }

    @Test
    public void testLoadAuthenticationWithValidLegalGuardian() throws Exception {
        final OAuth2Authentication oAuth2Authentication = testLoadAuthenticationWithLegalGuardian(true);
        assertNotNull(oAuth2Authentication);
    }

    @Test(expected = InvalidGrantException.class)
    public void testLoadAuthenticationWithInvalidLegalGuardian() throws Exception {
        testLoadAuthenticationWithLegalGuardian(false);
    }

    private OAuth2Authentication testLoadAuthenticationWithLegalGuardian(boolean validLegalGuardian)
            throws Exception {
        final TokenServicesImpl tokenServices = new TokenServicesImpl();
        final AuthorizationGrant auth = buildAuthorizationGrantWithLegalGuardian();
        auth.setAccessTokenExpires(new DateTime().plusYears(1).toDate());

        final ResidentServices residentServices = mock(ResidentServices.class);
        final AuthorizationGrantRepository repo = mock(AuthorizationGrantRepository.class);

        when(repo.findByAccessToken(anyString())).thenReturn(auth);
        when(residentServices.validateLegalGuardian(anyString(), anyString())).thenReturn(validLegalGuardian);

        tokenServices.setResidentServices(residentServices);
        tokenServices.setAuthorizationGrantRepository(repo);

        return tokenServices.loadAuthentication(auth.getAccessToken());
    }

    @Test
    @DirtiesContext
    public void testFindTokensByUsername() {
        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(READ_SCOPE))),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(authentication);

        Collection<OAuth2AccessToken> tokens = services.findTokensByUserName("1912121212");
        assertEquals(1, tokens.size());
        assertEquals(accessToken.getValue(), tokens.iterator().next().getValue());
    }

    @Test
    public void testFindOnlyValidTokensByUsername() {
        final TokenServicesImpl tokenServices = new TokenServicesImpl();
        final AuthorizationGrant auth = buildAuthorizationGrantWithLegalGuardian();

        final ResidentServices residentServices = mock(ResidentServices.class);
        final AuthorizationGrantRepository repo = mock(AuthorizationGrantRepository.class);

        tokenServices.setResidentServices(residentServices);
        tokenServices.setAuthorizationGrantRepository(repo);

        when(repo.findByResidentIdentificationNumber(anyString())).thenReturn(Collections.nCopies(4, auth));
        when(residentServices.validateLegalGuardian(anyString(), anyString())).thenReturn(true, false, false, true);

        final Collection<OAuth2AccessToken> tokens = tokenServices
                .findTokensByUserName(auth.getResidentIdentificationNumber());

        assertEquals(2, tokens.size());
    }

    @Test
    @DirtiesContext
    public void testFindTokensByClientId() {
        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(READ_SCOPE))),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(authentication);

        Collection<OAuth2AccessToken> tokens = services.findTokensByClientId(CLIENT);
        assertEquals(1, tokens.size());
        assertEquals(accessToken.getValue(), tokens.iterator().next().getValue());
    }

    @Test(expected = InvalidGrantException.class)
    @DirtiesContext
    public void testRevokeToken() {
        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(READ_SCOPE))),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(authentication);

        boolean revoked = services.revokeToken(accessToken.getValue());
        assertTrue(revoked);

        services.getAccessToken(authentication);
    }

    @Test
    @DirtiesContext
    public void testGetClientId() {
        OAuth2Authentication authentication = new OAuth2Authentication(
                createAuthorizationRequest(CLIENT, new HashSet<String>(Arrays.asList(READ_SCOPE))),
                new TestAuthentication(false));
        OAuth2AccessToken accessToken = services.createAccessToken(authentication);

        String clientId = services.getClientId(accessToken.getValue());
        assertEquals(CLIENT, clientId);
    }

    @Test
    @DirtiesContext
    public void buildScopeFromAuthorizationGrant() {
        TokenServicesImpl servicesImpl = new TokenServicesImpl();
        AuthorizationGrant grant = buildAuthorizationGrant();
        Set<String> scopes = servicesImpl.buildScopeFromAuthorizationGrant(grant);
        assertTrue(scopes.contains(READ_SCOPE));
        assertTrue(scopes.contains(WRITE_SCOPE));
    }

    protected static class TestAuthentication extends AbstractAuthenticationToken {
        private static final long serialVersionUID = 1L;
        private final String principal;

        public TestAuthentication(boolean authenticated, String residentIdentificationNumber) {
            super(null);
            setAuthenticated(authenticated);
            this.principal = residentIdentificationNumber;
        }

        public TestAuthentication(boolean authenticated) {
            this(authenticated, "1912121212");
        }

        @Override
        public Object getCredentials() {
            return null;
        }

        @Override
        public Object getPrincipal() {
            return this.principal;
        }
    }

}