9 Commits
39 changed files with 528 additions and 62 deletions

No files matched your search

+46 -2
View File
@@ -4,8 +4,38 @@
<groupId>org.scribe</groupId> <groupId>org.scribe</groupId>
<artifactId>scribe</artifactId> <artifactId>scribe</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.0.5</version> <version>1.0.6</version>
<name>OAuth Library</name> <name>Scribe OAuth Library</name>
<description>The best OAuth library out there</description>
<url>http://github.com/fernandezpablo85/scribe-java</url>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>5</version>
</parent>
<developers>
<developer>
<id>fernandezpablo85</id>
<name>Pablo Fernandez</name>
<email>fernandezpablo85@gmail.com</email>
<timezone>-3</timezone>
</developer>
</developers>
<licenses>
<license>
<name>MIT</name>
<url>http://github.com/fernandezpablo85/scribe-java/blob/master/LICENSE.txt</url>
</license>
</licenses>
<scm>
<connection>scm:http://github.com/fernandezpablo85/scribe-java.git</connection>
<developerConnection>scm:http://github.com/fernandezpablo85/scribe-java.git</developerConnection>
<url>http://github.com/fernandezpablo85/scribe-java.git</url>
</scm>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -18,6 +48,7 @@
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.8.1</version> <version>4.8.1</version>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -30,6 +61,19 @@
<target>1.6</target> <target>1.6</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>
@@ -20,7 +20,11 @@ public class ServiceBuilder
private String apiSecret; private String apiSecret;
private String callback; private String callback;
private Api api; private Api api;
private String scope;
/**
* Default constructor
*/
public ServiceBuilder() public ServiceBuilder()
{ {
this.callback = OAuthConstants.OUT_OF_BAND; this.callback = OAuthConstants.OUT_OF_BAND;
@@ -92,6 +96,19 @@ public class ServiceBuilder
return this; return this;
} }
/**
* Configures the OAuth scope. This is only necessary in some APIs (like Google's).
*
* @param scope The OAuth scope
* @return the {@link ServiceBuilder} instance for method chaining
*/
public ServiceBuilder scope(String scope)
{
Preconditions.checkEmptyString(scope, "Invalid OAuth scope");
this.scope = scope;
return this;
}
/** /**
* Returns the fully configured {@link OAuthService} * Returns the fully configured {@link OAuthService}
* *
@@ -102,6 +119,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(apiKey, apiSecret, callback); return api.createService(apiKey, apiSecret, callback, scope);
} }
} }
@@ -16,8 +16,9 @@ public interface Api
* @param apiKey your application api key * @param apiKey your application api key
* @param apiSecret your application api secret * @param apiSecret your application api secret
* @param callback the callback url (or 'oob' for out of band OAuth) * @param callback the callback url (or 'oob' for out of band OAuth)
* @param scope the OAuth scope
* *
* @return fully configured {@link OAuthService} * @return fully configured {@link OAuthService}
*/ */
OAuthService createService(String apiKey, String apiSecret, String callback); OAuthService createService(String apiKey, String apiSecret, String callback, String scope);
} }
@@ -5,53 +5,89 @@ import org.scribe.model.*;
import org.scribe.oauth.*; import org.scribe.oauth.*;
import org.scribe.services.*; import org.scribe.services.*;
/**
* Default implementation of the OAuth protocol, version 1.0a
*
* This class is meant to be extended by concrete implementations of the API.
* If your Api adheres to the 1.0a protocol correctly, you just need to extend
* this class and define the getters for your endpoints.
*
* @author Pablo Fernandez
*
*/
public abstract class DefaultApi10a implements Api public abstract class DefaultApi10a implements Api
{ {
public AccessTokenExtractor getAccessTokenExtractor()
protected AccessTokenExtractor getAccessTokenExtractor()
{ {
return new TokenExtractorImpl(); return new TokenExtractorImpl();
} }
public BaseStringExtractor getBaseStringExtractor() protected BaseStringExtractor getBaseStringExtractor()
{ {
return new BaseStringExtractorImpl(); return new BaseStringExtractorImpl();
} }
public HeaderExtractor getHeaderExtractor() protected HeaderExtractor getHeaderExtractor()
{ {
return new HeaderExtractorImpl(); return new HeaderExtractorImpl();
} }
public RequestTokenExtractor getRequestTokenExtractor() protected RequestTokenExtractor getRequestTokenExtractor()
{ {
return new TokenExtractorImpl(); return new TokenExtractorImpl();
} }
public SignatureService getSignatureService() protected SignatureService getSignatureService()
{ {
return new HMACSha1SignatureService(); return new HMACSha1SignatureService();
} }
public TimestampService getTimestampService() protected TimestampService getTimestampService()
{ {
return new TimestampServiceImpl(); return new TimestampServiceImpl();
} }
public Verb getAccessTokenVerb() protected Verb getAccessTokenVerb()
{ {
return Verb.POST; return Verb.POST;
} }
public Verb getRequestTokenVerb() protected Verb getRequestTokenVerb()
{ {
return Verb.POST; return Verb.POST;
} }
public abstract String getRequestTokenEndpoint(); /**
public abstract String getAccessTokenEndpoint(); * Returns the URL that receives the request token requests.
*
* @return request token URL
*/
protected abstract String getRequestTokenEndpoint();
@Override /**
public OAuthService createService(String apiKey, String apiSecret, String callback) * Returns the URL that receives the access token requests.
*
* @return access token URL
*/
protected abstract String getAccessTokenEndpoint();
/**
* Returns the {@link OAuthService} for this Api
*
* @param apiKey Key
* @param apiSecret Api Secret
* @param callback OAuth callback (either URL or 'oob')
* @param scope OAuth scope (optional)
*/
public OAuthService createService(String apiKey, String apiSecret, String callback, String scope)
{
OAuthService service = createService(apiKey, apiSecret, callback);
service.addScope(scope);
return service;
}
private OAuthService createService(String apiKey, String apiSecret, String callback)
{ {
return new OAuth10aServiceImpl( getSignatureService(), return new OAuth10aServiceImpl( getSignatureService(),
getTimestampService(), getTimestampService(),
@@ -61,7 +97,7 @@ public abstract class DefaultApi10a implements Api
getAccessTokenExtractor(), getAccessTokenExtractor(),
createConfig(apiKey, apiSecret, callback)); createConfig(apiKey, apiSecret, callback));
} }
private OAuthConfig createConfig(String apiKey, String apiSecret, String callback) private OAuthConfig createConfig(String apiKey, String apiSecret, String callback)
{ {
OAuthConfig config = new OAuthConfig(); OAuthConfig config = new OAuthConfig();
@@ -3,13 +3,13 @@ package org.scribe.builder.api;
public class FoursquareApi extends DefaultApi10a public class FoursquareApi extends DefaultApi10a
{ {
@Override @Override
public String getAccessTokenEndpoint() protected String getAccessTokenEndpoint()
{ {
return "http://foursquare.com/oauth/access_token"; return "http://foursquare.com/oauth/access_token";
} }
@Override @Override
public String getRequestTokenEndpoint() protected String getRequestTokenEndpoint()
{ {
return "http://foursquare.com/oauth/request_token"; return "http://foursquare.com/oauth/request_token";
} }
@@ -3,13 +3,13 @@ package org.scribe.builder.api;
public class GoogleApi extends DefaultApi10a public class GoogleApi extends DefaultApi10a
{ {
@Override @Override
public String getAccessTokenEndpoint() protected String getAccessTokenEndpoint()
{ {
return "https://www.google.com/accounts/OAuthGetAccessToken"; return "https://www.google.com/accounts/OAuthGetAccessToken";
} }
@Override @Override
public String getRequestTokenEndpoint() protected String getRequestTokenEndpoint()
{ {
return "https://www.google.com/accounts/OAuthGetRequestToken"; return "https://www.google.com/accounts/OAuthGetRequestToken";
} }
@@ -7,19 +7,19 @@ public class LinkedInApi extends DefaultApi10a
{ {
@Override @Override
public String getAccessTokenEndpoint() protected String getAccessTokenEndpoint()
{ {
return "https://api.linkedin.com/uas/oauth/accessToken"; return "https://api.linkedin.com/uas/oauth/accessToken";
} }
@Override @Override
public String getRequestTokenEndpoint() protected String getRequestTokenEndpoint()
{ {
return "https://api.linkedin.com/uas/oauth/requestToken"; return "https://api.linkedin.com/uas/oauth/requestToken";
} }
@Override @Override
public BaseStringExtractor getBaseStringExtractor() protected BaseStringExtractor getBaseStringExtractor()
{ {
return new LinkedInBaseStringExtractorImpl(); return new LinkedInBaseStringExtractorImpl();
} }
@@ -3,13 +3,13 @@ package org.scribe.builder.api;
public class TwitterApi extends DefaultApi10a public class TwitterApi extends DefaultApi10a
{ {
@Override @Override
public String getAccessTokenEndpoint() protected String getAccessTokenEndpoint()
{ {
return "http://twitter.com/oauth/access_token"; return "http://twitter.com/oauth/access_token";
} }
@Override @Override
public String getRequestTokenEndpoint() protected String getRequestTokenEndpoint()
{ {
return "http://twitter.com/oauth/request_token"; return "http://twitter.com/oauth/request_token";
} }
@@ -3,13 +3,13 @@ package org.scribe.builder.api;
public class VimeoApi extends DefaultApi10a public class VimeoApi extends DefaultApi10a
{ {
@Override @Override
public String getAccessTokenEndpoint() protected String getAccessTokenEndpoint()
{ {
return "http://vimeo.com/oauth/access_token"; return "http://vimeo.com/oauth/access_token";
} }
@Override @Override
public String getRequestTokenEndpoint() protected String getRequestTokenEndpoint()
{ {
return "http://vimeo.com/oauth/request_token"; return "http://vimeo.com/oauth/request_token";
} }
@@ -3,13 +3,13 @@ package org.scribe.builder.api;
public class YahooApi extends DefaultApi10a public class YahooApi extends DefaultApi10a
{ {
@Override @Override
public String getAccessTokenEndpoint() protected String getAccessTokenEndpoint()
{ {
return "https://api.login.yahoo.com/oauth/v2/get_token"; return "https://api.login.yahoo.com/oauth/v2/get_token";
} }
@Override @Override
public String getRequestTokenEndpoint() protected String getRequestTokenEndpoint()
{ {
return "https://api.login.yahoo.com/oauth/v2/get_request_token"; return "https://api.login.yahoo.com/oauth/v2/get_request_token";
} }
@@ -1,18 +1,33 @@
package org.scribe.exceptions; package org.scribe.exceptions;
/**
* Default scribe exception.
* Represents a problem in the OAuth signing process
*
* @author Pablo Fernandez
*/
public class OAuthException extends RuntimeException public class OAuthException extends RuntimeException
{ {
/**
* Default constructor
* @param message message explaining what went wrong
* @param e original exception
*/
public OAuthException(String message, Exception e) public OAuthException(String message, Exception e)
{ {
super(message, e); super(message, e);
} }
/**
* No-exception constructor. Used when there is no original exception
*
* @param message message explaining what went wrong
*/
public OAuthException(String message) public OAuthException(String message)
{ {
super(message, null); super(message, null);
} }
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
} }
@@ -2,6 +2,11 @@ package org.scribe.exceptions;
import org.scribe.model.*; import org.scribe.model.*;
/**
* Specialized exception that represents a missing OAuth parameter.
*
* @author Pablo Fernandez
*/
public class OAuthParametersMissingException extends OAuthException public class OAuthParametersMissingException extends OAuthException
{ {
@@ -9,6 +14,11 @@ public class OAuthParametersMissingException extends OAuthException
private static final String MSG = "Could not find oauth parameters in request: %s. " private static final String MSG = "Could not find oauth parameters in request: %s. "
+ "OAuth parameters must be specified with the addOAuthParameter() method"; + "OAuth parameters must be specified with the addOAuthParameter() method";
/**
* Default constructor.
*
* @param request OAuthRequest that caused the error
*/
public OAuthParametersMissingException(OAuthRequest request) public OAuthParametersMissingException(OAuthRequest request)
{ {
super(String.format(MSG, request)); super(String.format(MSG, request));
@@ -1,9 +1,20 @@
package org.scribe.exceptions; package org.scribe.exceptions;
/**
* Specialized exception that represents a problem in the signature
*
* @author Pablo Fernandez
*/
public class OAuthSignatureException extends OAuthException public class OAuthSignatureException extends OAuthException
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* Default constructor
*
* @param stringToSign plain string that gets signed (HMAC-SHA, etc)
* @param e original exception
*/
public OAuthSignatureException(String stringToSign, Exception e) public OAuthSignatureException(String stringToSign, Exception e)
{ {
super("Error while signing string: " + stringToSign, e); super("Error while signing string: " + stringToSign, e);
@@ -3,9 +3,17 @@ package org.scribe.extensions.linkedin;
import org.scribe.extractors.*; import org.scribe.extractors.*;
import org.scribe.model.*; import org.scribe.model.*;
/**
* LinkedIn's specific extractor that does not encode the tilde character.
*
* @author Pablo Fernandez
*/
public class LinkedInBaseStringExtractorImpl extends BaseStringExtractorImpl public class LinkedInBaseStringExtractorImpl extends BaseStringExtractorImpl
{ {
/**
* {@inheritDoc}
*/
@Override @Override
public String extract(OAuthRequest request) public String extract(OAuthRequest request)
{ {
@@ -2,7 +2,18 @@ package org.scribe.extractors;
import org.scribe.model.*; import org.scribe.model.*;
/**
* Simple command object that extracts a {@link Token} from a String
*
* @author Pablo Fernandez
*/
public interface AccessTokenExtractor public interface AccessTokenExtractor
{ {
/**
* Extracts the access token from the contents of an Http Response
*
* @param response the contents of the response
* @return OAuth access token
*/
public Token extract(String response); public Token extract(String response);
} }
@@ -2,7 +2,20 @@ package org.scribe.extractors;
import org.scribe.model.*; import org.scribe.model.*;
/**
* Simple command object that extracts a base string from a {@link OAuthRequest}
*
* @author Pablo Fernandez
*/
public interface BaseStringExtractor public interface BaseStringExtractor
{ {
/**
* Extracts an url-encoded base string from the {@link OAuthRequest}.
*
* See <a href="http://oauth.net/core/1.0/#anchor14">the oauth spec</a> for more info on this.
*
* @param request the OAuthRequest
* @return the url-encoded base string
*/
String extract(OAuthRequest request); String extract(OAuthRequest request);
} }
@@ -6,11 +6,20 @@ import org.scribe.exceptions.*;
import org.scribe.model.*; import org.scribe.model.*;
import org.scribe.utils.*; import org.scribe.utils.*;
/**
* Default implementation of {@link BaseStringExtractor}. Conforms to OAuth 1.0a
*
* @author Pablo Fernandez
*
*/
public class BaseStringExtractorImpl implements BaseStringExtractor public class BaseStringExtractorImpl implements BaseStringExtractor
{ {
private static final String AMPERSAND_SEPARATED_STRING = "%s&%s&%s"; private static final String AMPERSAND_SEPARATED_STRING = "%s&%s&%s";
/**
* {@inheritDoc}
*/
public String extract(OAuthRequest request) public String extract(OAuthRequest request)
{ {
checkPreconditions(request); checkPreconditions(request);
@@ -2,7 +2,18 @@ package org.scribe.extractors;
import org.scribe.model.*; import org.scribe.model.*;
/**
* Simple command object that generates an OAuth Authorization header to include in the request.
*
* @author Pablo Fernandez
*/
public interface HeaderExtractor public interface HeaderExtractor
{ {
/**
* Generates an OAuth 'Authorization' Http header to include in requests as the signature.
*
* @param request the OAuthRequest to inspect and generate the header
* @return the Http header value
*/
String extract(OAuthRequest request); String extract(OAuthRequest request);
} }
@@ -6,11 +6,20 @@ import org.scribe.exceptions.*;
import org.scribe.model.*; import org.scribe.model.*;
import org.scribe.utils.*; import org.scribe.utils.*;
/**
* Default implementation of {@link HeaderExtractor}. Conforms to OAuth 1.0a
*
* @author Pablo Fernandez
*
*/
public class HeaderExtractorImpl implements HeaderExtractor public class HeaderExtractorImpl implements HeaderExtractor
{ {
private static final String PARAM_SEPARATOR = ", "; private static final String PARAM_SEPARATOR = ", ";
private static final String PREAMBLE = "OAuth "; private static final String PREAMBLE = "OAuth ";
/**
* {@inheritDoc}
*/
public String extract(OAuthRequest request) public String extract(OAuthRequest request)
{ {
checkPreconditions(request); checkPreconditions(request);
@@ -2,8 +2,18 @@ package org.scribe.extractors;
import org.scribe.model.*; import org.scribe.model.*;
/**
* Simple command object that extracts a {@link Token} from a String
*
* @author Pablo Fernandez
*/
public interface RequestTokenExtractor public interface RequestTokenExtractor
{ {
/**
* Extracts the request token from the contents of an Http Response
*
* @param response the contents of the response
* @return OAuth access token
*/
public Token extract(String response); public Token extract(String response);
} }
@@ -6,11 +6,20 @@ import org.scribe.exceptions.*;
import org.scribe.model.*; import org.scribe.model.*;
import org.scribe.utils.*; import org.scribe.utils.*;
/**
* Default implementation of {@RequestTokenExtractor} and {@AccessTokenExtractor}. Conforms to OAuth 1.0a
*
* The process for extracting access and request tokens is similar so this class can do both things.
*
* @author Pablo Fernandez
*/
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 String TOKEN_REGEX = "oauth_token=(\\S*)&oauth_token_secret=(\\S*?)(&(.*))?";
/**
* {@inheritDoc}
*/
public Token extract(String response) public Token extract(String response)
{ {
Preconditions.checkEmptyString(response, "Cant extract a token from null object or an empty string."); Preconditions.checkEmptyString(response, "Cant extract a token from null object or an empty string.");
@@ -1,5 +1,10 @@
package org.scribe.model; package org.scribe.model;
/**
* Parameter object that groups OAuth config values
*
* @author Pablo Fernandez
*/
public class OAuthConfig public class OAuthConfig
{ {
private String apiKey; private String apiKey;
@@ -19,7 +19,6 @@ package org.scribe.model;
* This class contains OAuth constants, used project-wide * This class contains OAuth constants, used project-wide
* *
* @author Pablo Fernandez * @author Pablo Fernandez
*
*/ */
public class OAuthConstants public class OAuthConstants
{ {
@@ -38,4 +37,5 @@ public class OAuthConstants
public static final String VERIFIER = "oauth_verifier"; public static final String VERIFIER = "oauth_verifier";
public static final String HEADER = "Authorization"; public static final String HEADER = "Authorization";
public static final Token EMPTY_TOKEN = new Token("", ""); public static final Token EMPTY_TOKEN = new Token("", "");
public static final String SCOPE = "scope";
} }
@@ -1,6 +0,0 @@
package org.scribe.model;
public class OAuthParams
{
}
@@ -2,18 +2,38 @@ package org.scribe.model;
import java.util.*; import java.util.*;
/**
* The representation of an OAuth HttpRequest.
*
* Adds OAuth-related functionality to the {@link Request}
*
* @author Pablo Fernandez
*/
public class OAuthRequest extends Request public class OAuthRequest extends Request
{ {
private static final String OAUTH_PREFIX = "oauth_"; private static final String OAUTH_PREFIX = "oauth_";
private Map<String, String> oauthParameters; private Map<String, String> oauthParameters;
/**
* Default constructor.
*
* @param verb Http verb/method
* @param url resource URL
*/
public OAuthRequest(Verb verb, String url) public OAuthRequest(Verb verb, String url)
{ {
super(verb, url); super(verb, url);
this.oauthParameters = new HashMap<String, String>(); this.oauthParameters = new HashMap<String, String>();
} }
/**
* Adds an OAuth parameter.
*
* @param key name of the parameter
* @param value value of the parameter
*
* @throws IllegalArgumentException if the parameter is not an OAuth parameter
*/
public void addOAuthParameter(String key, String value) public void addOAuthParameter(String key, String value)
{ {
oauthParameters.put(checkKey(key), value); oauthParameters.put(checkKey(key), value);
@@ -21,15 +41,20 @@ public class OAuthRequest extends Request
private String checkKey(String key) private String checkKey(String key)
{ {
if (!key.startsWith(OAUTH_PREFIX)) if (!key.startsWith(OAUTH_PREFIX) || key.equals(OAuthConstants.SCOPE))
{ {
throw new IllegalArgumentException(String.format("OAuth parameters must start with '%s'", OAUTH_PREFIX)); throw new IllegalArgumentException(String.format("OAuth parameters must either be %s or start with '%s'", OAuthConstants.SCOPE, OAUTH_PREFIX));
} else } else
{ {
return key; return key;
} }
} }
/**
* Returns the {@link Map} containing the key-value pair of parameters.
*
* @return parameters as map
*/
public Map<String, String> getOauthParameters() public Map<String, String> getOauthParameters()
{ {
return oauthParameters; return oauthParameters;
@@ -14,7 +14,6 @@ import org.scribe.utils.*;
*/ */
class Request class Request
{ {
private static final String CONTENT_LENGTH = "Content-Length"; private static final String CONTENT_LENGTH = "Content-Length";
private String url; private String url;
@@ -13,7 +13,6 @@ import org.scribe.utils.*;
*/ */
public class Response public class Response
{ {
private static final String EMPTY = ""; private static final String EMPTY = "";
private int code; private int code;
@@ -52,11 +51,6 @@ public class Response
return headers; return headers;
} }
/**
* Checks if the code is in the successful or redirect range.
*
* @return true if successful
*/
private boolean wasSuccessful() private boolean wasSuccessful()
{ {
return getCode() >= 200 && getCode() < 400; return getCode() >= 200 && getCode() < 400;
+11 -1
View File
@@ -1,11 +1,21 @@
package org.scribe.model; package org.scribe.model;
/**
* Represents an OAuth token (either request or access token) and its secret
*
* @author Pablo Fernandez
*/
public class Token public class Token
{ {
private final String token; private final String token;
private final String secret; private final String secret;
/**
* Default constructor
*
* @param token token value
* @param secret token secret
*/
public Token(String token, String secret) public Token(String token, String secret)
{ {
this.token = token; this.token = token;
+10 -1
View File
@@ -1,10 +1,19 @@
package org.scribe.model; package org.scribe.model;
/**
* Represents an OAuth verifier code.
*
* @author Pablo Fernandez
*/
public class Verifier public class Verifier
{ {
private final String value; private final String value;
/**
* Default constructor.
*
* @param value verifier value
*/
public Verifier(String value) public Verifier(String value)
{ {
this.value = value; this.value = value;
@@ -4,9 +4,14 @@ import org.scribe.extractors.*;
import org.scribe.model.*; import org.scribe.model.*;
import org.scribe.services.*; import org.scribe.services.*;
/**
* OAuth 1.0a implementation of {@link OAuthService}
*
* @author Pablo Fernandez
*/
public class OAuth10aServiceImpl implements OAuthService public class OAuth10aServiceImpl implements OAuthService
{ {
private static final String NO_SCOPE = null;
private static final String VERSION = "1.0"; private static final String VERSION = "1.0";
private OAuthConfig config; private OAuthConfig config;
@@ -16,7 +21,19 @@ public class OAuth10aServiceImpl implements OAuthService
private HeaderExtractor headerExtractor; private HeaderExtractor headerExtractor;
private RequestTokenExtractor rtExtractor; private RequestTokenExtractor rtExtractor;
private AccessTokenExtractor atExtractor; private AccessTokenExtractor atExtractor;
private String scope;
/**
* Default constructor
*
* @param signatureService OAuth 1.0a signature service
* @param timestampService OAuth 1.0a timestamp service
* @param baseStringExtractor OAuth 1.0a base string extractor
* @param headerExtractor OAuth 1.0a http header extractor
* @param rtExtractor OAuth 1.0a request token extractor
* @param atExtractor OAuth 1.0a access token extractor
* @param config OAuth 1.0a configuration param object
*/
public OAuth10aServiceImpl(SignatureService signatureService, TimestampService timestampService, BaseStringExtractor baseStringExtractor, public OAuth10aServiceImpl(SignatureService signatureService, TimestampService timestampService, BaseStringExtractor baseStringExtractor,
HeaderExtractor headerExtractor, RequestTokenExtractor rtExtractor, AccessTokenExtractor atExtractor, OAuthConfig config) HeaderExtractor headerExtractor, RequestTokenExtractor rtExtractor, AccessTokenExtractor atExtractor, OAuthConfig config)
{ {
@@ -27,8 +44,12 @@ public class OAuth10aServiceImpl implements OAuthService
this.rtExtractor = rtExtractor; this.rtExtractor = rtExtractor;
this.atExtractor = atExtractor; this.atExtractor = atExtractor;
this.config = config; this.config = config;
this.scope = NO_SCOPE;
} }
/**
* {@inheritDoc}
*/
public Token getRequestToken() public Token getRequestToken()
{ {
OAuthRequest request = new OAuthRequest(config.getRequestTokenVerb(), config.getRequestTokenEndpoint()); OAuthRequest request = new OAuthRequest(config.getRequestTokenVerb(), config.getRequestTokenEndpoint());
@@ -46,9 +67,13 @@ public class OAuth10aServiceImpl implements OAuthService
request.addOAuthParameter(OAuthConstants.SIGN_METHOD, signatureService.getSignatureMethod()); request.addOAuthParameter(OAuthConstants.SIGN_METHOD, signatureService.getSignatureMethod());
request.addOAuthParameter(OAuthConstants.VERSION, getVersion()); request.addOAuthParameter(OAuthConstants.VERSION, getVersion());
request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback()); request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback());
if(scope != NO_SCOPE) request.addOAuthParameter(OAuthConstants.SCOPE, scope);
request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, token)); request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, token));
} }
/**
* {@inheritDoc}
*/
public Token getAccessToken(Token requestToken, Verifier verifier) public Token getAccessToken(Token requestToken, Verifier verifier)
{ {
OAuthRequest request = new OAuthRequest(config.getAccessTokenVerb(), config.getAccessTokenEndpoint()); OAuthRequest request = new OAuthRequest(config.getAccessTokenVerb(), config.getAccessTokenEndpoint());
@@ -60,6 +85,9 @@ public class OAuth10aServiceImpl implements OAuthService
return atExtractor.extract(response.getBody()); return atExtractor.extract(response.getBody());
} }
/**
* {@inheritDoc}
*/
public void signRequest(Token token, OAuthRequest request) public void signRequest(Token token, OAuthRequest request)
{ {
request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken()); request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken());
@@ -67,11 +95,22 @@ public class OAuth10aServiceImpl implements OAuthService
addOAuthHeader(request); addOAuthHeader(request);
} }
/**
* {@inheritDoc}
*/
public String getVersion() public String getVersion()
{ {
return VERSION; return VERSION;
} }
/**
* {@inheritDoc}
*/
public void addScope(String scope)
{
this.scope = scope;
}
private String getSignature(OAuthRequest request, Token token) private String getSignature(OAuthRequest request, Token token)
{ {
String baseString = baseStringExtractor.extract(request); String baseString = baseStringExtractor.extract(request);
@@ -2,13 +2,50 @@ package org.scribe.oauth;
import org.scribe.model.*; import org.scribe.model.*;
/**
* The main Scribe object.
*
* A facade responsible for the retrieval of request and access tokens and for the signing of HTTP requests.
*
* @author Pablo Fernandez
*/
public interface OAuthService public interface OAuthService
{ {
/**
* Retrieve the request token.
*
* @return request token
*/
public Token getRequestToken(); public Token getRequestToken();
/**
* Retrieve the access token
*
* @param requestToken request token (obtained previously)
* @param verifier verifier code
* @return access token
*/
public Token getAccessToken(Token requestToken, Verifier verifier); public Token getAccessToken(Token requestToken, Verifier verifier);
/**
* Signs am OAuth request
*
* @param accessToken access token (obtained previously)
* @param request request to sign
*/
public void signRequest(Token accessToken, OAuthRequest request); public void signRequest(Token accessToken, OAuthRequest request);
/**
* Returns the OAuth version of the service.
*
* @return oauth version as string
*/
public String getVersion(); public String getVersion();
/**
* Adds the 'scope' parameter. This is **not** a default OAuth parameter and the setting is optional.
*
* @param scope OAuth Api scope (optional)
*/
public void addScope(String scope);
} }
@@ -7,15 +7,23 @@ import org.apache.commons.codec.binary.*;
import org.scribe.exceptions.*; import org.scribe.exceptions.*;
import org.scribe.utils.*; import org.scribe.utils.*;
/**
* HMAC-SHA1 implementation of {@SignatureService}
*
* @author Pablo Fernandez
*
*/
public class HMACSha1SignatureService implements SignatureService public class HMACSha1SignatureService implements SignatureService
{ {
private static final String EMPTY_STRING = ""; private static final String EMPTY_STRING = "";
private static final String CARRIAGE_RETURN = "\r\n"; private static final String CARRIAGE_RETURN = "\r\n";
private static final String UTF8 = "UTF-8"; private static final String UTF8 = "UTF-8";
private static final String HMAC_SHA1 = "HmacSHA1"; private static final String HMAC_SHA1 = "HmacSHA1";
private static final String METHOD = "HMAC-SHA1"; private static final String METHOD = "HMAC-SHA1";
/**
* {@inheritDoc}
*/
public String getSignature(String baseString, String apiSecret, String tokenSecret) public String getSignature(String baseString, String apiSecret, String tokenSecret)
{ {
try try
@@ -39,6 +47,9 @@ public class HMACSha1SignatureService implements SignatureService
return new String(Base64.encodeBase64(bytes)).replace(CARRIAGE_RETURN, EMPTY_STRING); return new String(Base64.encodeBase64(bytes)).replace(CARRIAGE_RETURN, EMPTY_STRING);
} }
/**
* {@inheritDoc}
*/
public String getSignatureMethod() public String getSignatureMethod()
{ {
return METHOD; return METHOD;
@@ -1,8 +1,28 @@
package org.scribe.services; package org.scribe.services;
/**
* Signs a base string, returning the OAuth signature
*
* @author Pablo Fernandez
*
*/
public interface SignatureService public interface SignatureService
{ {
/**
* Returns the signature
*
* @param baseString url-encoded string to sign
* @param apiSecret api secret for your app
* @param tokenSecret token secret (empty string for the request token step)
*
* @return signature
*/
public String getSignature(String baseString, String apiSecret, String tokenSecret); public String getSignature(String baseString, String apiSecret, String tokenSecret);
/**
* Returns the signature method/algorithm
*
* @return
*/
public String getSignatureMethod(); public String getSignatureMethod();
} }
@@ -1,9 +1,25 @@
package org.scribe.services; package org.scribe.services;
/**
* Unix epoch timestamp generator.
*
* This class is useful for stubbing in tests.
*
* @author Pablo Fernandez
*/
public interface TimestampService public interface TimestampService
{ {
/**
* Returns the unix epoch timestamp in seconds
*
* @return timestamp
*/
public String getTimestampInSeconds(); public String getTimestampInSeconds();
/**
* Returns a nonce (unique value for each request)
*
* @return nonce
*/
public String getNonce(); public String getNonce();
} }
@@ -2,22 +2,35 @@ package org.scribe.services;
import java.util.*; import java.util.*;
/**
* Implementation of {@link TimestampService} using plain java classes.
*
* @author Pablo Fernandez
*/
public class TimestampServiceImpl implements TimestampService public class TimestampServiceImpl implements TimestampService
{ {
private Timer timer; private Timer timer;
/**
* Default constructor.
*/
public TimestampServiceImpl() public TimestampServiceImpl()
{ {
timer = new Timer(); timer = new Timer();
} }
/**
* {@inheritDoc}
*/
public String getNonce() public String getNonce()
{ {
Long ts = getTs(); Long ts = getTs();
return String.valueOf(ts + timer.getRandomInteger()); return String.valueOf(ts + timer.getRandomInteger());
} }
/**
* {@inheritDoc}
*/
public String getTimestampInSeconds() public String getTimestampInSeconds()
{ {
return String.valueOf(getTs()); return String.valueOf(getTs());
@@ -33,14 +46,19 @@ public class TimestampServiceImpl implements TimestampService
this.timer = timer; this.timer = timer;
} }
/**
* Inner class that uses {@link System} for generating the timestamps.
*
* @author Pablo Fernandez
*/
static class Timer static class Timer
{ {
public Long getMilis() Long getMilis()
{ {
return System.currentTimeMillis(); return System.currentTimeMillis();
} }
public Integer getRandomInteger() Integer getRandomInteger()
{ {
return new Random().nextInt(); return new Random().nextInt();
} }
+11 -1
View File
@@ -2,9 +2,19 @@ package org.scribe.utils;
import java.util.*; import java.util.*;
/**
* Utils for {@link Map} manipulation
*
* @author Pablo Fernandez
*/
public class MapUtils public class MapUtils
{ {
/**
* Sorts a Map
*
* @param map unsorted map
* @return sorted map
*/
public static final Map<String, String> sort(Map<String, String> map) public static final Map<String, String> sort(Map<String, String> map)
{ {
Preconditions.checkNotNull(map, "Cannot sort a null object."); Preconditions.checkNotNull(map, "Cannot sort a null object.");
@@ -4,27 +4,60 @@ import java.util.regex.Pattern;
import org.scribe.model.OAuthConstants; import org.scribe.model.OAuthConstants;
/**
* Utils for checking preconditions and invariants
*
* @author Pablo Fernandez
*/
public class Preconditions public class Preconditions
{ {
private static final String DEFAULT_MESSAGE = "Received an invalid parameter"; private static final String DEFAULT_MESSAGE = "Received an invalid parameter";
private static final Pattern URL_PATTERN = Pattern.compile("[a-zA-Z_-]+://\\S+"); private static final Pattern URL_PATTERN = Pattern.compile("[a-zA-Z_-]+://\\S+");
/**
* Checks that an object is not null.
*
* @param object any object
* @param errorMsg error message
*
* @throws IllegalArgumentException if the object is null
*/
public static void checkNotNull(Object object, String errorMsg) public static void checkNotNull(Object object, String errorMsg)
{ {
check(object != null, errorMsg); check(object != null, errorMsg);
} }
/**
* Checks that a string is not null or empty
*
* @param string any string
* @param errorMsg error message
*
* @throws IllegalArgumentException if the string is null or empty
*/
public static void checkEmptyString(String string, String errorMsg) public static void checkEmptyString(String string, String errorMsg)
{ {
check(string != null && !string.trim().equals(""), errorMsg); check(string != null && !string.trim().equals(""), errorMsg);
} }
/**
* Checks that a URL is valid
*
* @param url any string
* @param errorMsg error message
*/
public static void checkValidUrl(String url, String errorMsg) public static void checkValidUrl(String url, String errorMsg)
{ {
checkEmptyString(url, errorMsg); checkEmptyString(url, errorMsg);
check(isUrl(url), errorMsg); check(isUrl(url), errorMsg);
} }
/**
* Checks that a URL is a valid OAuth callback
*
* @param url any string
* @param errorMsg error message
*/
public static void checkValidOAuthCallback(String url, String errorMsg) public static void checkValidOAuthCallback(String url, String errorMsg)
{ {
checkEmptyString(url, errorMsg); checkEmptyString(url, errorMsg);
@@ -34,7 +67,7 @@ public class Preconditions
} }
} }
static boolean isUrl(String url) private static boolean isUrl(String url)
{ {
return URL_PATTERN.matcher(url).matches(); return URL_PATTERN.matcher(url).matches();
} }
@@ -2,9 +2,19 @@ package org.scribe.utils;
import java.io.*; import java.io.*;
/**
* Utils to deal with Streams.
*
* @author Pablo Fernandez
*/
public class StreamUtils public class StreamUtils
{ {
/**
* Returns the stream contents as an UTF-8 encoded string
*
* @param is input stream
* @return string contents
*/
public static String getStreamContents(InputStream is) public static String getStreamContents(InputStream is)
{ {
Preconditions.checkNotNull(is, "Cannot get String from a null object"); Preconditions.checkNotNull(is, "Cannot get String from a null object");
+23 -1
View File
@@ -6,9 +6,13 @@ import java.util.*;
import org.scribe.exceptions.*; import org.scribe.exceptions.*;
/**
* Utils to deal with URL and url-encodings
*
* @author Pablo Fernandez
*/
public class URLUtils 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 char PAIR_SEPARATOR = '=';
@@ -16,6 +20,12 @@ public class URLUtils
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);
/**
* Turns a map into a form-url-encoded string (key=value&key2=value2)
*
* @param map any map
* @return form-url-encoded string
*/
public static String formURLEncodeMap(Map<String, String> map) public static String formURLEncodeMap(Map<String, String> map)
{ {
Preconditions.checkNotNull(map, "Cannot url-encode a null object"); Preconditions.checkNotNull(map, "Cannot url-encode a null object");
@@ -36,6 +46,12 @@ public class URLUtils
return encodedString.toString(); return encodedString.toString();
} }
/**
* Percent encodes a string
*
* @param plain
* @return percent encoded string
*/
public static String percentEncode(String string) public static String percentEncode(String string)
{ {
Preconditions.checkNotNull(string, "Cannot encode null string"); Preconditions.checkNotNull(string, "Cannot encode null string");
@@ -49,6 +65,12 @@ public class URLUtils
} }
} }
/**
* Percent decodes a string
*
* @param string percent encoded string
* @return plain string
*/
public static String percentDecode(String string) public static String percentDecode(String string)
{ {
Preconditions.checkNotNull(string, "Cannot decode null string"); Preconditions.checkNotNull(string, "Cannot decode null string");