1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.amber.oauth2.common.utils;
23
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.io.Reader;
28 import java.io.UnsupportedEncodingException;
29 import java.lang.reflect.Constructor;
30 import java.lang.reflect.InvocationTargetException;
31 import java.net.URLDecoder;
32 import java.net.URLEncoder;
33 import java.util.Collection;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39 import java.util.StringTokenizer;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42
43 import javax.servlet.http.HttpServletRequest;
44
45 import org.apache.amber.oauth2.common.OAuth;
46 import org.apache.amber.oauth2.common.error.OAuthError;
47 import org.apache.amber.oauth2.common.exception.OAuthProblemException;
48 import org.apache.amber.oauth2.common.exception.OAuthSystemException;
49
50
51
52
53
54
55
56
57
58
59
60 public final class OAuthUtils {
61
62 private static final String ENCODING = "UTF-8";
63 private static final String PARAMETER_SEPARATOR = "&";
64 private static final String NAME_VALUE_SEPARATOR = "=";
65
66 public static final String AUTH_SCHEME = OAuth.OAUTH_HEADER_NAME;
67
68 private static final Pattern OAUTH_HEADER = Pattern.compile("\\s*(\\w*)\\s+(.*)");
69 private static final Pattern NVP = Pattern.compile("(\\S*)\\s*\\=\\s*\"([^\"]*)\"");
70
71 public static final String MULTIPART = "multipart/";
72
73 private static final String DEFAULT_CONTENT_CHARSET = ENCODING;
74
75
76
77
78
79
80
81
82
83
84 public static String format(
85 final Collection<? extends Map.Entry<String, Object>> parameters,
86 final String encoding) {
87 final StringBuilder result = new StringBuilder();
88 for (final Map.Entry<String, Object> parameter : parameters) {
89 String value = parameter.getValue() == null? null : String.valueOf(parameter.getValue());
90 if (!OAuthUtils.isEmpty(parameter.getKey())
91 && !OAuthUtils.isEmpty(value)) {
92 final String encodedName = encode(parameter.getKey(), encoding);
93 final String encodedValue = value != null ? encode(value, encoding) : "";
94 if (result.length() > 0) {
95 result.append(PARAMETER_SEPARATOR);
96 }
97 result.append(encodedName);
98 result.append(NAME_VALUE_SEPARATOR);
99 result.append(encodedValue);
100 }
101 }
102 return result.toString();
103 }
104
105 private static String encode(final String content, final String encoding) {
106 try {
107 return URLEncoder.encode(content,
108 encoding != null ? encoding : "UTF-8");
109 } catch (UnsupportedEncodingException problem) {
110 throw new IllegalArgumentException(problem);
111 }
112 }
113
114
115
116
117
118
119
120 public static String saveStreamAsString(InputStream is) throws IOException {
121 return toString(is, ENCODING);
122 }
123
124
125
126
127
128
129
130
131
132
133
134
135 public static String toString(
136 final InputStream is, final String defaultCharset) throws IOException {
137 if (is == null) {
138 throw new IllegalArgumentException("InputStream may not be null");
139 }
140
141 String charset = defaultCharset;
142 if (charset == null) {
143 charset = DEFAULT_CONTENT_CHARSET;
144 }
145 Reader reader = new InputStreamReader(is, charset);
146 StringBuilder sb = new StringBuilder();
147 int l;
148 try {
149 char[] tmp = new char[4096];
150 while ((l = reader.read(tmp)) != -1) {
151 sb.append(tmp, 0, l);
152 }
153 } finally {
154 reader.close();
155 }
156 return sb.toString();
157 }
158
159
160
161
162
163
164
165 public static OAuthProblemException handleOAuthProblemException(String message) {
166 return OAuthProblemException.error(OAuthError.TokenResponse.INVALID_REQUEST)
167 .description(message);
168 }
169
170
171
172
173
174
175
176
177 public static OAuthProblemException handleMissingParameters(Set<String> missingParams) {
178 StringBuffer sb = new StringBuffer("Missing parameters: ");
179 if (!OAuthUtils.isEmpty(missingParams)) {
180 for (String missingParam : missingParams) {
181 sb.append(missingParam).append(" ");
182 }
183 }
184 return handleOAuthProblemException(sb.toString().trim());
185 }
186
187 public static OAuthProblemException handleBadContentTypeException(String expectedContentType) {
188 StringBuilder errorMsg = new StringBuilder("Bad request content type. Expecting: ").append(
189 expectedContentType);
190 return handleOAuthProblemException(errorMsg.toString());
191 }
192
193 public static OAuthProblemException handleNotAllowedParametersOAuthException(
194 List<String> notAllowedParams) {
195 StringBuffer sb = new StringBuffer("Not allowed parameters: ");
196 if (notAllowedParams != null) {
197 for (String notAllowed : notAllowedParams) {
198 sb.append(notAllowed).append(" ");
199 }
200 }
201 return handleOAuthProblemException(sb.toString().trim());
202 }
203
204
205
206
207 public static Map<String, Object> decodeForm(String form) {
208 Map<String, Object> params = new HashMap<String, Object>();
209 if (!OAuthUtils.isEmpty(form)) {
210 for (String nvp : form.split("\\&")) {
211 int equals = nvp.indexOf('=');
212 String name;
213 String value;
214 if (equals < 0) {
215 name = decodePercent(nvp);
216 value = null;
217 } else {
218 name = decodePercent(nvp.substring(0, equals));
219 value = decodePercent(nvp.substring(equals + 1));
220 }
221 params.put(name, value);
222 }
223 }
224 return params;
225 }
226
227
228
229
230 public static boolean isFormEncoded(String contentType) {
231 if (contentType == null) {
232 return false;
233 }
234 int semi = contentType.indexOf(";");
235 if (semi >= 0) {
236 contentType = contentType.substring(0, semi);
237 }
238 return OAuth.ContentType.URL_ENCODED.equalsIgnoreCase(contentType.trim());
239 }
240
241 public static String decodePercent(String s) {
242 try {
243 return URLDecoder.decode(s, ENCODING);
244
245 } catch (java.io.UnsupportedEncodingException wow) {
246 throw new RuntimeException(wow.getMessage(), wow);
247 }
248 }
249
250
251
252
253 public static String percentEncode(Iterable values) {
254 StringBuilder p = new StringBuilder();
255 for (Object v : values) {
256 String stringValue = toString(v);
257 if (!isEmpty(stringValue)) {
258 if (p.length() > 0) {
259 p.append("&");
260 }
261 p.append(OAuthUtils.percentEncode(toString(v)));
262 }
263 }
264 return p.toString();
265 }
266
267 public static String percentEncode(String s) {
268 if (s == null) {
269 return "";
270 }
271 try {
272 return URLEncoder.encode(s, ENCODING)
273
274 .replace("+", "%20").replace("*", "%2A")
275 .replace("%7E", "~");
276
277 } catch (UnsupportedEncodingException wow) {
278 throw new RuntimeException(wow.getMessage(), wow);
279 }
280 }
281
282 private static final String toString(Object from) {
283 return (from == null) ? null : from.toString();
284 }
285
286 private static boolean isEmpty(Set<String> missingParams) {
287 if (missingParams == null || missingParams.size() == 0) {
288 return true;
289 }
290 return false;
291 }
292
293 public static <T> T instantiateClass(Class<T> clazz) throws OAuthSystemException {
294 try {
295 return (T)clazz.newInstance();
296 } catch (Exception e) {
297 throw new OAuthSystemException(e);
298 }
299 }
300
301 public static Object instantiateClassWithParameters(Class clazz, Class[] paramsTypes,
302 Object[] paramValues) throws OAuthSystemException {
303
304 try {
305 if (paramsTypes != null && paramValues != null) {
306 if (!(paramsTypes.length == paramValues.length)) {
307 throw new IllegalArgumentException("Number of types and values must be equal");
308 }
309
310 if (paramsTypes.length == 0 && paramValues.length == 0) {
311 return clazz.newInstance();
312 }
313 Constructor clazzConstructor = clazz.getConstructor(paramsTypes);
314 return clazzConstructor.newInstance(paramValues);
315 }
316 return clazz.newInstance();
317
318 } catch (NoSuchMethodException e) {
319 throw new OAuthSystemException(e);
320 } catch (InstantiationException e) {
321 throw new OAuthSystemException(e);
322 } catch (IllegalAccessException e) {
323 throw new OAuthSystemException(e);
324 } catch (InvocationTargetException e) {
325 throw new OAuthSystemException(e);
326 }
327
328 }
329
330
331 public static String getAuthHeaderField(String authHeader) {
332
333 if (authHeader != null) {
334 Matcher m = OAUTH_HEADER.matcher(authHeader);
335 if (m.matches()) {
336 if (AUTH_SCHEME.equalsIgnoreCase(m.group(1))) {
337 return m.group(2);
338 }
339 }
340 }
341 return null;
342 }
343
344 public static Map<String, String> decodeOAuthHeader(String header) {
345 Map<String, String> headerValues = new HashMap<String, String>();
346 if (header != null) {
347 Matcher m = OAUTH_HEADER.matcher(header);
348 if (m.matches()) {
349 if (AUTH_SCHEME.equalsIgnoreCase(m.group(1))) {
350 for (String nvp : m.group(2).split("\\s*,\\s*")) {
351 m = NVP.matcher(nvp);
352 if (m.matches()) {
353 String name = decodePercent(m.group(1));
354 String value = decodePercent(m.group(2));
355 headerValues.put(name, value);
356 }
357 }
358 }
359 }
360 }
361 return headerValues;
362 }
363
364
365
366
367
368
369 public static String encodeOAuthHeader(Map<String, Object> entries) {
370 StringBuffer sb = new StringBuffer();
371 sb.append(OAuth.OAUTH_HEADER_NAME).append(" ");
372 for (Map.Entry<String, Object> entry : entries.entrySet()) {
373 String value = entry.getValue() == null? null: String.valueOf(entry.getValue());
374 if (!OAuthUtils.isEmpty(entry.getKey()) && !OAuthUtils.isEmpty(value)) {
375 sb.append(entry.getKey());
376 sb.append("=\"");
377 sb.append(value);
378 sb.append("\",");
379 }
380 }
381
382 return sb.substring(0, sb.length() - 1);
383 }
384
385
386
387
388 public static String encodeAuthorizationBearerHeader(Map<String, Object> entries) {
389 StringBuffer sb = new StringBuffer();
390 sb.append(OAuth.OAUTH_HEADER_NAME).append(" ");
391 for (Map.Entry<String, Object> entry : entries.entrySet()) {
392 String value = entry.getValue() == null? null: String.valueOf(entry.getValue());
393 if (!OAuthUtils.isEmpty(entry.getKey()) && !OAuthUtils.isEmpty(value)) {
394 sb.append(value);
395 }
396 }
397
398 return sb.toString();
399 }
400
401 public static boolean isEmpty(String value) {
402 return value == null || "".equals(value);
403 }
404
405 public static boolean hasEmptyValues(String[] array) {
406 if (array == null || array.length == 0) {
407 return true;
408 }
409 for (String s : array) {
410 if (isEmpty(s)) {
411 return true;
412 }
413 }
414 return false;
415 }
416
417 public static String getAuthzMethod(String header) {
418 if (header != null) {
419 Matcher m = OAUTH_HEADER.matcher(header);
420 if (m.matches()) {
421 return m.group(1);
422
423 }
424 }
425 return null;
426 }
427
428 public static Set<String> decodeScopes(String s) {
429 Set<String> scopes = new HashSet<String>();
430 if (!OAuthUtils.isEmpty(s)) {
431 StringTokenizer tokenizer = new StringTokenizer(s, " ");
432
433 while (tokenizer.hasMoreElements()) {
434 scopes.add(tokenizer.nextToken());
435 }
436 }
437 return scopes;
438
439 }
440
441 public static String encodeScopes(Set<String> s) {
442 StringBuffer scopes = new StringBuffer();
443 for (String scope : s) {
444 scopes.append(scope).append(" ");
445 }
446 return scopes.toString().trim();
447
448 }
449
450 public static boolean isMultipart(HttpServletRequest request) {
451
452 if (!"post".equals(request.getMethod().toLowerCase())) {
453 return false;
454 }
455 String contentType = request.getContentType();
456 if (contentType == null) {
457 return false;
458 }
459 if (contentType.toLowerCase().startsWith(MULTIPART)) {
460 return true;
461 }
462 return false;
463 }
464
465
466 public static boolean hasContentType(String requestContentType, String requiredContentType) {
467 if (OAuthUtils.isEmpty(requiredContentType) || OAuthUtils.isEmpty(requestContentType)) {
468 return false;
469 }
470 StringTokenizer tokenizer = new StringTokenizer(requestContentType, ";");
471 while (tokenizer.hasMoreTokens()) {
472 if (requiredContentType.equals(tokenizer.nextToken())) {
473 return true;
474 }
475 }
476
477 return false;
478 }
479
480 }
481
482