View Javadoc

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   * @author Ryan Heaton
33   * @author Dave Syer
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  		// first make sure the resource is actually protected.
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 		// The error comes back as additional information because OAuth2AccessToken is so extensible!
149 		try {
150 			context.getAccessToken();
151 		}
152 		catch (Exception e) {
153 			// assertEquals("invalid_client", e.getOAuth2ErrorCode());
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 		// The error comes back as additional information because OAuth2AccessToken is so extensible!
170 		try {
171 			context.getAccessToken();
172 		}
173 		catch (Exception e) {
174 			// assertEquals("invalid_client", e.getOAuth2ErrorCode());
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 	 * tests that we get the correct error response if the media type is unacceptable.
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 }