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

Last change on this file since 4656 was 4231, checked in by stoecker, 13 years ago

add signpost and metadata extractor code to repository directly

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