38 Commits
Author SHA1 Message Date
Pablo Fernandez c265e730a3 removed auto-generated comments 2011-04-19 22:52:36 -03:00
Matthew Laudato 5719d7a18a Support for ConstantContact REST API 2011-04-19 22:42:25 -03:00
Pablo Fernandez 8abdde928a added convenience methods for the builder api 2011-04-19 22:32:32 -03:00
Pablo Fernandez 193dcd2e25 added SignatureType and querystring signing functionallity 2011-04-19 22:17:23 -03:00
Pablo Fernandez 931236561a rename method
to support both querystring and header signatures
2011-04-19 22:04:06 -03:00
Pablo Fernandez 5f40e0dca0 removed unused extractor (JsonTokenExtractor handles this) 2011-04-19 21:51:36 -03:00
Tom Tasche 8afb142b7f Add Foursquare2 API and example 2011-04-19 21:50:12 -03:00
Pablo Fernandez bfcf0655da added Dropbox Api 2011-04-13 19:03:56 -03:00
Pablo Fernandez db88d53807 made extractor independent of token/secret order 2011-04-13 19:02:45 -03:00
Pablo Fernandez cbfd88d749 removed picture from readme since link is broken 2011-04-11 14:06:39 -03:00
Pablo Fernandez 1eb0b1003c Added raw string to Token, for retrieving additional parameters 2011-04-11 14:06:08 -03:00
Stuart Grimshaw 75bcb0013d Added Lovefilm Api and example 2011-03-30 00:37:44 -03:00
Stuart Grimshaw 145c009fc3 Fix for HttpUrlConnection on pre-Gingerbread Android devices 2011-03-30 00:24:12 -03:00
Pablo Fernandez 72901165f8 updated readme with new pom version 2011-03-30 00:09:46 -03:00
Stuart Grimshaw 4f23cfaf20 Moved callback header above call to addOAthParams so it gets properly
signed.
2011-03-29 19:06:19 -03:00
Pablo Fernandez 5c0d150547 Added mvn-bundle script 2011-03-13 10:49:31 -03:00
Pablo Fernandez 6bdae4999e Fixed wrong method call 2011-03-13 10:44:31 -03:00
Pablo Fernandez c441b7d8a4 Merged Diego's changes for OAuth2.0 server-side flow 2011-03-13 10:40:09 -03:00
Pablo Fernandez d8ab4ba404 oauth_callback param should only be sent on the request_token request 2011-03-13 10:25:52 -03:00
Pablo Fernandez 6923d3311f Added facebook and 2.0 info on readme 2011-03-13 10:21:43 -03:00
Pablo Fernandez 0832613b60 Changed artifact version in README 2011-03-13 10:21:43 -03:00
Pablo Fernandez 73a53b363a Fixed RequestTests 2011-03-13 10:21:43 -03:00
Pablo Fernandez 16b3475926 Added uitilty method to MapUtils for appending and encoding parameters 2011-03-13 10:21:42 -03:00
Pablo Fernandez 22367de553 Changed getQuerystringParameters impl to form-urldecode the parameters 2011-03-13 10:21:42 -03:00
Pablo Fernandez 906a979e63 Removed encoding in Verifier constructor 2011-03-13 10:21:42 -03:00
Pablo Fernandez bb872e4466 Changed percentEncodings 2011-03-13 10:21:42 -03:00
Pablo Fernandez d19c5d6e35 Removed Verifier test (unused) 2011-03-13 10:21:42 -03:00
Pablo Fernandez e0e8df98d3 Added formURLEncode/Decode methods 2011-03-13 10:21:39 -03:00
diegossilveira de8f0d621d Changed the Facebook example to conform to the OAuth 2.0 server-side flow 2011-02-10 22:09:18 -02:00
diegossilveira db37744fa3 Fixed package imports 2011-02-10 21:54:46 -02:00
diegossilveira 02f3d4dac2 Changed the Facebook example to conform to the OAuth 2.0 server-side flow 2011-02-10 17:13:03 -02:00
diegossilveira 7385d3702e Added specific token extractor for OAuth 2.0 2011-02-10 17:09:11 -02:00
diegossilveira 581e2b6d84 Changed the getAccessToken() method implementation to conform to the OAuth 2.0 server-side flow 2011-02-10 17:03:28 -02:00
diegossilveira 08aebb5fb1 Added specific constants for OAuth 2.0 2011-02-10 16:56:10 -02:00
diegossilveira c7176c97e2 Added access_token related methods to OAuth 2.0 APIs 2011-02-10 16:54:28 -02:00
Pablo Fernandez 70358ad651 Added specific tests for + symbol encoding 2011-01-31 16:15:18 -03:00
dustin norlander 0f39d37ddc add api() method to service builder 2011-01-26 10:27:35 -03:00
Pablo Fernandez da3a3f4a0d Removed isEmpty for old VM compatibility 2011-01-19 13:41:41 -03:00
35 changed files with 763 additions and 106 deletions

No files matched your search

