package org.springframework.security.oauth2.provider; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import java.util.Arrays; import org.junit.Rule; import org.junit.Test; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.test.OAuth2ContextConfiguration; import org.springframework.security.oauth2.client.test.OAuth2ContextSetup; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.exceptions.InsufficientScopeException; import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException; /** * @author Dave Syer */ public class AdminEndpointsTests { @Rule public ServerRunning serverRunning = ServerRunning.isRunning(); @Rule public OAuth2ContextSetup context = OAuth2ContextSetup.standard(serverRunning); @Test @OAuth2ContextConfiguration(ResourceOwnerReadOnly.class) public void testListTokensByUser() throws Exception { ResponseEntity<String> result = serverRunning.getForString("/sparklr2/oauth/clients/my-trusted-client/users/marissa/tokens"); assertEquals(HttpStatus.OK, result.getStatusCode()); // System.err.println(result.getBody()); assertTrue(result.getBody().contains(context.getAccessToken().getValue())); } @Test @OAuth2ContextConfiguration(ResourceOwnerWriteOnly.class) public void testRevokeTokenByUser() throws Exception { OAuth2AccessToken token = context.getAccessToken(); String tokenValueBeforeDeletion = token.getValue(); HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); HttpEntity<?> request = new HttpEntity<Void>(headers); assertEquals( HttpStatus.NO_CONTENT, serverRunning .getRestTemplate() .exchange(serverRunning.getUrl("/sparklr2/oauth/users/{user}/tokens/{token}"), HttpMethod.DELETE, request, Void.class, "marissa", token.getValue()).getStatusCode()); try { // The request above will delete the oauth token so that the next request will initially fail. However, // the failure will be detected and a new access token will be obtained. The new access token // only has "write" scope and the requested resource needs "read" scope. So, an insufficient_scope // exception should be thrown. ResponseEntity<String> result = serverRunning.getForString("/sparklr2/oauth/clients/my-client-with-registered-redirect/users/marissa/tokens", headers); fail("Should have thrown an exception"); assertNotNull(result); } catch (InsufficientScopeException ex) { assertEquals(HttpStatus.FORBIDDEN.value(), ex.getHttpErrorCode()); assertEquals("insufficient_scope", ex.getOAuth2ErrorCode()); String secondTokenWithWriteOnlyScope = context.getOAuth2ClientContext().getAccessToken().getValue(); assertNotNull(secondTokenWithWriteOnlyScope); assertFalse(secondTokenWithWriteOnlyScope.equals(tokenValueBeforeDeletion)); } } @Test @OAuth2ContextConfiguration(ClientCredentialsReadOnly.class) public void testClientListsTokensOfUser() throws Exception { ResponseEntity<String> result = serverRunning.getForString("/sparklr2/oauth/clients/my-client-with-registered-redirect/users/marissa/tokens"); assertEquals(HttpStatus.OK, result.getStatusCode()); assertTrue(result.getBody().startsWith("[")); assertTrue(result.getBody().endsWith("]")); assertTrue(result.getBody().length() > 0); } @Test @OAuth2ContextConfiguration(ResourceOwnerReadOnly.class) public void testCannotListTokensOfAnotherUser() throws Exception { try { serverRunning.getStatusCode("/sparklr2/oauth/clients/my-client-with-registered-redirect/users/foo/tokens"); fail("Should have thrown an exception"); } catch (UserDeniedAuthorizationException ex) { // assertEquals(HttpStatus.FORBIDDEN.value(), ex.getHttpErrorCode()); assertEquals("access_denied", ex.getOAuth2ErrorCode()); } } @Test @OAuth2ContextConfiguration(ClientCredentialsReadOnly.class) public void testListTokensByClient() throws Exception { ResponseEntity<String> result = serverRunning .getForString("/sparklr2/oauth/clients/my-client-with-registered-redirect/tokens"); assertEquals(HttpStatus.OK, result.getStatusCode()); assertTrue(result.getBody().contains(context.getAccessToken().getValue())); } @Test @OAuth2ContextConfiguration(ResourceOwnerReadOnly.class) public void testUserCannotListTokensOfClient() throws Exception { try { serverRunning.getStatusCode("/sparklr2/oauth/clients/my-client-with-registered-redirect/tokens"); fail("Should have thrown an exception"); } catch (UserDeniedAuthorizationException ex) { // assertEquals(HttpStatus.FORBIDDEN.value(), ex.getHttpErrorCode()); assertEquals("access_denied", ex.getOAuth2ErrorCode()); } } static class ResourceOwnerReadOnly extends ResourceOwnerPasswordResourceDetails { public ResourceOwnerReadOnly(Object target) { setClientId("my-trusted-client"); setId(getClientId()); setScope(Arrays.asList("read")); setUsername("marissa"); setPassword("koala"); AdminEndpointsTests test = (AdminEndpointsTests) target; setAccessTokenUri(test.serverRunning.getUrl("/sparklr2/oauth/token")); } } static class ClientCredentialsReadOnly extends ClientCredentialsResourceDetails { public ClientCredentialsReadOnly(Object target) { setClientId("my-client-with-registered-redirect"); setId(getClientId()); setScope(Arrays.asList("read")); AdminEndpointsTests test = (AdminEndpointsTests) target; setAccessTokenUri(test.serverRunning.getUrl("/sparklr2/oauth/token")); } } static class ResourceOwnerWriteOnly extends ResourceOwnerReadOnly { public ResourceOwnerWriteOnly(Object target) { super(target); setScope(Arrays.asList("write")); } } }