62 Commits
Author SHA1 Message Date
Pablo Fernandez 380d858b13 Changed version number for release 2011-01-16 12:45:26 -03:00
Pablo Fernandez 9c4f5bc337 Refactored OOB checking in fbook api 2011-01-13 23:50:36 -03:00
Pablo Fernandez 09ee10dbd1 Added builder tests 2011-01-13 23:50:32 -03:00
Pablo Fernandez 690b9d4e21 (minor) removed specific import 2011-01-12 21:46:27 -03:00
diegossilveira 080dd962a9 Encoding the callback url 2011-01-12 18:05:50 -02:00
Pablo Fernandez 40bb525374 Added url params before making the connection
Renamed method to keep consistency
2011-01-10 15:04:26 -03:00
Pablo Fernandez 8e23e81c57 Explicited that you need a personal pair of keys to use Facebook's example 2011-01-09 21:37:44 -03:00
Pablo Fernandez 1f055d7eeb Minor exception handling refactoring 2011-01-09 16:10:00 -03:00
Pablo Fernandez 1f36fd0f32 Added Facebook Api and example 2011-01-09 15:58:58 -03:00
Pablo Fernandez 9ea686a3f8 Added OAuth 2.0 classes 2011-01-09 15:51:34 -03:00
Pablo Fernandez 7e92db3413 Added functionallity for appending querystring params once the request is created 2011-01-09 15:44:14 -03:00
Pablo Fernandez f6c3cb9bdb Lazy http connection creation 2011-01-09 15:34:19 -03:00
Pablo Fernandez 088fc5e443 Renamed OAuth 2.0 service class 2011-01-09 14:43:56 -03:00
Pablo Fernandez 27bac50073 Added append parameters to URL method 2011-01-09 14:14:08 -03:00
Pablo Fernandez 6fc5146706 Added Diego to list of developers 2011-01-09 13:56:34 -03:00
Pablo Fernandez 0e5ae92d3e Added exceptions to both token methods on 2.0 service
Rebased 1.0.9
2011-01-07 13:24:18 -03:00
Pablo Fernandez c145ee8842 Added Evernote playground api 2011-01-07 13:15:29 -03:00
Pablo Fernandez bdfd14309f Added some more apis to the readme 2011-01-07 13:15:29 -03:00
Pablo Fernandez 82f03604c3 Added OAuth2 service props 2011-01-07 13:15:29 -03:00
Pablo Fernandez 5ac849313d Added authorization URL to the Apis 2011-01-07 13:15:26 -03:00
Pablo Fernandez c12da16a38 Made 2.0 impl fit the internal refactor 2011-01-07 13:10:43 -03:00
Pablo Fernandez b78ca90df4 Added plumbing code for 2.0 2011-01-07 13:10:43 -03:00
Pablo Fernandez 577c18c752 corrected javadocs to reflect changes 2011-01-07 13:10:43 -03:00
Pablo Fernandez 41cbee06d7 Minor name refactor 2011-01-07 13:10:43 -03:00
Pablo Fernandez 8eb2062e95 Used OAuthConfig when possible instead of bunch of Strings 2011-01-07 13:10:43 -03:00
Pablo Fernandez 8f0760664f Removed endpoints and verbs from config (they are in the Api object anyways) 2011-01-07 13:10:42 -03:00
Pablo Fernandez 0be82d6832 OAuth 1.0a service refactor to remove many properties 2011-01-07 13:10:39 -03:00
Pablo Fernandez d0fa8689cb Added changes to changelog and version to pom 2010-11-30 15:05:10 -03:00
roger 896e738156 Signature test fixed. New reference signature as api & token secret must be urlencoded. 2010-11-30 15:01:12 -03:00
roger cf84381127 Added URLDecoding to verifier 2010-11-30 14:49:47 -03:00
klakegg 806d548629 Parameters scope and oauth_callback are needed in the request_token step only. 2010-11-30 14:25:42 -03:00
Roger 959c093620 keyString for SecretKey must be encoded 2010-11-30 14:10:57 -03:00
Pablo Fernandez fc2c46bd3a Changed GoogleApi to use GET 2010-11-23 17:35:42 -03:00
klakegg f1ecabdb52 Token implements Serializable - nice to have when creating web service. 2010-11-23 17:29:24 -03:00
Pablo Fernandez 83f17b0652 Changed version number for release 2010-11-20 19:49:13 -03:00
Pablo Fernandez ebe0ff9eda Fixed failing tests 2010-11-20 19:46:59 -03:00
Pablo Fernandez 6e141ba6a2 Added Google Example 2010-11-20 19:42:49 -03:00
Pablo Fernandez 23af69cbf8 Minor fixes to Request javadoc 2010-11-20 19:42:12 -03:00
Pablo Fernandez c0d24b556c Refactored URLEncoing code 2010-11-20 19:27:04 -03:00
Pablo Fernandez 99a03b4679 Improved TokenExtractor error messages 2010-11-20 18:31:11 -03:00
Pablo Fernandez 9337ac01d9 Fixed EvernoteApi to use GET 2010-11-06 20:03:13 -03:00
Pablo Fernandez dee0f55b1e Updated changelog 2010-11-06 19:56:40 -03:00
Pablo Fernandez 377c3233d9 Added TimeUnit for the timeout calls 2010-11-06 19:54:47 -03:00
Pablo Fernandez 029125490d Merge https://github.com/jamietsao/scribe-java 2010-11-06 19:38:39 -03:00
Pablo Fernandez 20b200c7bc Changed Twitter OAuth endpoints to use the new api.twitter.com 2010-11-06 19:09:28 -03:00
Jamie Tsao 3e7291eaee Added methods to allow for setting the connect and read timeouts on the underlying HttpURLConnection 2010-11-04 15:23:19 -04:00
Pablo Fernandez 149756e5ed Added test for scope parameter 2010-10-27 15:35:25 -03:00
Pablo Fernandez 9cb2af37c7 Fixed conditional 2010-10-27 15:27:35 -03:00
Pablo Fernandez ed0970c8cb Added Changelog 2010-10-27 15:12:31 -03:00
Pablo Fernandez 58991e81ef Changed revision number for release 2010-10-27 15:06:41 -03:00
Owen db27be9e2b Added Evernote and Yammer APIs 2010-10-27 10:54:13 -03:00
Pablo Fernandez 874e959e09 Fixed bug in scope for Google 2010-10-25 12:11:50 -03:00
Pablo Fernandez 545867ca1d Added maven info to homepage 2010-10-25 09:22:11 -03:00
Pablo Fernandez 8bd1e94287 Changed version number for deploy 2010-10-24 17:37:08 -03:00
Pablo Fernandez 843d9feddc Removed unused file 2010-10-24 12:30:08 -03:00
Pablo Fernandez 9b85863edf Added some conf needed for repo publishing and set Junit dep to 'test' scope 2010-10-24 12:29:20 -03:00
Pablo Fernandez 59f16bfdda Added bunch of javadocs and restricted some visibility.
No functional changes
2010-10-24 11:48:21 -03:00
Pablo Fernandez aad8dac580 Made get signature the last call 2010-10-20 17:10:11 -03:00
Pablo Fernandez 50b54fbfb6 Relaxed check to allow 'scope' parameter 2010-10-20 17:06:03 -03:00
Pablo Fernandez 5d86982421 Added check for invalid scope 2010-10-20 10:43:38 -03:00
Pablo Fernandez 1f94997120 Added support for Google's 'scope' param 2010-10-20 10:25:26 -03:00
Pablo Fernandez c3f113f39b Added required config for publishing bundle to maven central 2010-10-20 10:25:03 -03:00
61 changed files with 1356 additions and 226 deletions

