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

Last change on this file since 7448 was 6849, checked in by stoecker, 10 years ago

see #9710 - update oauth library code

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