• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2007 Netflix, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package net.oauth;
18 
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.io.UnsupportedEncodingException;
23 import java.net.URLDecoder;
24 import java.net.URLEncoder;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 
30 /**
31  * @author John Kristian
32  * @hide
33  */
34 public class OAuth {
35 
36     public static final String VERSION_1_0 = "1.0";
37 
38     /** The encoding used to represent characters as bytes. */
39     public static final String ENCODING = "UTF-8";
40 
41     /** The MIME type for a sequence of OAuth parameters. */
42     public static final String FORM_ENCODED = "application/x-www-form-urlencoded";
43 
44     public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key";
45     public static final String OAUTH_TOKEN = "oauth_token";
46     public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret";
47     public static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
48     public static final String OAUTH_SIGNATURE = "oauth_signature";
49     public static final String OAUTH_TIMESTAMP = "oauth_timestamp";
50     public static final String OAUTH_NONCE = "oauth_nonce";
51     public static final String OAUTH_VERSION = "oauth_version";
52 
53     public static final String HMAC_SHA1 = "HMAC-SHA1";
54     public static final String RSA_SHA1 = "RSA-SHA1";
55 
56     public static class Problems {
57         public static final String TOKEN_NOT_AUTHORIZED = "token_not_authorized";
58         public static final String INVALID_USED_NONCE = "invalid_used_nonce";
59         public static final String SIGNATURE_INVALID = "signature_invalid";
60         public static final String INVALID_EXPIRED_TOKEN = "invalid_expired_token";
61         public static final String INVALID_CONSUMER_KEY = "invalid_consumer_key";
62         public static final String CONSUMER_KEY_REFUSED = "consumer_key_refused";
63         public static final String TIMESTAMP_REFUSED = "timestamp_refused";
64         public static final String PARAMETER_REJECTED = "parameter_rejected";
65         public static final String PARAMETER_ABSENT = "parameter_absent";
66         public static final String VERSION_REJECTED = "version_rejected";
67         public static final String SIGNATURE_METHOD_REJECTED = "signature_method_rejected";
68 
69         public static final String OAUTH_PARAMETERS_ABSENT = "oauth_parameters_absent";
70         public static final String OAUTH_PARAMETERS_REJECTED = "oauth_parameters_rejected";
71         public static final String OAUTH_ACCEPTABLE_TIMESTAMPS = "oauth_acceptable_timestamps";
72         public static final String OAUTH_ACCEPTABLE_VERSIONS = "oauth_acceptable_versions";
73     }
74 
75     /** Return true if the given Content-Type header means FORM_ENCODED. */
isFormEncoded(String contentType)76     public static boolean isFormEncoded(String contentType) {
77         if (contentType == null) {
78             return false;
79         }
80         int semi = contentType.indexOf(";");
81         if (semi >= 0) {
82             contentType = contentType.substring(0, semi);
83         }
84         return FORM_ENCODED.equalsIgnoreCase(contentType.trim());
85     }
86 
87     /**
88      * Construct a form-urlencoded document containing the given sequence of
89      * name/value pairs. Use OAuth percent encoding (not exactly the encoding
90      * mandated by HTTP).
91      */
formEncode(Iterable<? extends Map.Entry> parameters)92     public static String formEncode(Iterable<? extends Map.Entry> parameters)
93             throws IOException {
94         ByteArrayOutputStream b = new ByteArrayOutputStream();
95         formEncode(parameters, b);
96         return new String(b.toByteArray());
97     }
98 
99     /**
100      * Write a form-urlencoded document into the given stream, containing the
101      * given sequence of name/value pairs.
102      */
formEncode(Iterable<? extends Map.Entry> parameters, OutputStream into)103     public static void formEncode(Iterable<? extends Map.Entry> parameters,
104             OutputStream into) throws IOException {
105         if (parameters != null) {
106             boolean first = true;
107             for (Map.Entry parameter : parameters) {
108                 if (first) {
109                     first = false;
110                 } else {
111                     into.write('&');
112                 }
113                 into.write(percentEncode(toString(parameter.getKey()))
114                         .getBytes());
115                 into.write('=');
116                 into.write(percentEncode(toString(parameter.getValue()))
117                         .getBytes());
118             }
119         }
120     }
121 
122     /** Parse a form-urlencoded document. */
decodeForm(String form)123     public static List<Parameter> decodeForm(String form) {
124         List<Parameter> list = new ArrayList<Parameter>();
125         if (!isEmpty(form)) {
126             for (String nvp : form.split("\\&")) {
127                 int equals = nvp.indexOf('=');
128                 String name;
129                 String value;
130                 if (equals < 0) {
131                     name = decodePercent(nvp);
132                     value = null;
133                 } else {
134                     name = decodePercent(nvp.substring(0, equals));
135                     value = decodePercent(nvp.substring(equals + 1));
136                 }
137                 list.add(new Parameter(name, value));
138             }
139         }
140         return list;
141     }
142 
143     /** Construct a &-separated list of the given values, percentEncoded. */
percentEncode(Iterable values)144     public static String percentEncode(Iterable values) {
145         StringBuilder p = new StringBuilder();
146         for (Object v : values) {
147             if (p.length() > 0) {
148                 p.append("&");
149             }
150             p.append(OAuth.percentEncode(toString(v)));
151         }
152         return p.toString();
153     }
154 
percentEncode(String s)155     public static String percentEncode(String s) {
156         if (s == null) {
157             return "";
158         }
159         try {
160             return URLEncoder.encode(s, ENCODING)
161                     // OAuth encodes some characters differently:
162                     .replace("+", "%20").replace("*", "%2A")
163                     .replace("%7E", "~");
164             // This could be done faster with more hand-crafted code.
165         } catch (UnsupportedEncodingException wow) {
166             throw new RuntimeException(wow.getMessage(), wow);
167         }
168     }
169 
decodePercent(String s)170     public static String decodePercent(String s) {
171         try {
172             return URLDecoder.decode(s, ENCODING);
173             // This implements http://oauth.pbwiki.com/FlexibleDecoding
174         } catch (java.io.UnsupportedEncodingException wow) {
175             throw new RuntimeException(wow.getMessage(), wow);
176         }
177     }
178 
179     /**
180      * Construct a Map containing a copy of the given parameters. If several
181      * parameters have the same name, the Map will contain the first value,
182      * only.
183      */
newMap(Iterable<? extends Map.Entry> from)184     public static Map<String, String> newMap(Iterable<? extends Map.Entry> from) {
185         Map<String, String> map = new HashMap<String, String>();
186         if (from != null) {
187             for (Map.Entry f : from) {
188                 String key = toString(f.getKey());
189                 if (!map.containsKey(key)) {
190                     map.put(key, toString(f.getValue()));
191                 }
192             }
193         }
194         return map;
195     }
196 
197     /** Construct a list of Parameters from name, value, name, value... */
newList(String... parameters)198     public static List<Parameter> newList(String... parameters) {
199         List<Parameter> list = new ArrayList<Parameter>(parameters.length / 2);
200         for (int p = 0; p + 1 < parameters.length; p += 2) {
201             list.add(new Parameter(parameters[p], parameters[p + 1]));
202         }
203         return list;
204     }
205 
206     /** A name/value pair. */
207     public static class Parameter implements Map.Entry<String, String> {
208 
Parameter(String key, String value)209         public Parameter(String key, String value) {
210             this.key = key;
211             this.value = value;
212         }
213 
214         private final String key;
215 
216         private String value;
217 
getKey()218         public String getKey() {
219             return key;
220         }
221 
getValue()222         public String getValue() {
223             return value;
224         }
225 
setValue(String value)226         public String setValue(String value) {
227             try {
228                 return this.value;
229             } finally {
230                 this.value = value;
231             }
232         }
233 
234         @Override
toString()235         public String toString() {
236             return percentEncode(getKey()) + '=' + percentEncode(getValue());
237         }
238 
239         @Override
hashCode()240         public int hashCode()
241         {
242             final int prime = 31;
243             int result = 1;
244             result = prime * result + ((key == null) ? 0 : key.hashCode());
245             result = prime * result + ((value == null) ? 0 : value.hashCode());
246             return result;
247         }
248 
249         @Override
equals(Object obj)250         public boolean equals(Object obj)
251         {
252             if (this == obj)
253                 return true;
254             if (obj == null)
255                 return false;
256             if (getClass() != obj.getClass())
257                 return false;
258             final Parameter that = (Parameter) obj;
259             if (key == null) {
260                 if (that.key != null)
261                     return false;
262             } else if (!key.equals(that.key))
263                 return false;
264             if (value == null) {
265                 if (that.value != null)
266                     return false;
267             } else if (!value.equals(that.value))
268                 return false;
269             return true;
270         }
271     }
272 
toString(Object from)273     private static final String toString(Object from) {
274         return (from == null) ? null : from.toString();
275     }
276 
277     /**
278      * Construct a URL like the given one, but with the given parameters added
279      * to its query string.
280      */
addParameters(String url, String... parameters)281     public static String addParameters(String url, String... parameters)
282             throws IOException {
283         return addParameters(url, newList(parameters));
284     }
285 
addParameters(String url, Iterable<? extends Map.Entry<String, String>> parameters)286     public static String addParameters(String url,
287             Iterable<? extends Map.Entry<String, String>> parameters)
288             throws IOException {
289         String form = formEncode(parameters);
290         if (form == null || form.length() <= 0) {
291             return url;
292         } else {
293             return url + ((url.indexOf("?") < 0) ? '?' : '&') + form;
294         }
295     }
296 
isEmpty(String str)297     public static boolean isEmpty(String str) {
298 	return (str == null) || (str.length() == 0);
299     }
300 }
301