Skip to content

Commit

Permalink
Do not allow query string parameters to /oauth/token
Browse files Browse the repository at this point in the history
  • Loading branch information
fhanik committed Feb 2, 2017
1 parent 3d8541f commit bc6e201
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 4 deletions.
Expand Up @@ -26,11 +26,14 @@
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;


import javax.servlet.http.HttpServletRequest;
import java.security.Principal; import java.security.Principal;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;


import static java.util.Collections.singleton; import static java.util.Collections.singleton;
import static org.springframework.http.HttpStatus.NOT_ACCEPTABLE;
import static org.springframework.util.StringUtils.hasText;
import static org.springframework.web.bind.annotation.RequestMethod.GET; import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST; import static org.springframework.web.bind.annotation.RequestMethod.POST;


Expand All @@ -47,7 +50,12 @@ public ResponseEntity<OAuth2AccessToken> doDelegateGet(Principal principal,


@RequestMapping(value = "**", method = POST) @RequestMapping(value = "**", method = POST)
public ResponseEntity<OAuth2AccessToken> doDelegatePost(Principal principal, public ResponseEntity<OAuth2AccessToken> doDelegatePost(Principal principal,
@RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException { @RequestParam Map<String, String> parameters,
HttpServletRequest request) throws HttpRequestMethodNotSupportedException {
if (hasText(request.getQueryString())) {
logger.debug("Call to /oauth/token contains a query string. Aborting.");
throw new HttpRequestMethodNotSupportedException("POST");
}
return super.postAccessToken(principal, parameters); return super.postAccessToken(principal, parameters);
} }


Expand All @@ -56,6 +64,25 @@ public void setAllowedRequestMethods(Set<HttpMethod> allowedRequestMethods) {
super.setAllowedRequestMethods(singleton(HttpMethod.POST)); super.setAllowedRequestMethods(singleton(HttpMethod.POST));
} }


@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@Override
public ResponseEntity<OAuth2Exception> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) throws Exception {
ResponseEntity<OAuth2Exception> result = super.handleHttpRequestMethodNotSupportedException(e);
if (HttpMethod.POST.matches(e.getMethod())) {
OAuth2Exception cause = new OAuth2Exception("Parameters must be passed in the body of the request", result.getBody().getCause()) {
public String getOAuth2ErrorCode() {
return "query_string_not_allowed";
}

public int getHttpErrorCode() {
return NOT_ACCEPTABLE.value();
}
};
result = new ResponseEntity<>(cause, result.getHeaders(), NOT_ACCEPTABLE);
}
return result;
}

@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)
@Override @Override
public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception { public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception {
Expand Down
Expand Up @@ -18,6 +18,8 @@
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException;


Expand All @@ -32,6 +34,7 @@
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.springframework.http.HttpMethod.GET; import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST; import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.HttpStatus.NOT_ACCEPTABLE;




public class UaaTokenEndpointTests { public class UaaTokenEndpointTests {
Expand All @@ -55,10 +58,35 @@ public void setAllowedRequestMethods() throws Exception {
} }


@Test(expected = HttpRequestMethodNotSupportedException.class) @Test(expected = HttpRequestMethodNotSupportedException.class)
public void call_to_get_always_throws() throws Exception { public void call_to_get_always_throws_super_method() throws Exception {
UaaTokenEndpoint endpoint = new UaaTokenEndpoint(); UaaTokenEndpoint endpoint = new UaaTokenEndpoint();
endpoint.setAllowedRequestMethods(allowedRequestMethods); endpoint.setAllowedRequestMethods(allowedRequestMethods);
endpoint.getAccessToken(mock(Principal.class), emptyMap()); try {
endpoint.getAccessToken(mock(Principal.class), emptyMap());
} catch (HttpRequestMethodNotSupportedException e) {
assertEquals("GET", e.getMethod());
throw e;
}
}


@Test(expected = HttpRequestMethodNotSupportedException.class)
public void call_to_get_always_throws_override_method() throws Exception {
UaaTokenEndpoint endpoint = new UaaTokenEndpoint();
endpoint.setAllowedRequestMethods(allowedRequestMethods);
try {
endpoint.doDelegateGet(mock(Principal.class), emptyMap());
} catch (HttpRequestMethodNotSupportedException e) {
assertEquals("GET", e.getMethod());
throw e;
}
}

@Test
public void call_to_post_with_query_string_throws_not_acceptable() throws Exception {
ResponseEntity<OAuth2Exception> result = endpoint.handleHttpRequestMethodNotSupportedException(new HttpRequestMethodNotSupportedException("POST"));
assertNotNull(result);
assertEquals(NOT_ACCEPTABLE, result.getStatusCode());
} }


} }
Expand Up @@ -211,14 +211,33 @@ public void token_endpoint_post() throws Exception {
try_token_with_non_post(post("/oauth/token"), status().isOk()); try_token_with_non_post(post("/oauth/token"), status().isOk());
} }


public void try_token_with_non_post(MockHttpServletRequestBuilder builder, ResultMatcher status) throws Exception { @Test
public void token_endpoint_post_query_string() throws Exception {
String username = setUpUserForPasswordGrant();

getMockMvc().perform(
post("/oauth/token?client_id=cf&client_secret=&grant_type=password&username={username}&password=secret", username)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_FORM_URLENCODED))
.andDo(print())
.andExpect(status().isNotAcceptable())
.andExpect(content().string(containsString("query_string_not_allowed")))
.andExpect(content().string(containsString("Parameters must be passed in the body of the request")));
}

public String setUpUserForPasswordGrant() throws Exception {
String username = "testuser" + generator.generate(); String username = "testuser" + generator.generate();
String userScopes = "uaa.user"; String userScopes = "uaa.user";
ScimUser user = setUpUser(username, userScopes, OriginKeys.UAA, IdentityZone.getUaa().getId()); ScimUser user = setUpUser(username, userScopes, OriginKeys.UAA, IdentityZone.getUaa().getId());
ScimUserProvisioning provisioning = getWebApplicationContext().getBean(ScimUserProvisioning.class); ScimUserProvisioning provisioning = getWebApplicationContext().getBean(ScimUserProvisioning.class);
ScimUser scimUser = provisioning.retrieve(user.getId()); ScimUser scimUser = provisioning.retrieve(user.getId());
assertNull(scimUser.getLastLogonTime()); assertNull(scimUser.getLastLogonTime());
assertNull(scimUser.getPreviousLogonTime()); assertNull(scimUser.getPreviousLogonTime());
return username;
}

public void try_token_with_non_post(MockHttpServletRequestBuilder builder, ResultMatcher status) throws Exception {
String username = setUpUserForPasswordGrant();


getMockMvc().perform( getMockMvc().perform(
builder builder
Expand Down

0 comments on commit bc6e201

Please sign in to comment.