diff --git a/src/main/java/org/scribe/builder/api/BiblioetecaApi.java b/src/main/java/org/scribe/builder/api/BiblioetecaApi.java new file mode 100644 index 000000000..fbbe605f5 --- /dev/null +++ b/src/main/java/org/scribe/builder/api/BiblioetecaApi.java @@ -0,0 +1,34 @@ +package org.scribe.builder.api; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import org.scribe.model.Token; + +public class BiblioetecaApi extends DefaultApi10 +{ + private static final String AUTHORIZE_URL = "http://www.biblioeteca.com/biblioeteca.web/authorize?oauth_token=%s"; + + @Override + public String getAccessTokenEndpoint() + { + return "http://api.biblioeteca.com/biblioeteca.web/access_token"; + } + + @Override + public String getRequestTokenEndpoint() + { + return "http://api.biblioeteca.com/biblioeteca.web/request_token"; + } + + @Override + public String getAuthorizationUrl(Token requestToken, String callback) + { + String url = String.format(AUTHORIZE_URL, requestToken.getToken()); + try { + return url+"&oauth_callback="+URLEncoder.encode(callback, "UTF-8"); + } catch (UnsupportedEncodingException e) { + return url; + } + } +} diff --git a/src/main/java/org/scribe/builder/api/BiblioetecaApi20.java b/src/main/java/org/scribe/builder/api/BiblioetecaApi20.java new file mode 100644 index 000000000..d40357cca --- /dev/null +++ b/src/main/java/org/scribe/builder/api/BiblioetecaApi20.java @@ -0,0 +1,67 @@ +package org.scribe.builder.api; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.scribe.exceptions.OAuthException; +import org.scribe.extractors.AccessTokenExtractor; +import org.scribe.model.OAuthConfig; +import org.scribe.model.Token; +import org.scribe.model.Verb; +import org.scribe.oauth.OAuth20HeaderServiceImpl; +import org.scribe.oauth.OAuthService; +import org.scribe.utils.OAuthEncoder; +import org.scribe.utils.Preconditions; + +public class BiblioetecaApi20 extends DefaultApi20 +{ + private static final String AUTHORIZE_URL = "http://api.biblioeteca.com/biblioeteca.web/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s"; + private static final String ACCESS_TOKEN_URL="http://api.biblioeteca.com/biblioeteca.web/oauth2/token?grant_type=authorization_code"; + + @Override + public String getAccessTokenEndpoint() + { + return ACCESS_TOKEN_URL; + } + +@Override +public String getAuthorizationUrl(OAuthConfig config) { + return String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback())); +} + +@Override +public Verb getAccessTokenVerb() { + return Verb.POST; +} + +// {"expires_in":"3600","refresh_token":"301f4c7f388f25a5a98d0cfb5c910","access_token":"db3a5a071f5563f75ca3b7f6573b352"} + +@Override +public AccessTokenExtractor getAccessTokenExtractor() { + return new AccessTokenExtractor() { + + @Override + public Token extract(String response) { + Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string"); + + Matcher matcher = Pattern.compile("\"access_token\":\"([^&\"]+)\"").matcher(response); + if (matcher.find()) + { + String token = OAuthEncoder.decode(matcher.group(1)); + return new Token(token, "", response); + } + else + { + throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null); + } + } + }; +} + +@Override +public OAuthService createService(OAuthConfig config) +{ + return new OAuth20HeaderServiceImpl(this, config); +} + +} \ No newline at end of file diff --git a/src/main/java/org/scribe/builder/api/DefaultApi10.java b/src/main/java/org/scribe/builder/api/DefaultApi10.java new file mode 100644 index 000000000..4ee3db551 --- /dev/null +++ b/src/main/java/org/scribe/builder/api/DefaultApi10.java @@ -0,0 +1,153 @@ +package org.scribe.builder.api; + +import org.scribe.extractors.AccessTokenExtractor; +import org.scribe.extractors.BaseStringExtractor; +import org.scribe.extractors.BaseStringExtractorImpl; +import org.scribe.extractors.HeaderExtractor; +import org.scribe.extractors.HeaderExtractorImpl; +import org.scribe.extractors.RequestTokenExtractor; +import org.scribe.extractors.TokenExtractorImpl; +import org.scribe.model.OAuthConfig; +import org.scribe.model.Token; +import org.scribe.model.Verb; +import org.scribe.oauth.OAuth10ServiceImpl; +import org.scribe.oauth.OAuthService; +import org.scribe.services.HMACSha1SignatureService; +import org.scribe.services.SignatureService; +import org.scribe.services.TimestampService; +import org.scribe.services.TimestampServiceImpl; + +/** +* Default implementation of the OAuth protocol, version 1.0 +* +* 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.0 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 (copy of OAuth 1.0a), patched by Martin Vlcek (OAuth 1.0) +* +*/ +public abstract class DefaultApi10 implements Api +{ + /** +* Returns the access token extractor. +* +* @return access token extractor +*/ + public AccessTokenExtractor getAccessTokenExtractor() + { + return new TokenExtractorImpl(); + } + + /** +* Returns the base string extractor. +* +* @return base string extractor +*/ + public BaseStringExtractor getBaseStringExtractor() + { + return new BaseStringExtractorImpl(); + } + + /** +* Returns the header extractor. +* +* @return header extractor +*/ + public HeaderExtractor getHeaderExtractor() + { + return new HeaderExtractorImpl(); + } + + /** +* Returns the request token extractor. +* +* @return request token extractor +*/ + public RequestTokenExtractor getRequestTokenExtractor() + { + return new TokenExtractorImpl(); + } + + /** +* Returns the signature service. +* +* @return signature service +*/ + public SignatureService getSignatureService() + { + return new HMACSha1SignatureService(); + } + + /** +* Returns the timestamp service. +* +* @return timestamp service +*/ + public TimestampService getTimestampService() + { + return new TimestampServiceImpl(); + } + + /** +* Returns the verb for the access token endpoint (defaults to POST) +* +* @return access token endpoint verb +*/ + public Verb getAccessTokenVerb() + { + return Verb.POST; + } + + /** +* Returns the verb for the request token endpoint (defaults to POST) +* +* @return request token endpoint verb +*/ + public Verb getRequestTokenVerb() + { + return Verb.POST; + } + + /** +* Returns the URL that receives the request token requests. +* +* @return request token URL +*/ + public abstract String getRequestTokenEndpoint(); + + /** +* Returns the URL that receives the access token requests. +* +* @return access token URL +*/ + public abstract String getAccessTokenEndpoint(); + + /** +* Returns the URL where you should redirect your users to authenticate +* 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, String callback); + + /** +* 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) + { + return new OAuth10ServiceImpl(this, config); + } +} diff --git a/src/main/java/org/scribe/oauth/OAuth10ServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth10ServiceImpl.java new file mode 100644 index 000000000..fbec7b93d --- /dev/null +++ b/src/main/java/org/scribe/oauth/OAuth10ServiceImpl.java @@ -0,0 +1,147 @@ +package org.scribe.oauth; + +import java.util.*; + +import org.scribe.builder.api.*; +import org.scribe.model.*; +import org.scribe.utils.*; + +/** +* OAuth 1.0 implementation of {@link OAuthService} +* +* @author Pablo Fernandez (copy of OAuth 1.0a), patched by Martin Vlcek (API 1.0) +*/ +public class OAuth10ServiceImpl implements OAuthService +{ + private static final String VERSION = "1.0"; + + private OAuthConfig config; + private DefaultApi10 api; + + /** +* Default constructor +* +* @param api OAuth1.0 api information +* @param config OAuth 1.0 configuration param object +*/ + public OAuth10ServiceImpl(DefaultApi10 api, OAuthConfig config) + { + this.api = api; + this.config = config; + } + + /** +* {@inheritDoc} +*/ + public Token getRequestToken() + { + config.log("obtaining request token from " + api.getRequestTokenEndpoint()); + OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint()); + + addOAuthParams(request, OAuthConstants.EMPTY_TOKEN); + appendSignature(request); + + config.log("sending request..."); + Response response = request.send(); + String body = response.getBody(); + + config.log("response status code: " + response.getCode()); + config.log("response body: " + body); + return api.getRequestTokenExtractor().extract(body); + } + + private void addOAuthParams(OAuthRequest request, Token token) + { + request.addOAuthParameter(OAuthConstants.TIMESTAMP, api.getTimestampService().getTimestampInSeconds()); + request.addOAuthParameter(OAuthConstants.NONCE, api.getTimestampService().getNonce()); + request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey()); + request.addOAuthParameter(OAuthConstants.SIGN_METHOD, api.getSignatureService().getSignatureMethod()); + request.addOAuthParameter(OAuthConstants.VERSION, getVersion()); + if(config.hasScope()) request.addOAuthParameter(OAuthConstants.SCOPE, config.getScope()); + request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, token)); + + config.log("appended additional OAuth parameters: " + MapUtils.toString(request.getOauthParameters())); + } + + /** +* {@inheritDoc} +*/ + public Token getAccessToken(Token requestToken, Verifier verifier) + { + config.log("obtaining access token from " + api.getAccessTokenEndpoint()); + OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); + request.addOAuthParameter(OAuthConstants.TOKEN, requestToken.getToken()); + //request.addOAuthParameter(OAuthConstants.VERIFIER, verifier.getValue()); + + config.log("setting token to: " + requestToken); + addOAuthParams(request, requestToken); + appendSignature(request); + Response response = request.send(); + return api.getAccessTokenExtractor().extract(response.getBody()); + } + + /** +* {@inheritDoc} +*/ + public void signRequest(Token token, OAuthRequest request) + { + config.log("signing request: " + request.getCompleteUrl()); + + // Do not append the token if empty. This is for two legged OAuth calls. + if (!token.isEmpty()) + { + request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken()); + } + config.log("setting token to: " + token); + addOAuthParams(request, token); + appendSignature(request); + } + + /** +* {@inheritDoc} +*/ + public String getVersion() + { + return VERSION; + } + + /** +* {@inheritDoc} +*/ + public String getAuthorizationUrl(Token requestToken) + { + return api.getAuthorizationUrl(requestToken, config.getCallback()); + } + + private String getSignature(OAuthRequest request, Token token) + { + config.log("generating signature..."); + String baseString = api.getBaseStringExtractor().extract(request); + String signature = api.getSignatureService().getSignature(baseString, config.getApiSecret(), token.getSecret()); + + config.log("base string is: " + baseString); + config.log("signature is: " + signature); + return signature; + } + + private void appendSignature(OAuthRequest request) + { + switch (config.getSignatureType()) + { + case Header: + config.log("using Http Header signature"); + + String oauthHeader = api.getHeaderExtractor().extract(request); + request.addHeader(OAuthConstants.HEADER, oauthHeader); + break; + case QueryString: + config.log("using Querystring signature"); + + for (Map.Entry entry : request.getOauthParameters().entrySet()) + { + request.addQuerystringParameter(entry.getKey(), entry.getValue()); + } + break; + } + } +} diff --git a/src/main/java/org/scribe/oauth/OAuth20HeaderServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth20HeaderServiceImpl.java new file mode 100644 index 000000000..207d1e775 --- /dev/null +++ b/src/main/java/org/scribe/oauth/OAuth20HeaderServiceImpl.java @@ -0,0 +1,73 @@ +package org.scribe.oauth; + +import org.scribe.builder.api.*; +import org.scribe.model.*; + +public class OAuth20HeaderServiceImpl 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 OAuth20HeaderServiceImpl(DefaultApi20 api, OAuthConfig config) + { + this.api = api; + this.config = config; + } + + /** + * {@inheritDoc} + */ + public Token getAccessToken(Token requestToken, Verifier verifier) + { + OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); + request.addQuerystringParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); + request.addQuerystringParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); + request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue()); + request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); + if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope()); + Response response = request.send(); + return api.getAccessTokenExtractor().extract(response.getBody()); + } + + /** + * {@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) + { + // Adds the header auth parameter + request.addHeader("Authorization", "Bearer "+accessToken.getToken()); + } + + /** + * {@inheritDoc} + */ + public String getAuthorizationUrl(Token requestToken) + { + return api.getAuthorizationUrl(config); + } + +} diff --git a/src/test/java/org/scribe/examples/BiblioetecaExample.java b/src/test/java/org/scribe/examples/BiblioetecaExample.java new file mode 100644 index 000000000..0723fb5fe --- /dev/null +++ b/src/test/java/org/scribe/examples/BiblioetecaExample.java @@ -0,0 +1,60 @@ +package org.scribe.examples; + +import java.util.Scanner; + +import org.scribe.builder.*; +import org.scribe.builder.api.*; +import org.scribe.model.*; +import org.scribe.oauth.*; + +public class BiblioetecaExample +{ + private static final String PROTECTED_RESOURCE_URL = "http://www.biblioeteca.com/biblioeteca.web/api/signin"; + + public static void main(String[] args) + { + OAuthService service = new ServiceBuilder() + .provider(BiblioetecaApi.class) + .apiKey("dj0yJmk9TXZDWVpNVVdGaVFmJmQ9WVdrOW") + .apiSecret("262be559f92a2be20c4c039419018f2b48cdfce9") + .build(); + Scanner in = new Scanner(System.in); + + System.out.println("=== Biblioeteca 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(); + + System.out.println("Now go and authorize Scribe here:"); + System.out.println(service.getAuthorizationUrl(requestToken)); + System.out.println("Simply authorize and press enter..."); + System.out.print(">>"); + Verifier verifier = new Verifier(in.nextLine()); + System.out.println(); + + // Trade the Request Token and Verfier for the Access Token + System.out.println("Trading the Request Token for an Access Token..."); + Token accessToken = service.getAccessToken(requestToken, verifier); + System.out.println("Got the Access Token!"); + System.out.println("(if your curious it looks like this: " + accessToken + " )"); + System.out.println(); + + // Now let's go and ask for a protected resource! + System.out.println("Now we're going to access a protected resource..."); + OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + service.signRequest(accessToken, request); + Response response = request.send(); + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + System.out.println(response.getCode()); + System.out.println(response.getBody()); + + System.out.println(); + System.out.println("Thats it man! Go and build something awesome with Scribe! :)"); + + } +}