+4 -4
View File
@@ -1,5 +1,3 @@
!http://img411.imageshack.us/img411/2851/scribelogo.png!
h2. Welcome to the home of Scribe, the simple OAuth Java lib! h2. Welcome to the home of Scribe, the simple OAuth Java lib!
h1. Why use Scribe? h1. Why use Scribe?
@@ -20,10 +18,12 @@ h3. Threadsafe
Hit Scribe as hard and with many threads as you like. Hit Scribe as hard and with many threads as you like.
h3. Supports all major OAuth APIs out-of-the-box h3. Supports all major 1.0a and 2.0 OAuth APIs out-of-the-box
* Google * Google
* Facebook
* Yahoo * Yahoo
* LinkedIn * LinkedIn
@@ -61,7 +61,7 @@ You can pull scribe from a maven repository, just add this to your __pom.xml__ f
@<dependency>@ @<dependency>@
@<groupId>org.scribe</groupId>@ @<groupId>org.scribe</groupId>@
@<artifactId>scribe</artifactId>@ @<artifactId>scribe</artifactId>@
@<version>1.0.6</version>@ @<version>1.1.2</version>@
@</dependency>@ @</dependency>@
h1. Getting started in less than 2 minutes h1. Getting started in less than 2 minutes
Executable
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh
mvn source:jar javadoc:jar package gpg:sign repository:bundle-create -Dgpg.passphrase=$1
+21 -1
View File
@@ -23,4 +23,24 @@
* FEATURE: OAuth 2.0 Support and Facebook support out of the box (thanks Diego Silveira) * FEATURE: OAuth 2.0 Support and Facebook support out of the box (thanks Diego Silveira)
* REFACTOR: Api creation and OAuthService refactors * REFACTOR: Api creation and OAuthService refactors
* REFACTOR: Connections are created lazily (just before sending the request) * REFACTOR: Connections are created lazily (just before sending the request)
[1.1.1]
* FIX: Removed isEmpty() from OAuth2.0 code in order to support older JVMs
* FEATURE: Overloaded provider() method to support instances as well as classes (thanks dustismo)
* FIX: White-space parameter bug (thanks Krogoth)
* FEATURE: OAuth 2.0 now implements server-flow (thanks Diego Silveira)
[1.1.2]
* FIX: Fixed ugly bug that made 1.1.1 practically unusable (thanks Stubbs)
[1.1.3]
* FEATURE: Added rawResponse to Token, for extracting additional parameters (thanks Dirk McCormick)
* FEATURE: Added Dropbox Api
* FIX: Token responses now work despite of the token/secret order
* FEATURE: Added Foursquare OAuth 2.0 Api (thanks Tom Tasche)
* FEATURE: Added support for OAuth1.0a signatures via querystring parameters
* FEATURE: Added ConstantContact Api (thanks Matthew Laudato)
+1 -1
View File
@@ -4,7 +4,7 @@
<groupId>org.scribe</groupId> <groupId>org.scribe</groupId>
<artifactId>scribe</artifactId> <artifactId>scribe</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.1.0</version> <version>1.1.2</version>
<name>Scribe OAuth Library</name> <name>Scribe OAuth Library</name>
<description>The best OAuth library out there</description> <description>The best OAuth library out there</description>
<url>http://github.com/fernandezpablo85/scribe-java</url> <url>http://github.com/fernandezpablo85/scribe-java</url>
@@ -20,6 +20,7 @@ public class ServiceBuilder
private String callback; private String callback;
private Api api; private Api api;
private String scope; private String scope;
private SignatureType signatureType;
/** /**
* Default constructor * Default constructor
@@ -55,7 +56,22 @@ public class ServiceBuilder
} }
return api; return api;
} }
/**
* Configures the {@link Api}
*
* Overloaded version. Let's you use an instance instead of a class.
*
* @param api instance of {@link Api}s
* @return the {@link ServiceBuilder} instance for method chaining
*/
public ServiceBuilder provider(Api api)
{
Preconditions.checkNotNull(api, "Api cannot be null");
this.api = api;
return this;
}
/** /**
* Adds an OAuth callback url * Adds an OAuth callback url
* *
@@ -107,6 +123,19 @@ public class ServiceBuilder
this.scope = scope; this.scope = scope;
return this; return this;
} }
/**
* Configures the signature type, choose between header, querystring, etc. Defaults to Header
*
* @param scope The OAuth scope
* @return the {@link ServiceBuilder} instance for method chaining
*/
public ServiceBuilder signatureType(SignatureType type)
{
Preconditions.checkNotNull(type, "Signature type can't be null");
this.signatureType = type;
return this;
}
/** /**
* Returns the fully configured {@link OAuthService} * Returns the fully configured {@link OAuthService}
@@ -118,6 +147,6 @@ public class ServiceBuilder
Preconditions.checkNotNull(api, "You must specify a valid api through the provider() method"); Preconditions.checkNotNull(api, "You must specify a valid api through the provider() method");
Preconditions.checkEmptyString(apiKey, "You must provide an api key"); Preconditions.checkEmptyString(apiKey, "You must provide an api key");
Preconditions.checkEmptyString(apiSecret, "You must provide an api secret"); Preconditions.checkEmptyString(apiSecret, "You must provide an api secret");
return api.createService(new OAuthConfig(apiKey, apiSecret, callback), scope); return api.createService(new OAuthConfig(apiKey, apiSecret, callback, signatureType), scope);
} }
} }
@@ -0,0 +1,26 @@
package org.scribe.builder.api;
import org.scribe.model.*;
public class ConstantContactApi extends DefaultApi10a
{
private static final String AUTHORIZE_URL = "https://oauth.constantcontact.com/ws/oauth/confirm_access?oauth_token=%s";
@Override
public String getAccessTokenEndpoint()
{
return "https://oauth.constantcontact.com/ws/oauth/access_token";
}
@Override
public String getAuthorizationUrl(Token requestToken)
{
return String.format(AUTHORIZE_URL, requestToken.getToken());
}
@Override
public String getRequestTokenEndpoint()
{
return "https://oauth.constantcontact.com/ws/oauth/request_token";
}
}
@@ -1,5 +1,6 @@
package org.scribe.builder.api; package org.scribe.builder.api;
import org.scribe.extractors.*;
import org.scribe.model.*; import org.scribe.model.*;
import org.scribe.oauth.*; import org.scribe.oauth.*;
@@ -22,6 +23,33 @@ import org.scribe.oauth.*;
public abstract class DefaultApi20 implements Api public abstract class DefaultApi20 implements Api
{ {
/**
* Returns the access token extractor.
*
* @return access token extractor
*/
public AccessTokenExtractor getAccessTokenExtractor()
{
return new TokenExtractor20Impl();
}
/**
* Returns the verb for the access token endpoint (defaults to GET)
*
* @return access token endpoint verb
*/
public Verb getAccessTokenVerb()
{
return Verb.GET;
}
/**
* Returns the URL that receives the access token requests.
*
* @return access token URL
*/
public abstract String getAccessTokenEndpoint();
/** /**
* Returns the URL where you should redirect your users to authenticate * Returns the URL where you should redirect your users to authenticate
* your application. * your application.
@@ -0,0 +1,25 @@
package org.scribe.builder.api;
import org.scribe.model.*;
public class DropBoxApi extends DefaultApi10a
{
@Override
public String getAccessTokenEndpoint()
{
return "https://api.dropbox.com/0/oauth/access_token";
}
@Override
public String getAuthorizationUrl(Token requestToken)
{
return "https://www.dropbox.com/0/oauth/authorize?oauth_token="+requestToken.getToken();
}
@Override
public String getRequestTokenEndpoint()
{
return "https://api.dropbox.com/0/oauth/request_token";
}
}
@@ -5,12 +5,18 @@ import org.scribe.utils.*;
public class FacebookApi extends DefaultApi20 public class FacebookApi extends DefaultApi20
{ {
private static final String AUTHORIZE_URL = "https://graph.facebook.com/oauth/authorize?response_type=token&client_id=%s&redirect_uri=%s"; private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s";
@Override
public String getAccessTokenEndpoint()
{
return "https://graph.facebook.com/oauth/access_token";
}
@Override @Override
public String getAuthorizationUrl(OAuthConfig config) public String getAuthorizationUrl(OAuthConfig config)
{ {
Preconditions.checkValidUrl(config.getCallback(), "Must provide a valid url as callback. Facebook does not support OOB"); Preconditions.checkValidUrl(config.getCallback(), "Must provide a valid url as callback. Facebook does not support OOB");
return String.format(AUTHORIZE_URL, config.getApiKey(), URLUtils.percentEncode(config.getCallback())); return String.format(AUTHORIZE_URL, config.getApiKey(), URLUtils.formURLEncode(config.getCallback()));
} }
} }
@@ -0,0 +1,29 @@
package org.scribe.builder.api;
import org.scribe.extractors.*;
import org.scribe.model.*;
import org.scribe.utils.*;
public class Foursquare2Api extends DefaultApi20
{
private static final String AUTHORIZATION_URL = "https://foursquare.com/oauth2/authenticate?client_id=%s&response_type=code&redirect_uri=%s";
@Override
public String getAccessTokenEndpoint()
{
return "https://foursquare.com/oauth2/access_token?grant_type=authorization_code";
}
@Override
public String getAuthorizationUrl(OAuthConfig config)
{
Preconditions.checkValidUrl(config.getCallback(), "Must provide a valid url as callback. Foursquare2 does not support OOB");
return String.format(AUTHORIZATION_URL, config.getApiKey(), URLUtils.formURLEncode(config.getCallback()));
}
@Override
public AccessTokenExtractor getAccessTokenExtractor()
{
return new JsonTokenExtractor();
}
}
@@ -0,0 +1,28 @@
package org.scribe.builder.api;
import org.scribe.model.Token;
public class LoveFilmApi extends DefaultApi10a
{
private static final String REQUEST_TOKEN_URL = "http://openapi.lovefilm.com/oauth/request_token";
private static final String ACCESS_TOKEN_URL = "http://openapi.lovefilm.com/oauth/access_token";
private static final String AUTHORIZE_URL = "https://www.lovefilm.com/activate?oauth_token=%s";
@Override
public String getRequestTokenEndpoint()
{
return REQUEST_TOKEN_URL;
}
@Override
public String getAccessTokenEndpoint()
{
return ACCESS_TOKEN_URL;
}
@Override
public String getAuthorizationUrl(Token requestToken)
{
return String.format(AUTHORIZE_URL, requestToken.getToken());
}
}
@@ -32,11 +32,11 @@ public class BaseStringExtractorImpl implements BaseStringExtractor
private String getSortedAndEncodedParams(OAuthRequest request) private String getSortedAndEncodedParams(OAuthRequest request)
{ {
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<String, String>();
params.putAll(request.getQueryStringParams()); MapUtils.decodeAndAppendEntries(request.getQueryStringParams(), params);
params.putAll(request.getBodyParams()); MapUtils.decodeAndAppendEntries(request.getBodyParams(), params);
params.putAll(request.getOauthParameters()); MapUtils.decodeAndAppendEntries(request.getOauthParameters(), params);
params = MapUtils.sort(params); params = MapUtils.sort(params);
return URLUtils.percentEncode(URLUtils.formURLEncodeMap(params)); return URLUtils.percentEncode(URLUtils.concatSortedPercentEncodedParams(params));
} }
private void checkPreconditions(OAuthRequest request) private void checkPreconditions(OAuthRequest request)
@@ -0,0 +1,28 @@
package org.scribe.extractors;
import java.util.regex.*;
import org.scribe.exceptions.*;
import org.scribe.model.*;
import org.scribe.utils.*;
public class JsonTokenExtractor implements AccessTokenExtractor
{
private Pattern accessTokenPattern = Pattern.compile("\"access_token\":\"(\\S*)\"");
@Override
public Token extract(String response)
{
Preconditions.checkEmptyString(response, "Cannot extract a token from a null or empty String");
Matcher matcher = accessTokenPattern.matcher(response);
if(matcher.find())
{
return new Token(matcher.group(1), "");
}
else
{
throw new OAuthException("Cannot extract an acces token. Response was: "+response);
}
}
}
@@ -0,0 +1,36 @@
package org.scribe.extractors;
import java.util.regex.*;
import org.scribe.exceptions.*;
import org.scribe.model.*;
import org.scribe.utils.*;
/**
* Default implementation of {@AccessTokenExtractor}. Conforms to OAuth 2.0
*
*/
public class TokenExtractor20Impl implements AccessTokenExtractor
{
private static final String TOKEN_REGEX = "access_token=(\\S*?)(&(\\S*))?";
private static final String EMPTY_SECRET = "";
/**
* {@inheritDoc}
*/
public Token extract(String response)
{
Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string");
Matcher matcher = Pattern.compile(TOKEN_REGEX).matcher(response);
if (matcher.matches())
{
String token = URLUtils.formURLDecode(matcher.group(1));
return new Token(token, EMPTY_SECRET, response);
}
else
{
throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null);
}
}
}
@@ -15,7 +15,8 @@ import org.scribe.utils.*;
*/ */
public class TokenExtractorImpl implements RequestTokenExtractor, AccessTokenExtractor public class TokenExtractorImpl implements RequestTokenExtractor, AccessTokenExtractor
{ {
private static final String TOKEN_REGEX = "oauth_token=(\\S*)&oauth_token_secret=(\\S*?)(&(.*))?"; private static final Pattern TOKEN_REGEX = Pattern.compile("oauth_token=([^&]+)");
private static final Pattern SECRET_REGEX = Pattern.compile("oauth_token_secret=([^&]+)");
/** /**
* {@inheritDoc} * {@inheritDoc}
@@ -23,16 +24,21 @@ public class TokenExtractorImpl implements RequestTokenExtractor, AccessTokenExt
public Token extract(String response) public Token extract(String response)
{ {
Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string"); Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string");
String token = extract(response, TOKEN_REGEX);
String secret = extract(response, SECRET_REGEX);
return new Token(token, secret, response);
}
Matcher matcher = Pattern.compile(TOKEN_REGEX).matcher(response); private String extract(String response, Pattern p)
if (matcher.matches()) {
Matcher matcher = p.matcher(response);
if (matcher.find() && matcher.groupCount() >= 1)
{ {
String token = URLUtils.percentDecode(matcher.group(1)); return URLUtils.formURLDecode(matcher.group(1));
String secret = URLUtils.percentDecode(matcher.group(2)); }
return new Token(token, secret); else
} else
{ {
throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null); throw new OAuthException("Response body is incorrect. Can't extract token and secret from this: '" + response + "'", null);
} }
} }
} }
@@ -10,17 +10,25 @@ public class OAuthConfig
private String apiKey; private String apiKey;
private String apiSecret; private String apiSecret;
private String callback; private String callback;
private SignatureType signatureType;
public OAuthConfig(String key, String secret) public OAuthConfig(String key, String secret)
{ {
this(key,secret,null); this(key,secret,null);
} }
//Kept for backwards compatibility
public OAuthConfig(String key, String secret, String callback) public OAuthConfig(String key, String secret, String callback)
{
this(key,secret,callback,null);
}
public OAuthConfig(String key, String secret, String callback, SignatureType type)
{ {
this.apiKey = key; this.apiKey = key;
this.apiSecret = secret; this.apiSecret = secret;
this.callback = callback; this.callback = callback;
this.signatureType = type;
} }
public String getApiKey() public String getApiKey()
@@ -52,4 +60,14 @@ public class OAuthConfig
{ {
this.callback = callback; this.callback = callback;
} }
public SignatureType getSignatureType()
{
return signatureType != null ? signatureType : SignatureType.Header;
}
public void setSignatureType(SignatureType type)
{
this.signatureType = type;
}
} }
@@ -41,4 +41,9 @@ public class OAuthConstants
//OAuth 2.0 //OAuth 2.0
public static final String ACCESS_TOKEN = "access_token"; public static final String ACCESS_TOKEN = "access_token";
public static final String CLIENT_ID = "client_id";
public static final String CLIENT_SECRET = "client_secret";
public static final String REDIRECT_URI = "redirect_uri";
public static final String CODE = "code";
} }
+5 -10
View File
@@ -64,6 +64,7 @@ class Request
String effectiveUrl = URLUtils.appendParametersToQueryString(url, querystringParams); String effectiveUrl = URLUtils.appendParametersToQueryString(url, querystringParams);
if (connection == null) if (connection == null)
{ {
System.setProperty("http.keepAlive", "false");
connection = (HttpURLConnection) new URL(effectiveUrl).openConnection(); connection = (HttpURLConnection) new URL(effectiveUrl).openConnection();
} }
} }
@@ -144,22 +145,16 @@ class Request
* Get a {@link Map} of the query string parameters. * Get a {@link Map} of the query string parameters.
* *
* @return a map containing the query string parameters * @return a map containing the query string parameters
* @throws OAuthException if the URL is not valid
*/ */
public Map<String, String> getQueryStringParams() public Map<String, String> getQueryStringParams()
{ {
try try
{ {
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<String, String>();
String query = new URL(url).getQuery(); String queryString = new URL(url).getQuery();
if (query != null) params.putAll(URLUtils.queryStringToMap(queryString));
{ params.putAll(this.querystringParams);
for (String param : query.split("&"))
{
String pair[] = param.split("=");
params.put(pair[0], pair[1]);
}
}
params.putAll(querystringParams);
return params; return params;
} }
catch (MalformedURLException mue) catch (MalformedURLException mue)
@@ -0,0 +1,7 @@
package org.scribe.model;
public enum SignatureType
{
Header,
QueryString
}
+16
View File
@@ -13,6 +13,7 @@ public class Token implements Serializable
private final String token; private final String token;
private final String secret; private final String secret;
private final String rawResponse;
/** /**
* Default constructor * Default constructor
@@ -21,9 +22,15 @@ public class Token implements Serializable
* @param secret token secret * @param secret token secret
*/ */
public Token(String token, String secret) public Token(String token, String secret)
{
this(token, secret, null);
}
public Token(String token, String secret, String rawResponse)
{ {
this.token = token; this.token = token;
this.secret = secret; this.secret = secret;
this.rawResponse = rawResponse;
} }
public String getToken() public String getToken()
@@ -36,6 +43,15 @@ public class Token implements Serializable
return secret; return secret;
} }
public String getRawResponse()
{
if (rawResponse == null)
{
throw new IllegalStateException("This token object was not constructed by scribe and does not have a rawResponse");
}
return rawResponse;
}
@Override @Override
public String toString() public String toString()
{ {
+2 -1
View File
@@ -19,7 +19,8 @@ public class Verifier
*/ */
public Verifier(String value) public Verifier(String value)
{ {
this.value = URLUtils.percentDecode(value); Preconditions.checkNotNull(value, "Must provide a valid string as verifier");
this.value = value;
} }
public String getValue() public String getValue()
@@ -1,5 +1,7 @@
package org.scribe.oauth; package org.scribe.oauth;
import java.util.*;
import org.scribe.builder.api.*; import org.scribe.builder.api.*;
import org.scribe.model.*; import org.scribe.model.*;
@@ -36,8 +38,9 @@ public class OAuth10aServiceImpl implements OAuthService
public Token getRequestToken() public Token getRequestToken()
{ {
OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint()); OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint());
request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback());
addOAuthParams(request, OAuthConstants.EMPTY_TOKEN); addOAuthParams(request, OAuthConstants.EMPTY_TOKEN);
addOAuthHeader(request); addSignature(request);
Response response = request.send(); Response response = request.send();
return api.getRequestTokenExtractor().extract(response.getBody()); return api.getRequestTokenExtractor().extract(response.getBody());
} }
@@ -49,7 +52,6 @@ public class OAuth10aServiceImpl implements OAuthService
request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey()); request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey());
request.addOAuthParameter(OAuthConstants.SIGN_METHOD, api.getSignatureService().getSignatureMethod()); request.addOAuthParameter(OAuthConstants.SIGN_METHOD, api.getSignatureService().getSignatureMethod());
request.addOAuthParameter(OAuthConstants.VERSION, getVersion()); request.addOAuthParameter(OAuthConstants.VERSION, getVersion());
request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback());
if(scope != NO_SCOPE) request.addOAuthParameter(OAuthConstants.SCOPE, scope); if(scope != NO_SCOPE) request.addOAuthParameter(OAuthConstants.SCOPE, scope);
request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, token)); request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, token));
} }
@@ -63,7 +65,7 @@ public class OAuth10aServiceImpl implements OAuthService
request.addOAuthParameter(OAuthConstants.TOKEN, requestToken.getToken()); request.addOAuthParameter(OAuthConstants.TOKEN, requestToken.getToken());
request.addOAuthParameter(OAuthConstants.VERIFIER, verifier.getValue()); request.addOAuthParameter(OAuthConstants.VERIFIER, verifier.getValue());
addOAuthParams(request, requestToken); addOAuthParams(request, requestToken);
addOAuthHeader(request); addSignature(request);
Response response = request.send(); Response response = request.send();
return api.getAccessTokenExtractor().extract(response.getBody()); return api.getAccessTokenExtractor().extract(response.getBody());
} }
@@ -75,7 +77,7 @@ public class OAuth10aServiceImpl implements OAuthService
{ {
request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken()); request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken());
addOAuthParams(request, token); addOAuthParams(request, token);
addOAuthHeader(request); addSignature(request);
} }
/** /**
@@ -108,9 +110,20 @@ public class OAuth10aServiceImpl implements OAuthService
return api.getSignatureService().getSignature(baseString, config.getApiSecret(), token.getSecret()); return api.getSignatureService().getSignature(baseString, config.getApiSecret(), token.getSecret());
} }
private void addOAuthHeader(OAuthRequest request) private void addSignature(OAuthRequest request)
{ {
String oauthHeader = api.getHeaderExtractor().extract(request); switch (config.getSignatureType())
request.addHeader(OAuthConstants.HEADER, oauthHeader); {
case Header:
String oauthHeader = api.getHeaderExtractor().extract(request);
request.addHeader(OAuthConstants.HEADER, oauthHeader);
break;
case QueryString:
for (Map.Entry<String, String> entry : request.getOauthParameters().entrySet())
{
request.addQuerystringParameter(entry.getKey(), entry.getValue());
}
break;
}
} }
} }
@@ -35,7 +35,13 @@ public class OAuth20ServiceImpl implements OAuthService
*/ */
public Token getAccessToken(Token requestToken, Verifier verifier) public Token getAccessToken(Token requestToken, Verifier verifier)
{ {
throw new UnsupportedOperationException("Unsupported operation, please use 'getAuthorizationUrl' and redirect your users there"); OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
request.addQuerystringParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
request.addQuerystringParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue());
request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback());
Response response = request.send();
return api.getAccessTokenExtractor().extract(response.getBody());
} }
/** /**
@@ -33,4 +33,19 @@ public class MapUtils
Collections.sort(keys); Collections.sort(keys);
return keys; return keys;
} }
/**
* Form-urlDecodes and appends all keys from the source {@link Map} to the
* target {@link Map}
*
* @param source Map from where the keys get copied and decoded
* @param target Map where the decoded keys are copied to
*/
public static void decodeAndAppendEntries(Map<String, String> source, Map<String, String> target)
{
for (String key: source.keySet())
{
target.put(URLUtils.percentEncode(key), URLUtils.percentEncode(source.get(key)));
}
}
} }
+78 -22
View File
@@ -13,8 +13,8 @@ public class URLUtils
{ {
private static final String EMPTY_STRING = ""; private static final String EMPTY_STRING = "";
private static final String UTF_8 = "UTF-8"; private static final String UTF_8 = "UTF-8";
private static final char PAIR_SEPARATOR = '='; private static final String PAIR_SEPARATOR = "=";
private static final char PARAM_SEPARATOR = '&'; private static final String PARAM_SEPARATOR = "&";
private static final char QUERY_STRING_SEPARATOR = '?'; private static final char QUERY_STRING_SEPARATOR = '?';
private static final String ERROR_MSG = String.format("Cannot find specified encoding: %s", UTF_8); private static final String ERROR_MSG = String.format("Cannot find specified encoding: %s", UTF_8);
@@ -31,7 +31,7 @@ public class URLUtils
} }
/** /**
* Turns a map into a form-url-encoded string (key=value&key2=value2) * Turns a map into a form-urlencoded string
* *
* @param map any map * @param map any map
* @return form-url-encoded string * @return form-url-encoded string
@@ -47,32 +47,43 @@ public class URLUtils
StringBuffer encodedString = new StringBuffer(map.size() * 20); StringBuffer encodedString = new StringBuffer(map.size() * 20);
for (String key : map.keySet()) for (String key : map.keySet())
{ {
if(encodedString.length() > 0) encodedString.append(PARAM_SEPARATOR).append(formURLEncode(key));
if(map.get(key) != null)
{ {
encodedString.append(PARAM_SEPARATOR); encodedString.append(PAIR_SEPARATOR).append(formURLEncode(map.get(key)));
} }
encodedString.append(percentEncode(key)).append(PAIR_SEPARATOR).append(percentEncode(map.get(key)));
} }
return encodedString.toString(); return encodedString.toString().substring(1);
} }
/** /**
* Percent encodes a string * Percent encodes a string
* *
* @param plain * @param string plain string
* @return percent encoded string * @return percent encoded string
*/ */
public static String percentEncode(String string) public static String percentEncode(String string)
{
String encoded = formURLEncode(string);
for (EncodingRule rule : ENCODING_RULES)
{
encoded = rule.apply(encoded);
}
return encoded;
}
/**
* Translates a string into application/x-www-form-urlencoded format
*
* @param plain
* @return form-urlencoded string
*/
public static String formURLEncode(String string)
{ {
Preconditions.checkNotNull(string, "Cannot encode null string"); Preconditions.checkNotNull(string, "Cannot encode null string");
try try
{ {
String encoded = URLEncoder.encode(string, UTF_8); return URLEncoder.encode(string, UTF_8);
for(EncodingRule rule : ENCODING_RULES)
{
encoded = rule.apply(encoded);
}
return encoded;
} }
catch (UnsupportedEncodingException uee) catch (UnsupportedEncodingException uee)
{ {
@@ -81,12 +92,12 @@ public class URLUtils
} }
/** /**
* Percent decodes a string * Decodes a application/x-www-form-urlencoded string
* *
* @param string percent encoded string * @param string form-urlencoded string
* @return plain string * @return plain string
*/ */
public static String percentDecode(String string) public static String formURLDecode(String string)
{ {
Preconditions.checkNotNull(string, "Cannot decode null string"); Preconditions.checkNotNull(string, "Cannot decode null string");
try try
@@ -110,12 +121,57 @@ public class URLUtils
{ {
Preconditions.checkNotNull(url, "Cannot append to null URL"); Preconditions.checkNotNull(url, "Cannot append to null URL");
String queryString = URLUtils.formURLEncodeMap(params); String queryString = URLUtils.formURLEncodeMap(params);
if (queryString.isEmpty()) return url; if (queryString.equals(EMPTY_STRING))
{
return url;
}
else
{
url += url.indexOf(QUERY_STRING_SEPARATOR) != -1 ? PARAM_SEPARATOR : QUERY_STRING_SEPARATOR;
url += queryString;
return url;
}
}
// Check if there are parameters in the url already and use '&' instead of '?' /**
url += url.indexOf(QUERY_STRING_SEPARATOR) != -1 ? PARAM_SEPARATOR : QUERY_STRING_SEPARATOR; * Concats a key-value map into a querystring-like String
url += queryString; *
return url; * @param params key-value map
* @return querystring-like String
*/
// TODO Move to MapUtils
public static String concatSortedPercentEncodedParams(Map<String, String> params)
{
StringBuilder result = new StringBuilder();
for (String key : params.keySet())
{
result.append(key).append(PAIR_SEPARATOR);
result.append(params.get(key)).append(PARAM_SEPARATOR);
}
return result.toString().substring(0, result.length() - 1);
}
/**
* Parses and form-urldecodes a querystring-like string into a map
*
* @param queryString querystring-like String
* @return a map with the form-urldecoded parameters
*/
// TODO Move to MapUtils
public static Map<String, String> queryStringToMap(String queryString)
{
Map<String, String> result = new HashMap<String, String>();
if (queryString != null && queryString.length() > 0)
{
for (String param : queryString.split(PARAM_SEPARATOR))
{
String pair[] = param.split(PAIR_SEPARATOR);
String key = formURLDecode(pair[0]);
String value = pair.length > 1 ? formURLDecode(pair[1]) : EMPTY_STRING;
result.put(key, value);
}
}
return result;
} }
private static final class EncodingRule private static final class EncodingRule
@@ -18,12 +18,13 @@ public class ServiceBuilderTest
} }
@Test @Test
public void shouldReturnOOBasDefaultCallback() public void shouldReturnConfigDefaultValues()
{ {
builder.provider(ApiMock.class).apiKey("key").apiSecret("secret").build(); builder.provider(ApiMock.class).apiKey("key").apiSecret("secret").build();
assertEquals(ApiMock.config.getApiKey(), "key"); assertEquals(ApiMock.config.getApiKey(), "key");
assertEquals(ApiMock.config.getApiSecret(), "secret"); assertEquals(ApiMock.config.getApiSecret(), "secret");
assertEquals(ApiMock.config.getCallback(), OAuthConstants.OUT_OF_BAND); assertEquals(ApiMock.config.getCallback(), OAuthConstants.OUT_OF_BAND);
assertEquals(ApiMock.config.getSignatureType(), SignatureType.Header);
} }
@Test @Test
@@ -35,6 +36,15 @@ public class ServiceBuilderTest
assertEquals(ApiMock.config.getCallback(), "http://example.com"); assertEquals(ApiMock.config.getCallback(), "http://example.com");
} }
@Test
public void shouldAcceptASignatureType()
{
builder.provider(ApiMock.class).apiKey("key").apiSecret("secret").signatureType(SignatureType.QueryString).build();
assertEquals(ApiMock.config.getApiKey(), "key");
assertEquals(ApiMock.config.getApiSecret(), "secret");
assertEquals(ApiMock.config.getSignatureType(), SignatureType.QueryString);
}
@Test(expected=IllegalArgumentException.class) @Test(expected=IllegalArgumentException.class)
public void shouldNotAcceptAnInvalidUrlAsCallback() public void shouldNotAcceptAnInvalidUrlAsCallback()
{ {
@@ -11,13 +11,12 @@ public class FacebookExample
{ {
private static final String NETWORK_NAME = "Facebook"; private static final String NETWORK_NAME = "Facebook";
private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/me"; private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/me";
private static final String NO_SECRET_NEEDED = "";
private static final Token EMPTY_TOKEN = null; private static final Token EMPTY_TOKEN = null;
public static void main(String[] args) public static void main(String[] args)
{ {
// Replace these with your own api key and secret // Replace these with your own api key and secret
String apiKey = "your_api_key"; String apiKey = "your_app_id";
String apiSecret = "your_api_secret"; String apiSecret = "your_api_secret";
OAuthService service = new ServiceBuilder() OAuthService service = new ServiceBuilder()
.provider(FacebookApi.class) .provider(FacebookApi.class)
@@ -36,9 +35,16 @@ public class FacebookExample
System.out.println("Got the Authorization URL!"); System.out.println("Got the Authorization URL!");
System.out.println("Now go and authorize Scribe here:"); System.out.println("Now go and authorize Scribe here:");
System.out.println(authorizationUrl); System.out.println(authorizationUrl);
System.out.println("And paste the access token here"); System.out.println("And paste the authorization code here");
System.out.print(">>"); System.out.print(">>");
Token accessToken = new Token(in.nextLine(), NO_SECRET_NEEDED); Verifier verifier = new Verifier(in.nextLine());
System.out.println();
// Trade the Request Token and Verfier for the Access Token
System.out.println("Trading the Request Token for an Access Token...");
Token accessToken = service.getAccessToken(EMPTY_TOKEN, verifier);
System.out.println("Got the Access Token!");
System.out.println("(if your curious it looks like this: " + accessToken + " )");
System.out.println(); System.out.println();
// Now let's go and ask for a protected resource! // Now let's go and ask for a protected resource!
@@ -0,0 +1,63 @@
package org.scribe.examples;
import java.util.*;
import org.scribe.builder.*;
import org.scribe.builder.api.*;
import org.scribe.model.*;
import org.scribe.oauth.*;
public class Foursquare2Example
{
private static final String PROTECTED_RESOURCE_URL = "https://api.foursquare.com/v2/users/self/friends?oauth_token=";
private static final Token EMPTY_TOKEN = null;
public static void main(String[] args)
{
// Replace these with your own api key and secret
String apiKey = "FEGFXJUFANVVDHVSNUAMUKTTXCP1AJQD53E33XKJ44YP1S4I";
String apiSecret = "AYWKUL5SWPNC0CTQ202QXRUG2NLZYXMRA34ZSDW4AUYBG2RC";
OAuthService service = new ServiceBuilder()
.provider(Foursquare2Api.class)
.apiKey(apiKey)
.apiSecret(apiSecret)
.callback("http://localhost:9000/")
.build();
Scanner in = new Scanner(System.in);
System.out.println("=== Foursquare2's OAuth Workflow ===");
System.out.println();
// Obtain the Authorization URL
System.out.println("Fetching the Authorization URL...");
String authorizationUrl = service.getAuthorizationUrl(EMPTY_TOKEN);
System.out.println("Got the Authorization URL!");
System.out.println("Now go and authorize Scribe here:");
System.out.println(authorizationUrl);
System.out.println("And paste the authorization code here");
System.out.print(">>");
Verifier verifier = new Verifier(in.nextLine());
System.out.println();
// Trade the Request Token and Verfier for the Access Token
System.out.println("Trading the Request Token for an Access Token...");
Token accessToken = service.getAccessToken(EMPTY_TOKEN, verifier);
System.out.println("Got the Access Token!");
System.out.println("(if your curious it looks like this: " + accessToken + " )");
System.out.println();
// Now let's go and ask for a protected resource!
System.out.println("Now we're going to access a protected resource...");
OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL + accessToken.getToken());
service.signRequest(accessToken, request);
Response response = request.send();
System.out.println("Got it! Lets see what we found...");
System.out.println();
System.out.println(response.getCode());
System.out.println(response.getBody());
System.out.println();
System.out.println("Thats it man! Go and build something awesome with Scribe! :)");
}
}
@@ -0,0 +1,64 @@
package org.scribe.examples;
import java.util.*;
import org.scribe.builder.*;
import org.scribe.builder.api.*;
import org.scribe.model.*;
import org.scribe.oauth.*;
public class LoveFilmExample
{
private static final String NETWORK_NAME = "LoveFilm";
private static final String PROTECTED_RESOURCE_URL = "https://api.lovefilm.com/users";
public static void main(String[] args)
{
// Replace these with your own api key and secret
String apiKey = "your_key";
String apiSecret = "your_secret";
OAuthService service = new ServiceBuilder().provider(LoveFilmApi.class).apiKey(apiKey).apiSecret(apiSecret).build();
Scanner in = new Scanner(System.in);
System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ===");
System.out.println();
// Grab a request token.
System.out.println("Fetching request token.");
Token requestToken = service.getRequestToken();
System.out.println("Got it ... ");
System.out.println(requestToken.getToken());
// Obtain the Authorization URL
System.out.println("Fetching the Authorization URL...");
String authorizationUrl = service.getAuthorizationUrl(requestToken);
System.out.println("Got the Authorization URL!");
System.out.println("Now go and authorize Scribe here:");
System.out.println(authorizationUrl);
System.out.println("And paste the authorization code here");
System.out.print(">>");
Verifier verifier = new Verifier(in.nextLine());
System.out.println();
// Trade the Request Token and Verfier for the Access Token
System.out.println("Trading the Request Token for an Access Token...");
Token accessToken = service.getAccessToken(requestToken, verifier);
System.out.println("Got the Access Token!");
System.out.println("(if your curious it looks like this: " + accessToken + " )");
System.out.println();
// Now let's go and ask for a protected resource!
System.out.println("Now we're going to access a protected resource...");
OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL);
service.signRequest(accessToken, request);
Response response = request.send();
System.out.println("Got it! Lets see what we found...");
System.out.println();
System.out.println(response.getCode());
System.out.println(response.getBody());
System.out.println();
System.out.println("Thats it man! Go and build something awesome with Scribe! :)");
}
}
@@ -0,0 +1,31 @@
package org.scribe.extractors;
import static org.junit.Assert.*;
import org.junit.*;
import org.scribe.model.*;
public class JsonTokenExtractorTest
{
private String response = "'{ \"access_token\":\"I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T3X\"}'";
private JsonTokenExtractor extractor = new JsonTokenExtractor();
@Test
public void shouldParseResponse()
{
Token token = extractor.extract(response);
assertEquals(token.getToken(), "I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T3X");
}
@Test(expected=IllegalArgumentException.class)
public void shouldThrowExceptionIfForNullParameters()
{
extractor.extract(null);
}
@Test(expected=IllegalArgumentException.class)
public void shouldThrowExceptionIfForEmptyStrings()
{
extractor.extract("");
}
}
@@ -0,0 +1,58 @@
package org.scribe.extractors;
import static org.junit.Assert.*;
import org.junit.*;
import org.scribe.exceptions.*;
import org.scribe.model.*;
public class TokenExtractor20Test
{
private TokenExtractor20Impl extractor;
@Before
public void setup()
{
extractor = new TokenExtractor20Impl();
}
@Test
public void shouldExtractTokenFromOAuthStandardResponse()
{
String response = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE";
Token extracted = extractor.extract(response);
assertEquals("166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE", extracted.getToken());
assertEquals("", extracted.getSecret());
}
@Test
public void shouldExtractTokenFromResponseWithExpiresParam()
{
String response = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE&expires=5108";
Token extracted = extractor.extract(response);
assertEquals("166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE", extracted.getToken());
assertEquals("", extracted.getSecret());
}
@Test(expected = OAuthException.class)
public void shouldThrowExceptionIfTokenIsAbsent()
{
String response = "&expires=5108";
extractor.extract(response);
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionIfResponseIsNull()
{
String response = null;
extractor.extract(response);
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionIfResponseIsEmptyString()
{
String response = "";
extractor.extract(response);
}
}
@@ -26,6 +26,15 @@ public class TokenExtractorTest
assertEquals("hdhd0244k9j7ao03", extracted.getSecret()); assertEquals("hdhd0244k9j7ao03", extracted.getSecret());
} }
@Test
public void shouldExtractTokenFromInvertedOAuthStandardResponse()
{
String response = "oauth_token_secret=hh5s93j4hdidpola&oauth_token=hdhd0244k9j7ao03";
Token extracted = extractor.extract(response);
assertEquals("hh5s93j4hdidpola", extracted.getSecret());
assertEquals("hdhd0244k9j7ao03", extracted.getToken());
}
@Test @Test
public void shouldExtractTokenFromResponseWithCallbackConfirmed() public void shouldExtractTokenFromResponseWithCallbackConfirmed()
{ {
@@ -16,8 +16,10 @@ public class RequestTest
{ {
connection = new ConnectionStub(); connection = new ConnectionStub();
postRequest = new Request(Verb.POST, "http://example.com"); postRequest = new Request(Verb.POST, "http://example.com");
postRequest.addBodyParameter("param", "value");
postRequest.addBodyParameter("param with spaces", "value with spaces");
postRequest.setConnection(connection); postRequest.setConnection(connection);
getRequest = new Request(Verb.GET, "http://example.com?qsparam=value&other=value+with+spaces"); getRequest = new Request(Verb.GET, "http://example.com?qsparam=value&other+param=value+with+spaces");
getRequest.setConnection(connection); getRequest.setConnection(connection);
} }
@@ -34,6 +36,7 @@ public class RequestTest
assertEquals(2, getRequest.getQueryStringParams().size()); assertEquals(2, getRequest.getQueryStringParams().size());
assertEquals(0, postRequest.getQueryStringParams().size()); assertEquals(0, postRequest.getQueryStringParams().size());
assertTrue(getRequest.getQueryStringParams().containsKey("qsparam")); assertTrue(getRequest.getQueryStringParams().containsKey("qsparam"));
assertTrue(getRequest.getQueryStringParams().get("qsparam").equals("value"));
} }
@Test @Test
@@ -47,12 +50,10 @@ public class RequestTest
} }
@Test @Test
public void shouldSetBodyParamsAndHeaders() public void shouldSetBodyParamsAndAddContentLength()
{ {
postRequest.addBodyParameter("param", "value"); assertEquals("param=value&param+with+spaces=value+with+spaces", postRequest.getBodyContents());
postRequest.addBodyParameter("param two", "value with spaces");
postRequest.send(); postRequest.send();
assertEquals("param%20two=value%20with%20spaces&param=value", postRequest.getBodyContents());
assertTrue(connection.getHeaders().containsKey("Content-Length")); assertTrue(connection.getHeaders().containsKey("Content-Length"));
} }
@@ -74,4 +75,9 @@ public class RequestTest
assertEquals(3, request.getQueryStringParams().size()); assertEquals(3, request.getQueryStringParams().size());
} }
@Test
public void shouldHandleQueryStringSpaceEncodingProperly()
{
assertTrue(getRequest.getQueryStringParams().get("other param").equals("value with spaces"));
}
} }
@@ -1,21 +0,0 @@
package org.scribe.model;
import static org.junit.Assert.*;
import org.junit.*;
public class VerifierTest
{
@Test
public void shouldParse()
{
Verifier verifier = new Verifier("p8k%2BGIjIL9PblXq%2BpH6LmT9l");
assertEquals(verifier.getValue(), "p8k+GIjIL9PblXq+pH6LmT9l");
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowErrorForNullString()
{
new Verifier(null);
}
}
@@ -16,7 +16,7 @@ public class URLUtilsTest
params.put("key with spaces", "value with spaces"); params.put("key with spaces", "value with spaces");
params.put("&symbols!", "#!"); params.put("&symbols!", "#!");
String expected = "key=value&key%20with%20spaces=value%20with%20spaces&%26symbols%21=%23%21"; String expected = "key=value&key+with+spaces=value+with+spaces&%26symbols%21=%23%21";
assertEquals(expected, URLUtils.formURLEncodeMap(params)); assertEquals(expected, URLUtils.formURLEncodeMap(params));
} }
@@ -28,6 +28,17 @@ public class URLUtilsTest
assertEquals(expected, URLUtils.formURLEncodeMap(params)); assertEquals(expected, URLUtils.formURLEncodeMap(params));
} }
@Test
public void shouldFormURLEncodeMapWithMissingValues()
{
Map<String, String> params = new LinkedHashMap<String, String>();
params.put("key", "value");
params.put("key with spaces", null);
String expected = "key=value&key+with+spaces";
assertEquals(expected, URLUtils.formURLEncodeMap(params));
}
@Test @Test
public void shouldPercentEncodeString() public void shouldPercentEncodeString()
{ {
@@ -37,24 +48,32 @@ public class URLUtilsTest
} }
@Test @Test
public void shouldPercentDecodeString() public void shouldFormURLEncodeString()
{ {
String toDecode = "this+is+a+test+%26%5E"; String toEncode = "this is a test &^";
String expected = "this is a test &^"; String expected = "this+is+a+test+%26%5E";
assertEquals(expected, URLUtils.percentDecode(toDecode)); assertEquals(expected, URLUtils.formURLEncode(toEncode));
} }
@Test @Test
public void shouldEncodeAllSpecialCharacters() public void shouldFormURLDecodeString()
{
String toDecode = "this+is+a+test+%26%5E";
String expected = "this is a test &^";
assertEquals(expected, URLUtils.formURLDecode(toDecode));
}
@Test
public void shouldPercentEncodeAllSpecialCharacters()
{ {
String plain = "!*'();:@&=+$,/?#[]"; String plain = "!*'();:@&=+$,/?#[]";
String encoded = "%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%23%5B%5D"; String encoded = "%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%23%5B%5D";
assertEquals(encoded, URLUtils.percentEncode(plain)); assertEquals(encoded, URLUtils.percentEncode(plain));
assertEquals(plain, URLUtils.percentDecode(encoded)); assertEquals(plain, URLUtils.formURLDecode(encoded));
} }
@Test @Test
public void shouldNotEncodeReservedCharacters() public void shouldNotPercentEncodeReservedCharacters()
{ {
String plain = "abcde123456-._~"; String plain = "abcde123456-._~";
String encoded = plain; String encoded = plain;
@@ -79,7 +98,7 @@ public class URLUtilsTest
public void shouldThrowExceptionIfStringToDecodeIsNull() public void shouldThrowExceptionIfStringToDecodeIsNull()
{ {
String toDecode = null; String toDecode = null;
URLUtils.percentDecode(toDecode); URLUtils.formURLDecode(toDecode);
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
@@ -103,7 +122,7 @@ public class URLUtilsTest
public void shouldAppendParametersToSimpleUrl() public void shouldAppendParametersToSimpleUrl()
{ {
String url = "http://www.example.com"; String url = "http://www.example.com";
String expectedUrl = "http://www.example.com?param1=value1&param2=value%20with%20spaces"; String expectedUrl = "http://www.example.com?param1=value1&param2=value+with+spaces";
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<String, String>();
params.put("param1", "value1"); params.put("param1", "value1");
@@ -117,7 +136,7 @@ public class URLUtilsTest
public void shouldAppendParametersToUrlWithQuerystring() public void shouldAppendParametersToUrlWithQuerystring()
{ {
String url = "http://www.example.com?already=present"; String url = "http://www.example.com?already=present";
String expectedUrl = "http://www.example.com?already=present&param1=value1&param2=value%20with%20spaces"; String expectedUrl = "http://www.example.com?already=present&param1=value1&param2=value+with+spaces";
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<String, String>();
params.put("param1", "value1"); params.put("param1", "value1");
@@ -127,4 +146,21 @@ public class URLUtilsTest
Assert.assertEquals(url, expectedUrl); Assert.assertEquals(url, expectedUrl);
} }
@Test
public void shouldPercentEncodePlusSymbol()
{
String plain = "7aEP+jNAwvjc0mjhqg0nuXPf";
String encoded = "7aEP%2BjNAwvjc0mjhqg0nuXPf";
Assert.assertEquals(encoded, URLUtils.percentEncode(plain));
}
@Test
public void shouldURLDecodePlusSymbol()
{
String encoded = "oauth_verifier=7aEP%2BjNAwvjc0mjhqg0nuXPf";
String expected = "oauth_verifier=7aEP+jNAwvjc0mjhqg0nuXPf";
Assert.assertEquals(expected, URLUtils.formURLDecode(encoded));
}
} }