1 package sparklr.common;
2
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertTrue;
5 import static org.junit.Assert.fail;
6
7 import java.io.IOException;
8 import java.nio.charset.Charset;
9 import java.util.Arrays;
10 import java.util.List;
11
12 import org.junit.Test;
13 import org.springframework.http.HttpHeaders;
14 import org.springframework.http.HttpStatus;
15 import org.springframework.http.MediaType;
16 import org.springframework.http.ResponseEntity;
17 import org.springframework.http.client.ClientHttpResponse;
18 import org.springframework.security.crypto.codec.Base64;
19 import org.springframework.security.oauth2.client.test.BeforeOAuth2Context;
20 import org.springframework.security.oauth2.client.test.OAuth2ContextConfiguration;
21 import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider;
22 import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails;
23 import org.springframework.security.oauth2.common.AuthenticationScheme;
24 import org.springframework.security.oauth2.common.OAuth2AccessToken;
25 import org.springframework.util.LinkedMultiValueMap;
26 import org.springframework.web.client.DefaultResponseErrorHandler;
27 import org.springframework.web.client.HttpClientErrorException;
28 import org.springframework.web.client.ResponseErrorHandler;
29 import org.springframework.web.client.ResponseExtractor;
30
31
32
33
34
35 public abstract class AbstractResourceOwnerPasswordProviderTests extends AbstractIntegrationTests {
36
37 private ClientHttpResponse tokenEndpointResponse;
38
39 @BeforeOAuth2Context
40 public void setupAccessTokenProvider() {
41 ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider() {
42
43 private ResponseExtractor<OAuth2AccessToken> extractor = super.getResponseExtractor();
44
45 private ResponseErrorHandler errorHandler = super.getResponseErrorHandler();
46
47 @Override
48 protected ResponseErrorHandler getResponseErrorHandler() {
49 return new DefaultResponseErrorHandler() {
50 public void handleError(ClientHttpResponse response) throws IOException {
51 response.getHeaders();
52 response.getStatusCode();
53 tokenEndpointResponse = response;
54 errorHandler.handleError(response);
55 }
56 };
57 }
58
59 @Override
60 protected ResponseExtractor<OAuth2AccessToken> getResponseExtractor() {
61 return new ResponseExtractor<OAuth2AccessToken>() {
62
63 public OAuth2AccessToken extractData(ClientHttpResponse response) throws IOException {
64 response.getHeaders();
65 response.getStatusCode();
66 tokenEndpointResponse = response;
67 return extractor.extractData(response);
68 }
69
70 };
71 }
72 };
73 context.setAccessTokenProvider(accessTokenProvider);
74 }
75
76 @Test
77 public void testUnauthenticated() throws Exception {
78
79 assertEquals(HttpStatus.UNAUTHORIZED, http.getStatusCode("/admin/beans"));
80 }
81
82 @Test
83 public void testUnauthenticatedErrorMessage() throws Exception {
84 HttpHeaders headers = new HttpHeaders();
85 ResponseEntity<Void> response = http.getForResponse("/admin/beans", headers);
86 assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
87 String authenticate = response.getHeaders().getFirst("WWW-Authenticate");
88 assertTrue("Wrong header: " + authenticate, authenticate.contains("error=\"unauthorized\""));
89 }
90
91 @Test
92 public void testInvalidTokenErrorMessage() throws Exception {
93 HttpHeaders headers = new HttpHeaders();
94 headers.set("Authorization", "Bearer FOO");
95 ResponseEntity<Void> response = http.getForResponse("/admin/beans", headers);
96 assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
97 String authenticate = response.getHeaders().getFirst("WWW-Authenticate");
98 assertTrue("Wrong header: " + authenticate, authenticate.contains("error=\"invalid_token\""));
99 }
100
101 @Test
102 @OAuth2ContextConfiguration(ResourceOwner.class)
103 public void testTokenObtainedWithHeaderAuthentication() throws Exception {
104 assertEquals(HttpStatus.OK, http.getStatusCode("/admin/beans"));
105 int expiry = context.getAccessToken().getExpiresIn();
106 assertTrue("Expiry not overridden in config: " + expiry, expiry < 1000);
107 assertEquals(new MediaType("application", "json", Charset.forName("UTF-8")), tokenEndpointResponse.getHeaders()
108 .getContentType());
109 }
110
111 @Test
112 @OAuth2ContextConfiguration(ResourceOwnerQuery.class)
113 public void testTokenObtainedWithQueryAuthentication() throws Exception {
114 assertEquals(HttpStatus.OK, http.getStatusCode("/admin/beans"));
115 }
116
117 @Test
118 @OAuth2ContextConfiguration(resource = ResourceOwnerNoSecretProvided.class, initialize = false)
119 public void testTokenNotGrantedIfSecretNotProvided() throws Exception {
120 try {
121 context.getAccessToken();
122 }
123 catch (HttpClientErrorException e) {
124 assertEquals(HttpStatus.UNAUTHORIZED, e.getStatusCode());
125 List<String> values = tokenEndpointResponse.getHeaders().get("WWW-Authenticate");
126 assertEquals(1, values.size());
127 String header = values.get(0);
128 assertTrue("Wrong header " + header, header.contains("Basic realm=\"oauth2/client\""));
129 }
130 }
131
132 @Test
133 @OAuth2ContextConfiguration(ResourceOwnerSecretProvidedInForm.class)
134 public void testSecretProvidedInForm() throws Exception {
135 assertEquals(HttpStatus.OK, http.getStatusCode("/admin/beans"));
136 }
137
138 @Test
139 @OAuth2ContextConfiguration(ResourceOwnerSecretProvided.class)
140 public void testSecretProvidedInHeader() throws Exception {
141 assertEquals(HttpStatus.OK, http.getStatusCode("/admin/beans"));
142 }
143
144 @Test
145 @OAuth2ContextConfiguration(resource = NoSuchClient.class, initialize = false)
146 public void testNoSuchClient() throws Exception {
147
148
149 try {
150 context.getAccessToken();
151 }
152 catch (Exception e) {
153
154 }
155
156 assertEquals(HttpStatus.UNAUTHORIZED, tokenEndpointResponse.getStatusCode());
157
158 List<String> newCookies = tokenEndpointResponse.getHeaders().get("Set-Cookie");
159 if (newCookies != null && !newCookies.isEmpty()) {
160 fail("No cookies should be set. Found: " + newCookies.get(0) + ".");
161 }
162
163 }
164
165 @Test
166 @OAuth2ContextConfiguration(resource = InvalidGrantType.class, initialize = false)
167 public void testInvalidGrantType() throws Exception {
168
169
170 try {
171 context.getAccessToken();
172 }
173 catch (Exception e) {
174
175 }
176
177 assertEquals(HttpStatus.UNAUTHORIZED, tokenEndpointResponse.getStatusCode());
178
179 List<String> newCookies = tokenEndpointResponse.getHeaders().get("Set-Cookie");
180 if (newCookies != null && !newCookies.isEmpty()) {
181 fail("No cookies should be set. Found: " + newCookies.get(0) + ".");
182 }
183
184 }
185
186
187
188
189 @Test
190 public void testMissingGrantType() throws Exception {
191 HttpHeaders headers = new HttpHeaders();
192 headers.set("Authorization", String.format("Basic %s", new String(Base64.encode("my-trusted-client:".getBytes()))));
193 headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
194 ResponseEntity<String> response = http.postForString(tokenPath(), headers, new LinkedMultiValueMap<String, String>());
195 assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
196 assertTrue(response.getBody().contains("invalid_request"));
197 }
198
199 static class ResourceOwner extends ResourceOwnerPasswordResourceDetails {
200 public ResourceOwner(Object target) {
201 setClientId("my-trusted-client");
202 setScope(Arrays.asList("read"));
203 setId(getClientId());
204 setUsername("user");
205 setPassword("password");
206 }
207 }
208
209 static class ResourceOwnerQuery extends ResourceOwner {
210 public ResourceOwnerQuery(Object target) {
211 super(target);
212 setAuthenticationScheme(AuthenticationScheme.query);
213 }
214 }
215
216 static class ResourceOwnerNoSecretProvided extends ResourceOwner {
217 public ResourceOwnerNoSecretProvided(Object target) {
218 super(target);
219 setClientId("my-client-with-secret");
220 }
221 }
222
223 static class ResourceOwnerSecretProvided extends ResourceOwner {
224 public ResourceOwnerSecretProvided(Object target) {
225 super(target);
226 setClientId("my-client-with-secret");
227 setClientSecret("secret");
228 }
229 }
230
231 static class ResourceOwnerSecretProvidedInForm extends ResourceOwnerSecretProvided {
232 public ResourceOwnerSecretProvidedInForm(Object target) {
233 super(target);
234 setAuthenticationScheme(AuthenticationScheme.form);
235 }
236 }
237
238 static class InvalidGrantType extends ResourceOwner {
239 public InvalidGrantType(Object target) {
240 super(target);
241 setClientId("my-client-with-registered-redirect");
242 }
243 }
244
245 static class NoSuchClient extends ResourceOwner {
246 public NoSuchClient(Object target) {
247 super(target);
248 setClientId("no-such-client");
249 }
250 }
251
252 }