source: josm/trunk/src/oauth/signpost/AbstractOAuthProvider.java@ 16334

Last change on this file since 16334 was 10831, checked in by Don-vip, 8 years ago

see #13232 - cleanup OAuth signpost code:

  • remove classes unused by JOSM
  • add missing @Override annotations
File size: 13.0 KB
RevLine 
[4231]1/*
2 * Copyright (c) 2009 Matthias Kaeppler Licensed under the Apache License,
3 * Version 2.0 (the "License"); you may not use this file except in compliance
4 * with the License. You may obtain a copy of the License at
5 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
6 * or agreed to in writing, software distributed under the License is
7 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
8 * KIND, either express or implied. See the License for the specific language
9 * governing permissions and limitations under the License.
10 */
11package oauth.signpost;
12
13import java.io.BufferedReader;
[9227]14import java.io.InputStream;
[4231]15import java.io.InputStreamReader;
16import java.util.HashMap;
17import java.util.Map;
18
19import oauth.signpost.exception.OAuthCommunicationException;
20import oauth.signpost.exception.OAuthExpectationFailedException;
21import oauth.signpost.exception.OAuthMessageSignerException;
22import oauth.signpost.exception.OAuthNotAuthorizedException;
23import oauth.signpost.http.HttpParameters;
24import oauth.signpost.http.HttpRequest;
25import oauth.signpost.http.HttpResponse;
26
27/**
28 * ABC for all provider implementations. If you're writing a custom provider,
29 * you will probably inherit from this class, since it takes a lot of work from
30 * you.
[9227]31 *
[4231]32 * @author Matthias Kaeppler
33 */
34public abstract class AbstractOAuthProvider implements OAuthProvider {
35
36 private static final long serialVersionUID = 1L;
37
38 private String requestTokenEndpointUrl;
39
40 private String accessTokenEndpointUrl;
41
42 private String authorizationWebsiteUrl;
43
44 private HttpParameters responseParameters;
45
46 private Map<String, String> defaultHeaders;
47
48 private boolean isOAuth10a;
49
50 private transient OAuthProviderListener listener;
51
52 public AbstractOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl,
53 String authorizationWebsiteUrl) {
54 this.requestTokenEndpointUrl = requestTokenEndpointUrl;
55 this.accessTokenEndpointUrl = accessTokenEndpointUrl;
56 this.authorizationWebsiteUrl = authorizationWebsiteUrl;
57 this.responseParameters = new HttpParameters();
58 this.defaultHeaders = new HashMap<String, String>();
59 }
60
[10831]61 @Override
[6849]62 public synchronized String retrieveRequestToken(OAuthConsumer consumer, String callbackUrl,
63 String... customOAuthParams) throws OAuthMessageSignerException,
64 OAuthNotAuthorizedException, OAuthExpectationFailedException,
65 OAuthCommunicationException {
[4231]66
67 // invalidate current credentials, if any
68 consumer.setTokenWithSecret(null, null);
69
70 // 1.0a expects the callback to be sent while getting the request token.
71 // 1.0 service providers would simply ignore this parameter.
[6849]72 HttpParameters params = new HttpParameters();
73 params.putAll(customOAuthParams, true);
74 params.put(OAuth.OAUTH_CALLBACK, callbackUrl, true);
[4231]75
[6849]76 retrieveToken(consumer, requestTokenEndpointUrl, params);
77
[4231]78 String callbackConfirmed = responseParameters.getFirst(OAuth.OAUTH_CALLBACK_CONFIRMED);
79 responseParameters.remove(OAuth.OAUTH_CALLBACK_CONFIRMED);
80 isOAuth10a = Boolean.TRUE.toString().equals(callbackConfirmed);
81
82 // 1.0 service providers expect the callback as part of the auth URL,
83 // Do not send when 1.0a.
84 if (isOAuth10a) {
85 return OAuth.addQueryParameters(authorizationWebsiteUrl, OAuth.OAUTH_TOKEN,
86 consumer.getToken());
87 } else {
88 return OAuth.addQueryParameters(authorizationWebsiteUrl, OAuth.OAUTH_TOKEN,
89 consumer.getToken(), OAuth.OAUTH_CALLBACK, callbackUrl);
90 }
91 }
92
[10831]93 @Override
[6849]94 public synchronized void retrieveAccessToken(OAuthConsumer consumer, String oauthVerifier,
95 String... customOAuthParams) throws OAuthMessageSignerException,
96 OAuthNotAuthorizedException, OAuthExpectationFailedException,
97 OAuthCommunicationException {
[4231]98
99 if (consumer.getToken() == null || consumer.getTokenSecret() == null) {
100 throw new OAuthExpectationFailedException(
101 "Authorized request token or token secret not set. "
102 + "Did you retrieve an authorized request token before?");
103 }
104
[6849]105 HttpParameters params = new HttpParameters();
106 params.putAll(customOAuthParams, true);
107
[4231]108 if (isOAuth10a && oauthVerifier != null) {
[6849]109 params.put(OAuth.OAUTH_VERIFIER, oauthVerifier, true);
[4231]110 }
[6849]111 retrieveToken(consumer, accessTokenEndpointUrl, params);
[4231]112 }
113
114 /**
115 * <p>
116 * Implemented by subclasses. The responsibility of this method is to
117 * contact the service provider at the given endpoint URL and fetch a
118 * request or access token. What kind of token is retrieved solely depends
119 * on the URL being used.
120 * </p>
121 * <p>
122 * Correct implementations of this method must guarantee the following
123 * post-conditions:
124 * <ul>
125 * <li>the {@link OAuthConsumer} passed to this method must have a valid
126 * {@link OAuth#OAUTH_TOKEN} and {@link OAuth#OAUTH_TOKEN_SECRET} set by
127 * calling {@link OAuthConsumer#setTokenWithSecret(String, String)}</li>
128 * <li>{@link #getResponseParameters()} must return the set of query
129 * parameters served by the service provider in the token response, with all
130 * OAuth specific parameters being removed</li>
131 * </ul>
132 * </p>
[9227]133 *
[4231]134 * @param consumer
135 * the {@link OAuthConsumer} that should be used to sign the request
136 * @param endpointUrl
137 * the URL at which the service provider serves the OAuth token that
138 * is to be fetched
[6849]139 * @param customOAuthParams
140 * you can pass custom OAuth parameters here (such as oauth_callback
141 * or oauth_verifier) which will go directly into the signer, i.e.
142 * you don't have to put them into the request first.
[4231]143 * @throws OAuthMessageSignerException
144 * if signing the token request fails
145 * @throws OAuthCommunicationException
146 * if a network communication error occurs
147 * @throws OAuthNotAuthorizedException
148 * if the server replies 401 - Unauthorized
149 * @throws OAuthExpectationFailedException
150 * if an expectation has failed, e.g. because the server didn't
151 * reply in the expected format
152 */
153 protected void retrieveToken(OAuthConsumer consumer, String endpointUrl,
[6849]154 HttpParameters customOAuthParams) throws OAuthMessageSignerException,
[4231]155 OAuthCommunicationException, OAuthNotAuthorizedException,
156 OAuthExpectationFailedException {
157 Map<String, String> defaultHeaders = getRequestHeaders();
158
159 if (consumer.getConsumerKey() == null || consumer.getConsumerSecret() == null) {
160 throw new OAuthExpectationFailedException("Consumer key or secret not set");
161 }
162
163 HttpRequest request = null;
164 HttpResponse response = null;
165 try {
166 request = createRequest(endpointUrl);
167 for (String header : defaultHeaders.keySet()) {
168 request.setHeader(header, defaultHeaders.get(header));
169 }
[6849]170 if (customOAuthParams != null && !customOAuthParams.isEmpty()) {
171 consumer.setAdditionalParameters(customOAuthParams);
[4231]172 }
[9227]173
[4231]174 if (this.listener != null) {
175 this.listener.prepareRequest(request);
176 }
177
178 consumer.sign(request);
[9227]179
[4231]180 if (this.listener != null) {
181 this.listener.prepareSubmission(request);
182 }
183
184 response = sendRequest(request);
185 int statusCode = response.getStatusCode();
186
187 boolean requestHandled = false;
188 if (this.listener != null) {
189 requestHandled = this.listener.onResponseReceived(request, response);
190 }
191 if (requestHandled) {
192 return;
193 }
194
195 if (statusCode >= 300) {
196 handleUnexpectedResponse(statusCode, response);
197 }
198
199 HttpParameters responseParams = OAuth.decodeForm(response.getContent());
200
201 String token = responseParams.getFirst(OAuth.OAUTH_TOKEN);
202 String secret = responseParams.getFirst(OAuth.OAUTH_TOKEN_SECRET);
203 responseParams.remove(OAuth.OAUTH_TOKEN);
204 responseParams.remove(OAuth.OAUTH_TOKEN_SECRET);
205
206 setResponseParameters(responseParams);
207
208 if (token == null || secret == null) {
209 throw new OAuthExpectationFailedException(
210 "Request token or token secret not set in server reply. "
211 + "The service provider you use is probably buggy.");
212 }
213
214 consumer.setTokenWithSecret(token, secret);
215
216 } catch (OAuthNotAuthorizedException e) {
217 throw e;
218 } catch (OAuthExpectationFailedException e) {
219 throw e;
220 } catch (Exception e) {
221 throw new OAuthCommunicationException(e);
222 } finally {
223 try {
224 closeConnection(request, response);
225 } catch (Exception e) {
226 throw new OAuthCommunicationException(e);
227 }
228 }
229 }
230
231 protected void handleUnexpectedResponse(int statusCode, HttpResponse response) throws Exception {
232 if (response == null) {
233 return;
234 }
235 StringBuilder responseBody = new StringBuilder();
[9227]236 InputStream content = response.getContent();
237 if (content != null) {
238 BufferedReader reader = new BufferedReader(new InputStreamReader(content));
[4231]239
[9227]240 String line = reader.readLine();
241 while (line != null) {
242 responseBody.append(line);
243 line = reader.readLine();
244 }
[4231]245 }
246
247 switch (statusCode) {
248 case 401:
249 throw new OAuthNotAuthorizedException(responseBody.toString());
250 default:
251 throw new OAuthCommunicationException("Service provider responded in error: "
252 + statusCode + " (" + response.getReasonPhrase() + ")", responseBody.toString());
253 }
254 }
255
256 /**
257 * Overrride this method if you want to customize the logic for building a
258 * request object for the given endpoint URL.
[9227]259 *
[4231]260 * @param endpointUrl
261 * the URL to which the request will go
262 * @return the request object
263 * @throws Exception
264 * if something breaks
265 */
266 protected abstract HttpRequest createRequest(String endpointUrl) throws Exception;
267
268 /**
269 * Override this method if you want to customize the logic for how the given
270 * request is sent to the server.
[9227]271 *
[4231]272 * @param request
273 * the request to send
274 * @return the response to the request
275 * @throws Exception
276 * if something breaks
277 */
278 protected abstract HttpResponse sendRequest(HttpRequest request) throws Exception;
279
280 /**
281 * Called when the connection is being finalized after receiving the
282 * response. Use this to do any cleanup / resource freeing.
[9227]283 *
[4231]284 * @param request
285 * the request that has been sent
286 * @param response
287 * the response that has been received
288 * @throws Exception
289 * if something breaks
290 */
291 protected void closeConnection(HttpRequest request, HttpResponse response) throws Exception {
292 // NOP
293 }
294
[10831]295 @Override
[4231]296 public HttpParameters getResponseParameters() {
297 return responseParameters;
298 }
299
300 /**
301 * Returns a single query parameter as served by the service provider in a
302 * token reply. You must call {@link #setResponseParameters} with the set of
303 * parameters before using this method.
[9227]304 *
[4231]305 * @param key
306 * the parameter name
307 * @return the parameter value
308 */
309 protected String getResponseParameter(String key) {
310 return responseParameters.getFirst(key);
311 }
312
[10831]313 @Override
[4231]314 public void setResponseParameters(HttpParameters parameters) {
315 this.responseParameters = parameters;
316 }
317
[10831]318 @Override
[4231]319 public void setOAuth10a(boolean isOAuth10aProvider) {
320 this.isOAuth10a = isOAuth10aProvider;
321 }
322
[10831]323 @Override
[4231]324 public boolean isOAuth10a() {
325 return isOAuth10a;
326 }
327
[10831]328 @Override
[4231]329 public String getRequestTokenEndpointUrl() {
330 return this.requestTokenEndpointUrl;
331 }
332
[10831]333 @Override
[4231]334 public String getAccessTokenEndpointUrl() {
335 return this.accessTokenEndpointUrl;
336 }
337
[10831]338 @Override
[4231]339 public String getAuthorizationWebsiteUrl() {
340 return this.authorizationWebsiteUrl;
341 }
342
[10831]343 @Override
[4231]344 public void setRequestHeader(String header, String value) {
345 defaultHeaders.put(header, value);
346 }
347
[10831]348 @Override
[4231]349 public Map<String, String> getRequestHeaders() {
350 return defaultHeaders;
351 }
352
[10831]353 @Override
[4231]354 public void setListener(OAuthProviderListener listener) {
355 this.listener = listener;
356 }
357
[10831]358 @Override
[4231]359 public void removeListener(OAuthProviderListener listener) {
360 this.listener = null;
361 }
362}
Note: See TracBrowser for help on using the repository browser.