No files matched your search

+16
View File
@@ -32,6 +32,12 @@ h3. Supports all major OAuth APIs out-of-the-box
* Foursquare * Foursquare
* Evernote
* Vimeo
* Yammer
* and many more! check the "examples folder":http://github.com/fernandezpablo85/scribe-java/tree/master/src/test/java/org/scribe/examples * and many more! check the "examples folder":http://github.com/fernandezpablo85/scribe-java/tree/master/src/test/java/org/scribe/examples
h3. Small and modular h3. Small and modular
@@ -48,6 +54,16 @@ Good test coverage to keep you safe from harm.
When something bad actually happens, Scribe's meaningful error messages will tell you exactly what went wrong, when and where. When something bad actually happens, Scribe's meaningful error messages will tell you exactly what went wrong, when and where.
h3. Pull it from Maven! (new)
You can pull scribe from a maven repository, just add this to your __pom.xml__ file:
@<dependency>@
@<groupId>org.scribe</groupId>@
@<artifactId>scribe</artifactId>@
@<version>1.0.6</version>@
@</dependency>@
h1. Getting started in less than 2 minutes h1. Getting started in less than 2 minutes
Check the "Getting Started":http://wiki.github.com/fernandezpablo85/scribe-java/getting-started page and start rocking! Check the "Getting Started":http://wiki.github.com/fernandezpablo85/scribe-java/getting-started page and start rocking!
+26
View File
@@ -0,0 +1,26 @@
[1.0.6]
* FEATURE: Available in maven central repositories.
[1.0.7]
* FEATURE: Added support for Yammer and Evernote ( thanks oparrsih )
* FIX: Fixed bug that preventing setting the scope param on Google Api
[1.0.8]
* FEATURE: Added setConnectTimeout and setReadTimeout for the Request Object
* FIX: Fixed Evernote Api (uses GET for request and access tokens)
* REFACTOR: URLEncoding stuff. LinkedIn specific decoder is no longer needed
* FEATURE: Added Google example
[1.0.9]
* FEATURE: Token made Serializable
* FIX: Google Api encoding issues (thanks @roger and @klakegg)
[1.1.0]
* FEATURE: OAuth 2.0 Support and Facebook support out of the box (thanks Diego Silveira)
* REFACTOR: Api creation and OAuthService refactors
* REFACTOR: Connections are created lazily (just before sending the request)
+52 -2
View File
@@ -4,8 +4,44 @@
<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.1.0</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>
<developer>
<id>diegossilveira</id>
<name>Diego Silveira</name>
<email>diegossilveira@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 +54,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 +67,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>
@@ -2,7 +2,6 @@ package org.scribe.builder;
import org.scribe.builder.api.*; import org.scribe.builder.api.*;
import org.scribe.exceptions.*; import org.scribe.exceptions.*;
import org.scribe.model.*; import org.scribe.model.*;
import org.scribe.oauth.*; import org.scribe.oauth.*;
import org.scribe.utils.*; import org.scribe.utils.*;
@@ -20,7 +19,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 +95,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 +118,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(new OAuthConfig(apiKey, apiSecret, callback), scope);
} }
} }
@@ -1,5 +1,6 @@
package org.scribe.builder.api; package org.scribe.builder.api;
import org.scribe.model.*;
import org.scribe.oauth.*; import org.scribe.oauth.*;
/** /**
@@ -16,8 +17,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(OAuthConfig config, String scope);
} }
@@ -5,74 +5,144 @@ 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,
* providing the endpoints and endpoint-http-verbs.
*
* If your Api adheres to the 1.0a protocol correctly, you just need to extend
* this class and define the getters for your endpoints.
*
* If your Api does something a bit different, you can override the different
* extractors or services, in order to fine-tune the process. Please read the
* javadocs of the interfaces to get an idea of what to do.
*
* @author Pablo Fernandez
*
*/
public abstract class DefaultApi10a implements Api public abstract class DefaultApi10a implements Api
{ {
/**
* Returns the access token extractor.
*
* @return access token extractor
*/
public AccessTokenExtractor getAccessTokenExtractor() public AccessTokenExtractor getAccessTokenExtractor()
{ {
return new TokenExtractorImpl(); return new TokenExtractorImpl();
} }
/**
* Returns the base string extractor.
*
* @return base string extractor
*/
public BaseStringExtractor getBaseStringExtractor() public BaseStringExtractor getBaseStringExtractor()
{ {
return new BaseStringExtractorImpl(); return new BaseStringExtractorImpl();
} }
/**
* Returns the header extractor.
*
* @return header extractor
*/
public HeaderExtractor getHeaderExtractor() public HeaderExtractor getHeaderExtractor()
{ {
return new HeaderExtractorImpl(); return new HeaderExtractorImpl();
} }
/**
* Returns the request token extractor.
*
* @return request token extractor
*/
public RequestTokenExtractor getRequestTokenExtractor() public RequestTokenExtractor getRequestTokenExtractor()
{ {
return new TokenExtractorImpl(); return new TokenExtractorImpl();
} }
/**
* Returns the signature service.
*
* @return signature service
*/
public SignatureService getSignatureService() public SignatureService getSignatureService()
{ {
return new HMACSha1SignatureService(); return new HMACSha1SignatureService();
} }
/**
* Returns the timestamp service.
*
* @return timestamp service
*/
public TimestampService getTimestampService() public TimestampService getTimestampService()
{ {
return new TimestampServiceImpl(); return new TimestampServiceImpl();
} }
/**
* Returns the verb for the access token endpoint (defaults to POST)
*
* @return access token endpoint verb
*/
public Verb getAccessTokenVerb() public Verb getAccessTokenVerb()
{ {
return Verb.POST; return Verb.POST;
} }
/**
* Returns the verb for the request token endpoint (defaults to POST)
*
* @return request token endpoint verb
*/
public Verb getRequestTokenVerb() public Verb getRequestTokenVerb()
{ {
return Verb.POST; return Verb.POST;
} }
/**
* Returns the URL that receives the request token requests.
*
* @return request token URL
*/
public abstract String getRequestTokenEndpoint(); public abstract String getRequestTokenEndpoint();
/**
* Returns the URL that receives the access token requests.
*
* @return access token URL
*/
public abstract String getAccessTokenEndpoint(); public abstract String getAccessTokenEndpoint();
@Override /**
public OAuthService createService(String apiKey, String apiSecret, String callback) * Returns the URL where you should redirect your users to authenticate
* your application.
*
* @param requestToken the request token you need to authorize
* @return the URL where you should redirect your users
*/
public abstract String getAuthorizationUrl(Token requestToken);
/**
* 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(OAuthConfig config, String scope)
{ {
return new OAuth10aServiceImpl( getSignatureService(), OAuthService service = doCreateService(config);
getTimestampService(), service.addScope(scope);
getBaseStringExtractor(), return service;
getHeaderExtractor(),
getRequestTokenExtractor(),
getAccessTokenExtractor(),
createConfig(apiKey, apiSecret, callback));
} }
private OAuthConfig createConfig(String apiKey, String apiSecret, String callback) private OAuthService doCreateService(OAuthConfig config)
{ {
OAuthConfig config = new OAuthConfig(); return new OAuth10aServiceImpl(this, config);
config.setRequestTokenVerb(getRequestTokenVerb());
config.setRequestTokenEndpoint(getRequestTokenEndpoint());
config.setAccessTokenVerb(getAccessTokenVerb());
config.setAccessTokenEndpoint(getAccessTokenEndpoint());
config.setApiKey(apiKey);
config.setApiSecret(apiSecret);
config.setCallback(callback);
return config;
} }
} }
@@ -0,0 +1,42 @@
package org.scribe.builder.api;
import org.scribe.model.*;
import org.scribe.oauth.*;
/**
* Default implementation of the OAuth protocol, version 2.0 (draft 11)
*
* This class is meant to be extended by concrete implementations of the API,
* providing the endpoints and endpoint-http-verbs.
*
* If your Api adheres to the 2.0 (draft 11) protocol correctly, you just need to extend
* this class and define the getters for your endpoints.
*
* If your Api does something a bit different, you can override the different
* extractors or services, in order to fine-tune the process. Please read the
* javadocs of the interfaces to get an idea of what to do.
*
* @author Diego Silveira
*
*/
public abstract class DefaultApi20 implements Api
{
/**
* Returns the URL where you should redirect your users to authenticate
* your application.
*
* @param config OAuth 2.0 configuration param object
* @return the URL where you should redirect your users
*/
public abstract String getAuthorizationUrl(OAuthConfig config);
/**
* {@inheritDoc}
*/
public OAuthService createService(OAuthConfig config, String scope)
{
return new OAuth20ServiceImpl(this, config);
}
}
@@ -0,0 +1,62 @@
package org.scribe.builder.api;
import org.scribe.model.Token;
import org.scribe.model.Verb;
public class EvernoteApi extends DefaultApi10a
{
private static final String AUTHORIZATION_URL = "https://www.evernote.com/OAuth.action?oauth_token=%s";
@Override
public Verb getRequestTokenVerb()
{
return Verb.GET;
}
@Override
public String getRequestTokenEndpoint()
{
return "https://www.evernote.com/oauth";
}
@Override
public Verb getAccessTokenVerb()
{
return Verb.GET;
}
@Override
public String getAccessTokenEndpoint()
{
return "https://www.evernote.com/oauth";
}
@Override
public String getAuthorizationUrl(Token requestToken)
{
return String.format(AUTHORIZATION_URL, requestToken.getToken());
}
public static class Sandbox extends EvernoteApi
{
private static final String SANDBOX_URL = "https://sandbox.evernote.com/oauth";
@Override
public String getRequestTokenEndpoint()
{
return SANDBOX_URL;
}
@Override
public String getAccessTokenEndpoint()
{
return SANDBOX_URL;
}
@Override
public String getAuthorizationUrl(Token requestToken)
{
return String.format(SANDBOX_URL + "?oauth_token=%s", requestToken.getToken());
}
}
}
@@ -0,0 +1,16 @@
package org.scribe.builder.api;
import org.scribe.model.*;
import org.scribe.utils.*;
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";
@Override
public String getAuthorizationUrl(OAuthConfig config)
{
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()));
}
}
@@ -1,7 +1,11 @@
package org.scribe.builder.api; package org.scribe.builder.api;
import org.scribe.model.Token;
public class FoursquareApi extends DefaultApi10a public class FoursquareApi extends DefaultApi10a
{ {
private static final String AUTHORIZATION_URL = "http://foursquare.com/oauth/authorize?oauth_token=%s";
@Override @Override
public String getAccessTokenEndpoint() public String getAccessTokenEndpoint()
{ {
@@ -13,4 +17,10 @@ public class FoursquareApi extends DefaultApi10a
{ {
return "http://foursquare.com/oauth/request_token"; return "http://foursquare.com/oauth/request_token";
} }
@Override
public String getAuthorizationUrl(Token requestToken)
{
return String.format(AUTHORIZATION_URL, requestToken.getToken());
}
} }
@@ -1,7 +1,11 @@
package org.scribe.builder.api; package org.scribe.builder.api;
import org.scribe.model.*;
public class GoogleApi extends DefaultApi10a public class GoogleApi extends DefaultApi10a
{ {
private static final String AUTHORIZATION_URL = "https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=%s";
@Override @Override
public String getAccessTokenEndpoint() public String getAccessTokenEndpoint()
{ {
@@ -13,4 +17,22 @@ public class GoogleApi extends DefaultApi10a
{ {
return "https://www.google.com/accounts/OAuthGetRequestToken"; return "https://www.google.com/accounts/OAuthGetRequestToken";
} }
@Override
public Verb getAccessTokenVerb()
{
return Verb.GET;
}
@Override
public Verb getRequestTokenVerb()
{
return Verb.GET;
}
@Override
public String getAuthorizationUrl(Token requestToken)
{
return String.format(AUTHORIZATION_URL, requestToken.getToken());
}
} }
@@ -1,10 +1,10 @@
package org.scribe.builder.api; package org.scribe.builder.api;
import org.scribe.extensions.linkedin.*; import org.scribe.model.*;
import org.scribe.extractors.*;
public class LinkedInApi extends DefaultApi10a public class LinkedInApi extends DefaultApi10a
{ {
private static final String AUTHORIZE_URL = "https://api.linkedin.com/uas/oauth/authorize?oauth_token=%s";
@Override @Override
public String getAccessTokenEndpoint() public String getAccessTokenEndpoint()
@@ -19,9 +19,9 @@ public class LinkedInApi extends DefaultApi10a
} }
@Override @Override
public BaseStringExtractor getBaseStringExtractor() public String getAuthorizationUrl(Token requestToken)
{ {
return new LinkedInBaseStringExtractorImpl(); return String.format(AUTHORIZE_URL, requestToken.getToken());
} }
} }
@@ -1,16 +1,26 @@
package org.scribe.builder.api; package org.scribe.builder.api;
import org.scribe.model.Token;
public class TwitterApi extends DefaultApi10a public class TwitterApi extends DefaultApi10a
{ {
private static final String AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize?oauth_token=%s";
@Override @Override
public String getAccessTokenEndpoint() public String getAccessTokenEndpoint()
{ {
return "http://twitter.com/oauth/access_token"; return "http://api.twitter.com/oauth/access_token";
} }
@Override @Override
public String getRequestTokenEndpoint() public String getRequestTokenEndpoint()
{ {
return "http://twitter.com/oauth/request_token"; return "http://api.twitter.com/oauth/request_token";
}
@Override
public String getAuthorizationUrl(Token requestToken)
{
return String.format(AUTHORIZE_URL, requestToken.getToken());
} }
} }
@@ -1,7 +1,11 @@
package org.scribe.builder.api; package org.scribe.builder.api;
import org.scribe.model.Token;
public class VimeoApi extends DefaultApi10a public class VimeoApi extends DefaultApi10a
{ {
private static final String AUTHORIZATION_URL = "http://vimeo.com/oauth/authorize?oauth_token=%s";
@Override @Override
public String getAccessTokenEndpoint() public String getAccessTokenEndpoint()
{ {
@@ -13,4 +17,10 @@ public class VimeoApi extends DefaultApi10a
{ {
return "http://vimeo.com/oauth/request_token"; return "http://vimeo.com/oauth/request_token";
} }
@Override
public String getAuthorizationUrl(Token requestToken)
{
return String.format(AUTHORIZATION_URL, requestToken.getToken());
}
} }
@@ -1,7 +1,11 @@
package org.scribe.builder.api; package org.scribe.builder.api;
import org.scribe.model.Token;
public class YahooApi extends DefaultApi10a public class YahooApi extends DefaultApi10a
{ {
private static final String AUTHORIZE_URL = "https://api.login.yahoo.com/oauth/v2/request_auth?oauth_token=%s";
@Override @Override
public String getAccessTokenEndpoint() public String getAccessTokenEndpoint()
{ {
@@ -13,4 +17,10 @@ public class YahooApi extends DefaultApi10a
{ {
return "https://api.login.yahoo.com/oauth/v2/get_request_token"; return "https://api.login.yahoo.com/oauth/v2/get_request_token";
} }
@Override
public String getAuthorizationUrl(Token requestToken)
{
return String.format(AUTHORIZE_URL, requestToken.getToken());
}
} }
@@ -0,0 +1,26 @@
package org.scribe.builder.api;
import org.scribe.model.Token;
public class YammerApi extends DefaultApi10a
{
private static final String AUTHORIZATION_URL = "'https://www.yammer.com/oauth/authorize?oauth_token=%s'";
@Override
public String getRequestTokenEndpoint()
{
return "https://www.yammer.com/oauth/request_token";
}
@Override
public String getAccessTokenEndpoint()
{
return "https://www.yammer.com/oauth/access_token";
}
@Override
public String getAuthorizationUrl(Token requestToken)
{
return String.format(AUTHORIZATION_URL, requestToken.getToken());
}
}
@@ -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);
@@ -1,15 +0,0 @@
package org.scribe.extensions.linkedin;
import org.scribe.extractors.*;
import org.scribe.model.*;
public class LinkedInBaseStringExtractorImpl extends BaseStringExtractorImpl
{
@Override
public String extract(OAuthRequest request)
{
String baseString = super.extract(request);
return baseString.replace("%7E", "~");
}
}
@@ -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,14 +6,23 @@ 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, "Response body is incorrect. Can't extract a token from an empty string");
Matcher matcher = Pattern.compile(TOKEN_REGEX).matcher(response); Matcher matcher = Pattern.compile(TOKEN_REGEX).matcher(response);
if (matcher.matches()) if (matcher.matches())
@@ -23,7 +32,7 @@ public class TokenExtractorImpl implements RequestTokenExtractor, AccessTokenExt
return new Token(token, secret); return new Token(token, secret);
} else } else
{ {
throw new OAuthException("Could not find request token or secret in response: " + response, null); throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null);
} }
} }
} }
+17 -45
View File
@@ -1,14 +1,27 @@
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;
private String apiSecret; private String apiSecret;
private String callback; private String callback;
private String requestTokenEndpoint;
private String accessTokenEndpoint; public OAuthConfig(String key, String secret)
private Verb requestTokenVerb; {
private Verb accessTokenVerb; this(key,secret,null);
}
public OAuthConfig(String key, String secret, String callback)
{
this.apiKey = key;
this.apiSecret = secret;
this.callback = callback;
}
public String getApiKey() public String getApiKey()
{ {
@@ -39,45 +52,4 @@ public class OAuthConfig
{ {
this.callback = callback; this.callback = callback;
} }
public String getRequestTokenEndpoint()
{
return requestTokenEndpoint;
}
public void setRequestTokenEndpoint(String requestTokenEndpoint)
{
this.requestTokenEndpoint = requestTokenEndpoint;
}
public String getAccessTokenEndpoint()
{
return accessTokenEndpoint;
}
public void setAccessTokenEndpoint(String accessTokenEndpoint)
{
this.accessTokenEndpoint = accessTokenEndpoint;
}
public Verb getRequestTokenVerb()
{
return requestTokenVerb != null ? requestTokenVerb : Verb.GET;
}
public void setRequestTokenVerb(Verb requestTokenVerb)
{
this.requestTokenVerb = requestTokenVerb;
}
public Verb getAccessTokenVerb()
{
return accessTokenVerb != null ? accessTokenVerb : Verb.GET;
}
public void setAccessTokenVerb(Verb accessTokenVerb)
{
this.accessTokenVerb = accessTokenVerb;
}
} }
@@ -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,8 @@ 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";
//OAuth 2.0
public static final String ACCESS_TOKEN = "access_token";
} }
@@ -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,21 @@ 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));
} else
{ {
return key; return key;
}
else
{
throw new IllegalArgumentException(String.format("OAuth parameters must either be '%s' or start with '%s'", OAuthConstants.SCOPE, OAUTH_PREFIX));
} }
} }
/**
* 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;
+50 -20
View File
@@ -3,6 +3,7 @@ package org.scribe.model;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import org.scribe.exceptions.*; import org.scribe.exceptions.*;
import org.scribe.utils.*; import org.scribe.utils.*;
@@ -14,15 +15,14 @@ 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;
private Verb verb; private Verb verb;
private Map<String, String> querystringParams;
private Map<String, String> bodyParams; private Map<String, String> bodyParams;
private Map<String, String> headers; private Map<String, String> headers;
private String payload = null; private String payload = null;
private Integer timeout = null;
private HttpURLConnection connection; private HttpURLConnection connection;
/** /**
@@ -35,16 +35,9 @@ class Request
{ {
this.verb = verb; this.verb = verb;
this.url = url; this.url = url;
this.querystringParams = new HashMap<String, String>();
this.bodyParams = new HashMap<String, String>(); this.bodyParams = new HashMap<String, String>();
this.headers = new HashMap<String, String>(); this.headers = new HashMap<String, String>();
try
{
connection = (HttpURLConnection) new URL(url).openConnection();
if(timeout != null) connection.setConnectTimeout(timeout);
} catch (IOException ioe)
{
throw new OAuthException("Could not open connection to: " + url, ioe);
}
} }
/** /**
@@ -58,6 +51,7 @@ class Request
{ {
try try
{ {
createConnection();
return doSend(); return doSend();
} catch (IOException ioe) } catch (IOException ioe)
{ {
@@ -65,6 +59,15 @@ class Request
} }
} }
private void createConnection() throws IOException
{
String effectiveUrl = URLUtils.appendParametersToQueryString(url, querystringParams);
if (connection == null)
{
connection = (HttpURLConnection) new URL(effectiveUrl).openConnection();
}
}
Response doSend() throws IOException Response doSend() throws IOException
{ {
connection.setRequestMethod(this.verb.name()); connection.setRequestMethod(this.verb.name());
@@ -92,8 +95,8 @@ class Request
/** /**
* Add an HTTP Header to the Request * Add an HTTP Header to the Request
* *
* @param name * @param key the header name
* @param value * @param value the header value
*/ */
public void addHeader(String key, String value) public void addHeader(String key, String value)
{ {
@@ -103,14 +106,25 @@ class Request
/** /**
* Add a body Parameter (for POST/ PUT Requests) * Add a body Parameter (for POST/ PUT Requests)
* *
* @param name * @param key the parameter name
* @param value * @param value the parameter value
*/ */
public void addBodyParameter(String key, String value) public void addBodyParameter(String key, String value)
{ {
this.bodyParams.put(key, value); this.bodyParams.put(key, value);
} }
/**
* Add a QueryString parameter
*
* @param key the parameter name
* @param value the parameter value
*/
public void addQuerystringParameter(String key, String value)
{
this.querystringParams.put(key, value);
}
/** /**
* Add body payload. * Add body payload.
* *
@@ -119,7 +133,7 @@ class Request
* *
* Note: The contents are not part of the OAuth signature * Note: The contents are not part of the OAuth signature
* *
* @param payload * @param payload the body of the request
*/ */
public void addPayload(String payload) public void addPayload(String payload)
{ {
@@ -145,8 +159,10 @@ class Request
params.put(pair[0], pair[1]); params.put(pair[0], pair[1]);
} }
} }
params.putAll(querystringParams);
return params; return params;
} catch (MalformedURLException mue) }
catch (MalformedURLException mue)
{ {
throw new OAuthException("Malformed URL", mue); throw new OAuthException("Malformed URL", mue);
} }
@@ -213,13 +229,27 @@ class Request
} }
/** /**
* Sets the connection timeout in milliseconds for the underlying {@link HttpURLConnection} * Sets the connect timeout for the underlying {@link HttpURLConnection}
* *
* @param timeout in milliseconds * @param duration duration of the timeout
*
* @param unit unit of time (milliseconds, seconds, etc)
*/ */
public void setTimeout(int timeout) public void setConnectTimeout(int duration, TimeUnit unit)
{ {
this.timeout = timeout; this.connection.setConnectTimeout((int) unit.toMillis(duration));
}
/**
* Sets the read timeout for the underlying {@link HttpURLConnection}
*
* @param duration duration of the timeout
*
* @param unit unit of time (milliseconds, seconds, etc)
*/
public void setReadTimeout(int duration, TimeUnit unit)
{
this.connection.setReadTimeout((int) unit.toMillis(duration));
} }
/* /*
@@ -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;
+16 -2
View File
@@ -1,11 +1,25 @@
package org.scribe.model; package org.scribe.model;
public class Token import java.io.Serializable;
{
/**
* Represents an OAuth token (either request or access token) and its secret
*
* @author Pablo Fernandez
*/
public class Token implements Serializable
{
private static final long serialVersionUID = 715000866082812683L;
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;
+13 -1
View File
@@ -1,13 +1,25 @@
package org.scribe.model; package org.scribe.model;
import org.scribe.utils.*;
/**
* 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 = URLUtils.percentDecode(value);
} }
public String getValue() public String getValue()
@@ -1,65 +1,76 @@
package org.scribe.oauth; package org.scribe.oauth;
import org.scribe.extractors.*; import org.scribe.builder.api.*;
import org.scribe.model.*; import org.scribe.model.*;
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;
private SignatureService signatureService; private DefaultApi10a api;
private TimestampService timestampService; private String scope;
private BaseStringExtractor baseStringExtractor;
private HeaderExtractor headerExtractor;
private RequestTokenExtractor rtExtractor;
private AccessTokenExtractor atExtractor;
public OAuth10aServiceImpl(SignatureService signatureService, TimestampService timestampService, BaseStringExtractor baseStringExtractor, /**
HeaderExtractor headerExtractor, RequestTokenExtractor rtExtractor, AccessTokenExtractor atExtractor, OAuthConfig config) * Default constructor
*
* @param api OAuth1.0a api information
* @param config OAuth 1.0a configuration param object
*/
public OAuth10aServiceImpl(DefaultApi10a api, OAuthConfig config)
{ {
this.signatureService = signatureService; this.api = api;
this.timestampService = timestampService;
this.baseStringExtractor = baseStringExtractor;
this.headerExtractor = headerExtractor;
this.rtExtractor = rtExtractor;
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(api.getRequestTokenVerb(), api.getRequestTokenEndpoint());
addOAuthParams(request, OAuthConstants.EMPTY_TOKEN); addOAuthParams(request, OAuthConstants.EMPTY_TOKEN);
addOAuthHeader(request); addOAuthHeader(request);
Response response = request.send(); Response response = request.send();
return rtExtractor.extract(response.getBody()); return api.getRequestTokenExtractor().extract(response.getBody());
} }
private void addOAuthParams(OAuthRequest request, Token token) private void addOAuthParams(OAuthRequest request, Token token)
{ {
request.addOAuthParameter(OAuthConstants.TIMESTAMP, timestampService.getTimestampInSeconds()); request.addOAuthParameter(OAuthConstants.TIMESTAMP, api.getTimestampService().getTimestampInSeconds());
request.addOAuthParameter(OAuthConstants.NONCE, timestampService.getNonce()); request.addOAuthParameter(OAuthConstants.NONCE, api.getTimestampService().getNonce());
request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey()); request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey());
request.addOAuthParameter(OAuthConstants.SIGN_METHOD, signatureService.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()); 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(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
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); addOAuthHeader(request);
Response response = request.send(); Response response = request.send();
return atExtractor.extract(response.getBody()); return api.getAccessTokenExtractor().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,20 +78,39 @@ 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;
}
/**
* {@inheritDoc}
*/
public String getAuthorizationUrl(Token requestToken)
{
return api.getAuthorizationUrl(requestToken);
}
private String getSignature(OAuthRequest request, Token token) private String getSignature(OAuthRequest request, Token token)
{ {
String baseString = baseStringExtractor.extract(request); String baseString = api.getBaseStringExtractor().extract(request);
return signatureService.getSignature(baseString, config.getApiSecret(), token.getSecret()); return api.getSignatureService().getSignature(baseString, config.getApiSecret(), token.getSecret());
} }
private void addOAuthHeader(OAuthRequest request) private void addOAuthHeader(OAuthRequest request)
{ {
String oauthHeader = headerExtractor.extract(request); String oauthHeader = api.getHeaderExtractor().extract(request);
request.addHeader(OAuthConstants.HEADER, oauthHeader); request.addHeader(OAuthConstants.HEADER, oauthHeader);
} }
} }
@@ -0,0 +1,73 @@
package org.scribe.oauth;
import org.scribe.builder.api.*;
import org.scribe.model.*;
public class OAuth20ServiceImpl implements OAuthService
{
private static final String VERSION = "2.0";
private final DefaultApi20 api;
private final OAuthConfig config;
/**
* Default constructor
*
* @param api OAuth2.0 api information
* @param config OAuth 2.0 configuration param object
*/
public OAuth20ServiceImpl(DefaultApi20 api, OAuthConfig config)
{
this.api = api;
this.config = config;
}
/**
* {@inheritDoc}
*/
public void addScope(String scope)
{
throw new UnsupportedOperationException("OAuth 2 does not use scopes");
}
/**
* {@inheritDoc}
*/
public Token getAccessToken(Token requestToken, Verifier verifier)
{
throw new UnsupportedOperationException("Unsupported operation, please use 'getAuthorizationUrl' and redirect your users there");
}
/**
* {@inheritDoc}
*/
public Token getRequestToken()
{
throw new UnsupportedOperationException("Unsupported operation, please use 'getAuthorizationUrl' and redirect your users there");
}
/**
* {@inheritDoc}
*/
public String getVersion()
{
return VERSION;
}
/**
* {@inheritDoc}
*/
public void signRequest(Token accessToken, OAuthRequest request)
{
request.addQuerystringParameter(OAuthConstants.ACCESS_TOKEN, accessToken.getToken());
}
/**
* {@inheritDoc}
*/
public String getAuthorizationUrl(Token requestToken)
{
return api.getAuthorizationUrl(config);
}
}
@@ -2,13 +2,59 @@ 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);
/**
* Returns the URL where you should redirect your users to authenticate
* your application.
*
* @param requestToken the request token you need to authorize
* @return the URL where you should redirect your users
*/
public String getAuthorizationUrl(Token requestToken);
} }
@@ -7,22 +7,30 @@ 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
{ {
Preconditions.checkEmptyString(baseString, "Base string cant be null or empty string"); Preconditions.checkEmptyString(baseString, "Base string cant be null or empty string");
Preconditions.checkEmptyString(apiSecret, "Api secret cant be null or empty string"); Preconditions.checkEmptyString(apiSecret, "Api secret cant be null or empty string");
return doSign(baseString, apiSecret + '&' + tokenSecret); return doSign(baseString, URLUtils.percentEncode(apiSecret) + '&' + URLUtils.percentEncode(tokenSecret));
} }
catch (Exception e) catch (Exception e)
{ {
@@ -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");
@@ -26,7 +36,7 @@ public class StreamUtils
return out.toString(); return out.toString();
} catch (IOException ioe) } catch (IOException ioe)
{ {
throw new RuntimeException("Error while reading response body", ioe); throw new IllegalStateException("Error while reading response body", ioe);
} }
} }
} }
+80 -7
View File
@@ -4,18 +4,38 @@ import java.io.*;
import java.net.*; import java.net.*;
import java.util.*; import java.util.*;
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 = '=';
private static final char PARAM_SEPARATOR = '&'; private static final char PARAM_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);
private static final Set<EncodingRule> ENCODING_RULES;
static
{
Set<EncodingRule> rules = new HashSet<EncodingRule>();
rules.add(new EncodingRule("*","%2A"));
rules.add(new EncodingRule("+","%20"));
rules.add(new EncodingRule("%7E", "~"));
ENCODING_RULES = Collections.unmodifiableSet(rules);
}
/**
* 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,28 +56,81 @@ 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");
try try
{ {
return URLEncoder.encode(string, UTF_8).replaceAll("\\+", "%20"); String encoded = URLEncoder.encode(string, UTF_8);
for(EncodingRule rule : ENCODING_RULES)
{
encoded = rule.apply(encoded);
}
return encoded;
} }
catch (UnsupportedEncodingException uee) catch (UnsupportedEncodingException uee)
{ {
throw new OAuthException(ERROR_MSG, uee); throw new IllegalStateException(ERROR_MSG, uee);
} }
} }
/**
* 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");
try try
{ {
return URLDecoder.decode(string, UTF_8); return URLDecoder.decode(string, UTF_8);
} catch (UnsupportedEncodingException uee) }
catch (UnsupportedEncodingException uee)
{ {
throw new OAuthException(ERROR_MSG, uee); throw new IllegalStateException(ERROR_MSG, uee);
}
}
/**
* Append given parameters to the query string of the url
*
* @param url the url to append parameters to
* @param params any map
* @return new url with parameters on query string
*/
public static String appendParametersToQueryString(String url, Map<String, String> params)
{
Preconditions.checkNotNull(url, "Cannot append to null URL");
String queryString = URLUtils.formURLEncodeMap(params);
if (queryString.isEmpty()) 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;
url += queryString;
return url;
}
private static final class EncodingRule
{
private final String ch;
private final String toCh;
EncodingRule(String ch, String toCh)
{
this.ch = ch;
this.toCh = toCh;
}
String apply(String string) {
return string.replace(ch, toCh);
} }
} }
} }
@@ -0,0 +1,66 @@
package org.scribe.builder;
import static org.junit.Assert.*;
import org.junit.*;
import org.scribe.builder.api.*;
import org.scribe.model.*;
import org.scribe.oauth.*;
public class ServiceBuilderTest
{
private ServiceBuilder builder;
@Before
public void setup()
{
builder = new ServiceBuilder();
}
@Test
public void shouldReturnOOBasDefaultCallback()
{
builder.provider(ApiMock.class).apiKey("key").apiSecret("secret").build();
assertEquals(ApiMock.config.getApiKey(), "key");
assertEquals(ApiMock.config.getApiSecret(), "secret");
assertEquals(ApiMock.config.getCallback(), OAuthConstants.OUT_OF_BAND);
}
@Test
public void shouldAcceptValidCallbackUrl()
{
builder.provider(ApiMock.class).apiKey("key").apiSecret("secret").callback("http://example.com").build();
assertEquals(ApiMock.config.getApiKey(), "key");
assertEquals(ApiMock.config.getApiSecret(), "secret");
assertEquals(ApiMock.config.getCallback(), "http://example.com");
}
@Test(expected=IllegalArgumentException.class)
public void shouldNotAcceptAnInvalidUrlAsCallback()
{
builder.provider(ApiMock.class).apiKey("key").apiSecret("secret").callback("example.com").build();
}
@Test
public void shouldAcceptAnScope()
{
builder.provider(ApiMock.class).apiKey("key").apiSecret("secret").scope("rss-api").build();
assertEquals(ApiMock.config.getApiKey(), "key");
assertEquals(ApiMock.config.getApiSecret(), "secret");
assertEquals(ApiMock.scope, "rss-api");
}
public static class ApiMock implements Api
{
public static OAuthConfig config;
public static String scope;
@Override
public OAuthService createService(OAuthConfig config, String scope)
{
ApiMock.config = config;
ApiMock.scope = scope;
return null;
}
}
}
@@ -0,0 +1,58 @@
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 FacebookExample
{
private static final String NETWORK_NAME = "Facebook";
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;
public static void main(String[] args)
{
// Replace these with your own api key and secret
String apiKey = "your_api_key";
String apiSecret = "your_api_secret";
OAuthService service = new ServiceBuilder()
.provider(FacebookApi.class)
.apiKey(apiKey)
.apiSecret(apiSecret)
.callback("http://www.example.com/oauth_callback/")
.build();
Scanner in = new Scanner(System.in);
System.out.println("=== " + NETWORK_NAME + "'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 access token here");
System.out.print(">>");
Token accessToken = new Token(in.nextLine(), NO_SECRET_NEEDED);
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! :)");
}
}
@@ -9,7 +9,6 @@ import org.scribe.oauth.*;
public class FoursquareExample public class FoursquareExample
{ {
private static final String AUTHORIZE_URL = "http://foursquare.com/oauth/authorize?oauth_token=";
private static final String PROTECTED_RESOURCE_URL = "http://api.foursquare.com/v1/user"; private static final String PROTECTED_RESOURCE_URL = "http://api.foursquare.com/v1/user";
public static void main(String[] args) public static void main(String[] args)
@@ -31,7 +30,7 @@ public class FoursquareExample
System.out.println(); System.out.println();
System.out.println("Now go and authorize Scribe here:"); System.out.println("Now go and authorize Scribe here:");
System.out.println(AUTHORIZE_URL + requestToken.getToken()); System.out.println(service.getAuthorizationUrl(requestToken));
System.out.println("And paste the verifier here"); System.out.println("And paste the verifier here");
System.out.print(">>"); System.out.print(">>");
Verifier verifier = new Verifier(in.nextLine()); Verifier verifier = new Verifier(in.nextLine());
@@ -0,0 +1,66 @@
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 GoogleExample
{
private static final String NETWORK_NAME = "Google";
private static final String AUTHORIZE_URL = "https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=";
private static final String PROTECTED_RESOURCE_URL = "https://docs.google.com/feeds/default/private/full/";
private static final String SCOPE = "https://docs.google.com/feeds/";
public static void main(String[] args)
{
OAuthService service = new ServiceBuilder()
.provider(GoogleApi.class)
.apiKey("anonymous")
.apiSecret("anonymous")
.scope(SCOPE)
.build();
Scanner in = new Scanner(System.in);
System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ===");
System.out.println();
// Obtain the Request Token
System.out.println("Fetching the Request Token...");
Token requestToken = service.getRequestToken();
System.out.println("Got the Request Token!");
System.out.println("(if your curious it looks like this: " + requestToken + " )");
System.out.println();
System.out.println("Now go and authorize Scribe here:");
System.out.println(AUTHORIZE_URL + requestToken.getToken());
System.out.println("And paste the verifier 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);
request.addHeader("GData-Version", "3.0");
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! :)");
}
}
@@ -9,18 +9,17 @@ import org.scribe.oauth.*;
public class LinkedInExample public class LinkedInExample
{ {
private static final String AUTHORIZE_URL = "https://api.linkedin.com/uas/oauth/authorize?oauth_token=";
private static final String PROTECTED_RESOURCE_URL = "http://api.linkedin.com/v1/people/~/connections:(id,last-name)"; private static final String PROTECTED_RESOURCE_URL = "http://api.linkedin.com/v1/people/~/connections:(id,last-name)";
public static void main(String[] args) public static void main(String[] args)
{ {
OAuthService service = new ServiceBuilder() OAuthService service = new ServiceBuilder()
.provider(LinkedInApi.class) .provider(LinkedInApi.class)
.apiKey("CiEgwWDkA5BFpNrc0RfGyVuSlOh4tig5kOTZ9q97qcXNrFl7zqk-Ts7DqRGaKDCV") .apiKey("CiEgwWDkA5BFpNrc0RfGyVuSlOh4tig5kOTZ9q97qcXNrFl7zqk-Ts7DqRGaKDCV")
.apiSecret("dhho4dfoCmiQXrkw4yslork5XWLFnPSuMR-8gscPVjY4jqFFHPYWJKgpFl4uLTM6") .apiSecret("dhho4dfoCmiQXrkw4yslork5XWLFnPSuMR-8gscPVjY4jqFFHPYWJKgpFl4uLTM6")
.build(); .build();
Scanner in = new Scanner(System.in); Scanner in = new Scanner(System.in);
System.out.println("=== LinkedIn's OAuth Workflow ==="); System.out.println("=== LinkedIn's OAuth Workflow ===");
System.out.println(); System.out.println();
@@ -31,7 +30,7 @@ public class LinkedInExample
System.out.println(); System.out.println();
System.out.println("Now go and authorize Scribe here:"); System.out.println("Now go and authorize Scribe here:");
System.out.println(AUTHORIZE_URL + requestToken.getToken()); System.out.println(service.getAuthorizationUrl(requestToken));
System.out.println("And paste the verifier here"); System.out.println("And paste the verifier here");
System.out.print(">>"); System.out.print(">>");
Verifier verifier = new Verifier(in.nextLine()); Verifier verifier = new Verifier(in.nextLine());
@@ -55,8 +54,6 @@ public class LinkedInExample
System.out.println(); System.out.println();
System.out.println("Thats it man! Go and build something awesome with Scribe! :)"); System.out.println("Thats it man! Go and build something awesome with Scribe! :)");
System.out.println(service.getRequestToken());
} }
} }
@@ -9,7 +9,6 @@ import org.scribe.oauth.*;
public class TwitterExample public class TwitterExample
{ {
private static final String AUTHORIZE_URL = "https://twitter.com/oauth/authorize?oauth_token=";
private static final String PROTECTED_RESOURCE_URL = "http://api.twitter.com/1/account/verify_credentials.xml"; private static final String PROTECTED_RESOURCE_URL = "http://api.twitter.com/1/account/verify_credentials.xml";
public static void main(String[] args) public static void main(String[] args)
@@ -31,7 +30,7 @@ public class TwitterExample
System.out.println(); System.out.println();
System.out.println("Now go and authorize Scribe here:"); System.out.println("Now go and authorize Scribe here:");
System.out.println(AUTHORIZE_URL + requestToken.getToken()); System.out.println(service.getAuthorizationUrl(requestToken));
System.out.println("And paste the verifier here"); System.out.println("And paste the verifier here");
System.out.print(">>"); System.out.print(">>");
Verifier verifier = new Verifier(in.nextLine()); Verifier verifier = new Verifier(in.nextLine());
@@ -9,20 +9,18 @@ import org.scribe.oauth.*;
public class YahooExample public class YahooExample
{ {
private static final String NETWORK_NAME = "Yahoo";
private static final String AUTHORIZE_URL = "https://api.login.yahoo.com/oauth/v2/request_auth?oauth_token=";
private static final String PROTECTED_RESOURCE_URL = "http://social.yahooapis.com/v1/user/A6ROU63MXWDCW3Y5MGCYWVHDJI/profile/status?format=json"; private static final String PROTECTED_RESOURCE_URL = "http://social.yahooapis.com/v1/user/A6ROU63MXWDCW3Y5MGCYWVHDJI/profile/status?format=json";
public static void main(String[] args) public static void main(String[] args)
{ {
OAuthService service = new ServiceBuilder() OAuthService service = new ServiceBuilder()
.provider(YahooApi.class) .provider(YahooApi.class)
.apiKey("dj0yJmk9TXZDWVpNVVdGaVFmJmQ9WVdrOWMweHZXbkZLTkhVbWNHbzlNVEl5TWprd05qUTJNZy0tJnM9Y29uc3VtZXJzZWNyZXQmeD0wMw--") .apiKey("dj0yJmk9TXZDWVpNVVdGaVFmJmQ9WVdrOWMweHZXbkZLTkhVbWNHbzlNVEl5TWprd05qUTJNZy0tJnM9Y29uc3VtZXJzZWNyZXQmeD0wMw--")
.apiSecret("262be559f92a2be20c4c039419018f2b48cdfce9") .apiSecret("262be559f92a2be20c4c039419018f2b48cdfce9")
.build(); .build();
Scanner in = new Scanner(System.in); Scanner in = new Scanner(System.in);
System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ==="); System.out.println("=== Yahoo's OAuth Workflow ===");
System.out.println(); System.out.println();
// Obtain the Request Token // Obtain the Request Token
@@ -32,7 +30,7 @@ public class YahooExample
System.out.println(); System.out.println();
System.out.println("Now go and authorize Scribe here:"); System.out.println("Now go and authorize Scribe here:");
System.out.println(AUTHORIZE_URL + requestToken.getToken()); System.out.println(service.getAuthorizationUrl(requestToken));
System.out.println("And paste the verifier here"); System.out.println("And paste the verifier here");
System.out.print(">>"); System.out.print(">>");
Verifier verifier = new Verifier(in.nextLine()); Verifier verifier = new Verifier(in.nextLine());
@@ -23,7 +23,7 @@ public class BaseStringExtractorTest
@Test @Test
public void shouldExtractBaseStringFromOAuthRequest() public void shouldExtractBaseStringFromOAuthRequest()
{ {
String expected = "GET&http%3A%2F%2Fexample.com&oauth_callback%3Dhttp%253A%252F%252Fexample%252Fcallback%26oauth_consumer_key%3DAS%2523%2524%255E*%2540%2526%26oauth_signature%3DOAuth-Signature%26oauth_timestamp%3D123456"; String expected = "GET&http%3A%2F%2Fexample.com&oauth_callback%3Dhttp%253A%252F%252Fexample%252Fcallback%26oauth_consumer_key%3DAS%2523%2524%255E%252A%2540%2526%26oauth_signature%3DOAuth-Signature%26oauth_timestamp%3D123456";
String baseString = extractor.extract(request); String baseString = extractor.extract(request);
assertEquals(expected, baseString); assertEquals(expected, baseString);
} }
@@ -45,7 +45,7 @@ public class BaseStringExtractorTest
@Test @Test
public void shouldProperlyEncodeSpaces() public void shouldProperlyEncodeSpaces()
{ {
String expected = "GET&http%3A%2F%2Fexample.com&body%3Dthis%2520param%2520has%2520whitespace%26oauth_callback%3Dhttp%253A%252F%252Fexample%252Fcallback%26oauth_consumer_key%3DAS%2523%2524%255E*%2540%2526%26oauth_signature%3DOAuth-Signature%26oauth_timestamp%3D123456"; String expected = "GET&http%3A%2F%2Fexample.com&body%3Dthis%2520param%2520has%2520whitespace%26oauth_callback%3Dhttp%253A%252F%252Fexample%252Fcallback%26oauth_consumer_key%3DAS%2523%2524%255E%252A%2540%2526%26oauth_signature%3DOAuth-Signature%26oauth_timestamp%3D123456";
request.addBodyParameter("body", "this param has whitespace"); request.addBodyParameter("body", "this param has whitespace");
assertEquals(expected, extractor.extract(request)); assertEquals(expected, extractor.extract(request));
} }
@@ -24,7 +24,7 @@ public class HeaderExtractorTest
public void shouldExtractStandardHeader() public void shouldExtractStandardHeader()
{ {
String expected = "OAuth oauth_callback=\"http%3A%2F%2Fexample%2Fcallback\", " + "oauth_signature=\"OAuth-Signature\", " String expected = "OAuth oauth_callback=\"http%3A%2F%2Fexample%2Fcallback\", " + "oauth_signature=\"OAuth-Signature\", "
+ "oauth_consumer_key=\"AS%23%24%5E*%40%26\", " + "oauth_timestamp=\"123456\""; + "oauth_consumer_key=\"AS%23%24%5E%2A%40%26\", " + "oauth_timestamp=\"123456\"";
String header = extractor.extract(request); String header = extractor.extract(request);
assertEquals(expected, header); assertEquals(expected, header);
} }
@@ -10,22 +10,17 @@ public class OAuthConfigTest
@Test @Test
public void shouldReturnDefaultValuesIfNotSet() public void shouldReturnDefaultValuesIfNotSet()
{ {
OAuthConfig config = new OAuthConfig(); OAuthConfig config = new OAuthConfig("key", "secret");
assertEquals(Verb.GET, config.getAccessTokenVerb());
assertEquals(Verb.GET, config.getRequestTokenVerb());
assertEquals(OAuthConstants.OUT_OF_BAND, config.getCallback()); assertEquals(OAuthConstants.OUT_OF_BAND, config.getCallback());
} }
@Test @Test
public void shouldOverrideDefaultsIfSet() public void shouldOverrideDefaultsIfSet()
{ {
OAuthConfig config = new OAuthConfig(); OAuthConfig config = new OAuthConfig("key", "secret", "http://callback");
config.setCallback("http://callback");
config.setAccessTokenVerb(Verb.POST);
config.setRequestTokenVerb(Verb.POST);
assertEquals("http://callback", config.getCallback()); assertEquals("http://callback", config.getCallback());
assertEquals(Verb.POST, config.getAccessTokenVerb()); assertEquals("key", config.getApiKey());
assertEquals(Verb.POST, config.getRequestTokenVerb()); assertEquals("secret", config.getApiSecret());
} }
} }
@@ -21,8 +21,9 @@ public class OAuthRequestTest
request.addOAuthParameter(OAuthConstants.TOKEN, "token"); request.addOAuthParameter(OAuthConstants.TOKEN, "token");
request.addOAuthParameter(OAuthConstants.NONCE, "nonce"); request.addOAuthParameter(OAuthConstants.NONCE, "nonce");
request.addOAuthParameter(OAuthConstants.TIMESTAMP, "ts"); request.addOAuthParameter(OAuthConstants.TIMESTAMP, "ts");
request.addOAuthParameter(OAuthConstants.SCOPE, "feeds");
assertEquals(3, request.getOauthParameters().size()); assertEquals(4, request.getOauthParameters().size());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
@@ -65,4 +65,13 @@ public class RequestTest
assertTrue(connection.getHeaders().containsKey("Content-Length")); assertTrue(connection.getHeaders().containsKey("Content-Length"));
} }
@Test
public void shouldAllowAddingQuerystringParametersAfterCreation()
{
Request request = new Request(Verb.GET, "http://example.com?one=val");
request.addQuerystringParameter("two", "other val");
request.addQuerystringParameter("more", "params");
assertEquals(3, request.getQueryStringParams().size());
}
} }
@@ -0,0 +1,21 @@
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);
}
}
@@ -3,7 +3,7 @@ package org.scribe.services;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.junit.*; import org.junit.*;
import org.scribe.exceptions.OAuthException; import org.scribe.exceptions.*;
public class HMACSha1SignatureServiceTest public class HMACSha1SignatureServiceTest
{ {
@@ -29,7 +29,7 @@ public class HMACSha1SignatureServiceTest
String apiSecret = "api secret"; String apiSecret = "api secret";
String tokenSecret = "token secret"; String tokenSecret = "token secret";
String baseString = "base string"; String baseString = "base string";
String signature = "cyxvUDZY/5cQBcBiKulaNgnhN/w="; String signature = "uGymw2KHOTWI699YEaoi5xyLT50=";
assertEquals(signature, service.getSignature(baseString, apiSecret, tokenSecret)); assertEquals(signature, service.getSignature(baseString, apiSecret, tokenSecret));
} }
@@ -8,13 +8,6 @@ import org.junit.*;
public class URLUtilsTest public class URLUtilsTest
{ {
@Before
public void setup()
{
}
@Test @Test
public void shouldPercentEncodeMap() public void shouldPercentEncodeMap()
{ {
@@ -51,6 +44,23 @@ public class URLUtilsTest
assertEquals(expected, URLUtils.percentDecode(toDecode)); assertEquals(expected, URLUtils.percentDecode(toDecode));
} }
@Test
public void shouldEncodeAllSpecialCharacters()
{
String plain = "!*'();:@&=+$,/?#[]";
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(plain, URLUtils.percentDecode(encoded));
}
@Test
public void shouldNotEncodeReservedCharacters()
{
String plain = "abcde123456-._~";
String encoded = plain;
assertEquals(encoded, URLUtils.percentEncode(plain));
}
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionIfMapIsNull() public void shouldThrowExceptionIfMapIsNull()
{ {
@@ -72,4 +82,49 @@ public class URLUtilsTest
URLUtils.percentDecode(toDecode); URLUtils.percentDecode(toDecode);
} }
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenAppendingNullMapToQuerystring()
{
String url = "http://www.example.com";
Map<String, String> nullMap = null;
URLUtils.appendParametersToQueryString(url, nullMap);
}
@Test
public void shouldAppendNothingToQuerystringIfGivenEmptyMap()
{
String url = "http://www.example.com";
Map<String, String> emptyMap = new HashMap<String, String>();
String newUrl = URLUtils.appendParametersToQueryString(url, emptyMap);
Assert.assertEquals(url, newUrl);
}
@Test
public void shouldAppendParametersToSimpleUrl()
{
String url = "http://www.example.com";
String expectedUrl = "http://www.example.com?param1=value1&param2=value%20with%20spaces";
Map<String, String> params = new HashMap<String, String>();
params.put("param1", "value1");
params.put("param2", "value with spaces");
url = URLUtils.appendParametersToQueryString(url, params);
Assert.assertEquals(url, expectedUrl);
}
@Test
public void shouldAppendParametersToUrlWithQuerystring()
{
String url = "http://www.example.com?already=present";
String expectedUrl = "http://www.example.com?already=present&param1=value1&param2=value%20with%20spaces";
Map<String, String> params = new HashMap<String, String>();
params.put("param1", "value1");
params.put("param2", "value with spaces");
url = URLUtils.appendParametersToQueryString(url, params);
Assert.assertEquals(url, expectedUrl);
}
} }